From 0cf8c3e211e47f401f208d0614a8ba2ba29b7937 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 16 Mar 2021 13:05:48 +0100 Subject: [PATCH 01/44] use vue-client with nginx, so no CORS problems shoud be occure --- .gitignore | 3 ++- docker-compose.override.yml | 6 +++++- docker-compose.yml | 4 +--- frontend/vue.config.js | 4 +++- nginx/nginx.conf | 35 +++++++++++++++++++++++++---------- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index b12f322a2..5c23ad8bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.log -/node_modules/* \ No newline at end of file +/node_modules/* +.vscode diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 97cdb3155..e5d5d25c4 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -38,6 +38,10 @@ services: ## NGINX ################################################ ######################################################### nginx: + depends_on: + - frontend + - community-server + - login-server volumes: - ./logs:/var/log/nginx @@ -60,4 +64,4 @@ services: volumes: frontend_node_modules: - conan: \ No newline at end of file + conan: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b0c30ca66..567018dd1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,8 +16,6 @@ services: target: production networks: - external-net - depends_on: - - nginx ports: - 8080:8080 environment: @@ -135,4 +133,4 @@ networks: internal: true volumes: - db_vol: + db_vol: \ No newline at end of file diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 537477c6d..babcc63d7 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -15,6 +15,7 @@ module.exports = { } }, lintOnSave: true, + publicPath: '/vue/', configureWebpack: { // Set up all the aliases we use in our app. resolve: { @@ -30,5 +31,6 @@ module.exports = { css: { // Enable CSS source maps. sourceMap: process.env.NODE_ENV !== 'production' - } + }, + outputDir: path.resolve(__dirname, "/vue" ), }; diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 064395336..cdc418d05 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -12,15 +12,6 @@ server { root /var/www/cakephp/webroot; index index.php; - - location ~* \.(png|jpg|ico|webp)$ { - expires 30d; - } - - location ~* \.(js|css) { - # expires 1d; - expires 1d; - } location ~ \.php$ { fastcgi_pass community-server:9000; @@ -67,9 +58,33 @@ server { proxy_pass http://login-server:1201; proxy_redirect off; } + + location /vue { + + + location /vue/sockjs-node { + rewrite /vue/(.*) /$1; + } + location ~* \.(png) { + expires 1d; + rewrite /vue/(.*) /$1; + } + + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + #rewrite /vue/(.*) /$1 break; + + proxy_pass http://frontend:8080; + proxy_redirect off; + } location / { - try_files $uri $uri/ /index.php?$args; + try_files $uri $uri/ /index.php?$args; } # access_log /var/log/nginx/access.log main; From eabf0621fc85157d81a5459226c507bf678f3a2d Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 16 Mar 2021 13:05:48 +0100 Subject: [PATCH 02/44] use vue-client with nginx, so no CORS problems shoud be occure --- .gitignore | 3 ++- docker-compose.override.yml | 6 +++++- docker-compose.yml | 4 +--- frontend/vue.config.js | 4 +++- nginx/nginx.conf | 35 +++++++++++++++++++++++++---------- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index b12f322a2..5c23ad8bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.log -/node_modules/* \ No newline at end of file +/node_modules/* +.vscode diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 97cdb3155..e5d5d25c4 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -38,6 +38,10 @@ services: ## NGINX ################################################ ######################################################### nginx: + depends_on: + - frontend + - community-server + - login-server volumes: - ./logs:/var/log/nginx @@ -60,4 +64,4 @@ services: volumes: frontend_node_modules: - conan: \ No newline at end of file + conan: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b0c30ca66..567018dd1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,8 +16,6 @@ services: target: production networks: - external-net - depends_on: - - nginx ports: - 8080:8080 environment: @@ -135,4 +133,4 @@ networks: internal: true volumes: - db_vol: + db_vol: \ No newline at end of file diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 537477c6d..babcc63d7 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -15,6 +15,7 @@ module.exports = { } }, lintOnSave: true, + publicPath: '/vue/', configureWebpack: { // Set up all the aliases we use in our app. resolve: { @@ -30,5 +31,6 @@ module.exports = { css: { // Enable CSS source maps. sourceMap: process.env.NODE_ENV !== 'production' - } + }, + outputDir: path.resolve(__dirname, "/vue" ), }; diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 064395336..cdc418d05 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -12,15 +12,6 @@ server { root /var/www/cakephp/webroot; index index.php; - - location ~* \.(png|jpg|ico|webp)$ { - expires 30d; - } - - location ~* \.(js|css) { - # expires 1d; - expires 1d; - } location ~ \.php$ { fastcgi_pass community-server:9000; @@ -67,9 +58,33 @@ server { proxy_pass http://login-server:1201; proxy_redirect off; } + + location /vue { + + + location /vue/sockjs-node { + rewrite /vue/(.*) /$1; + } + location ~* \.(png) { + expires 1d; + rewrite /vue/(.*) /$1; + } + + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + #rewrite /vue/(.*) /$1 break; + + proxy_pass http://frontend:8080; + proxy_redirect off; + } location / { - try_files $uri $uri/ /index.php?$args; + try_files $uri $uri/ /index.php?$args; } # access_log /var/log/nginx/access.log main; From 217ca82c0f5bea78a9969935fc85dc1ef058dfb5 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 18 Mar 2021 14:22:05 +0100 Subject: [PATCH 03/44] update vue client --- frontend/vue.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index babcc63d7..eade75e14 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -32,5 +32,5 @@ module.exports = { // Enable CSS source maps. sourceMap: process.env.NODE_ENV !== 'production' }, - outputDir: path.resolve(__dirname, "/vue" ), + outputDir: path.resolve(__dirname, "./dist/vue" ), }; From aa3c28763f67f621e78f5f2de02a456d42c90a35 Mon Sep 17 00:00:00 2001 From: Dario via Pythagoras Date: Thu, 25 Mar 2021 07:49:06 +0100 Subject: [PATCH 04/44] remove protobuf tests, shorten cpu core getter --- login_server/Dockerfile.debug | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/login_server/Dockerfile.debug b/login_server/Dockerfile.debug index 0c7c741d5..e5603bbc1 100644 --- a/login_server/Dockerfile.debug +++ b/login_server/Dockerfile.debug @@ -6,10 +6,10 @@ RUN git clone --recurse-submodules https://github.com/protocolbuffers/protobuf.g WORKDIR /protobuf RUN git checkout v3.9.1 -RUN ./autogen.sh +RUN ./autogen.sh RUN ./configure --enable-static=yes -RUN make -j$(grep processor /proc/cpuinfo | wc -l) -RUN make check +RUN make -j$(nproc) +#RUN make check CMD ["./protobuf"] @@ -43,7 +43,7 @@ RUN cd dependencies/iroha-ed25519 && \ mkdir build && \ cd build && \ cmake .. -DCMAKE_BUILD_TYPE=Debug -DEDIMPL=ref10 -DHASH=sha2_sphlib -DRANDOM=bcryptgen -DBUILD=STATIC && \ - make -j$(grep processor /proc/cpuinfo | wc -l) + make -j$(nproc) RUN cd dependencies/mariadb-connector-c && \ mkdir build && \ @@ -98,7 +98,7 @@ RUN ./compile_pot.sh RUN ./compile_proto.sh RUN cd build && \ cmake -DCMAKE_BUILD_TYPE=Debug .. && \ - make -j$(grep processor /proc/cpuinfo | wc -l) + make -j$(nproc) ######################################################################################################### @@ -129,4 +129,4 @@ EXPOSE 1201 #RUN chmod +x ./start_after_mysql.sh #ENTRYPOINT ["/usr/bin/Gradido_LoginServer"] #CMD gdb -ex=r Gradido_LoginServer -CMD Gradido_LoginServer \ No newline at end of file +CMD Gradido_LoginServer From d0eb304c5951d5a982ec053c0f954ae4f2e3f492 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 25 Mar 2021 10:53:03 +0100 Subject: [PATCH 05/44] simplify code for email send, use new email code instead old system with additional task --- .../src/cpp/HTTPInterface/ElopageWebhook.cpp | 76 +++---------------- 1 file changed, 12 insertions(+), 64 deletions(-) diff --git a/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp b/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp index c29b280a8..eb551b821 100644 --- a/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp +++ b/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp @@ -11,6 +11,7 @@ using namespace Poco::Data::Keywords; #include "../SingletonManager/ConnectionManager.h" #include "../SingletonManager/ErrorManager.h" #include "../SingletonManager/SessionManager.h" +#include "../SingletonManager/EmailManager.h" #include "../ServerConfig.h" @@ -20,7 +21,7 @@ using namespace Poco::Data::Keywords; #include "../controller/EmailVerificationCode.h" #include "../model/table/ElopageBuy.h" - +#include "../lib/DataTypeConverter.h" void ElopageWebhook::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) @@ -277,22 +278,9 @@ int HandleElopageRequestTask::run() saveElopageBuy->scheduleTask(saveElopageBuy); // check product id - Poco::UInt64 product_id = 0; - try { - product_id = stoull(mRequestData.get("product[id]", "0")); - } - catch (const std::invalid_argument& ia) { - std::cerr << __FUNCTION__ << "Invalid argument: " << ia.what() << '\n'; - } - catch (const std::out_of_range& oor) { - std::cerr << __FUNCTION__ << "Out of Range error: " << oor.what() << '\n'; - } - catch (const std::logic_error & ler) { - std::cerr << __FUNCTION__ << "Logical error: " << ler.what() << '\n'; - } - catch (...) { - std::cerr << __FUNCTION__ << "Unknown error" << '\n'; - } + unsigned long long product_id = 0; + DataTypeConverter::strToInt(mRequestData.get("product[id]", "0"), product_id); + std::string order_id = mRequestData.get("order_id", ""); auto param_error_order_id = new ParamError("HandleElopageRequestTask", "order_id", order_id.data()); @@ -380,56 +368,16 @@ int HandleElopageRequestTask::run() sendErrorsAsEmail(); return -4; } + auto em = EmailManager::getInstance(); + if (emailVerification->getModel()->insertIntoDB(false)) { + int noEMail = 0; + DataTypeConverter::strToInt(mRequestData.get("noEmail", "0"), noEMail); - // write email verification code into db - UniLib::controller::TaskPtr saveEmailVerificationCode(new model::table::ModelInsertTask(emailVerification->getModel(), true)); - saveEmailVerificationCode->scheduleTask(saveEmailVerificationCode); - int noEMail = 0; - - std::string noEmailString = mRequestData.get("noEmail", "0"); - try { - noEMail = stoi(noEmailString); - } - catch (const std::invalid_argument& ia) { - std::cerr << __FUNCTION__ << " Invalid argument: " << ia.what() << ", str: " << noEmailString << '\n'; - } - catch (const std::out_of_range& oor) { - std::cerr << __FUNCTION__ << " Out of Range error: " << oor.what() << '\n'; - } - catch (const std::logic_error & ler) { - std::cerr << __FUNCTION__ << " Logical error: " << ler.what() << '\n'; - } - catch (...) { - std::cerr << __FUNCTION__ << " Unknown error" << '\n'; + if (noEMail != 1) { + em->addEmail(new model::Email(emailVerification, newUser, model::EMAIL_USER_VERIFICATION_CODE)); + } } - if (noEMail != 1) { - - // send email to user - /*auto message = new Poco::Net::MailMessage; - - message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, mEmail)); - message->setSubject("Gradido: E-Mail Verification"); - std::stringstream ss; - ss << "Hallo " << mFirstName << " " << mLastName << "," << std::endl << std::endl; - ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Gradido registriert. " << std::endl; - ss << "Wenn du es warst, klicke bitte auf den Link: " << ServerConfig::g_serverPath << "/checkEmail/" << emailVerification->getModel()->getCode() << std::endl; - //ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl; - ss << "oder kopiere den obigen Link in Dein Browserfenster." << std::endl; - ss << std::endl; - - ss << "Mit freundlichen " << u8"Gren" << std::endl; - ss << "Dario, Gradido Server Admin" << std::endl; - - message->addContent(new Poco::Net::StringPartSource(ss.str())); - */ - //UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1)); - //Email(AutoPtr emailVerification, AutoPtr user, EmailType type); - UniLib::controller::TaskPtr sendEmail(new SendEmailTask(new model::Email(emailVerification, newUser, model::EMAIL_USER_VERIFICATION_CODE), ServerConfig::g_CPUScheduler, 1)); - //sendEmail->setParentTaskPtrInArray(prepareEmail, 0); - sendEmail->setParentTaskPtrInArray(saveEmailVerificationCode, 0); - sendEmail->scheduleTask(sendEmail); - } } // if errors occured, send via email From 8a143be8423d7bd894d4f512848895df8b9694b0 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 25 Mar 2021 11:00:00 +0100 Subject: [PATCH 06/44] removed email tasks complete --- .../src/cpp/HTTPInterface/ElopageWebhook.cpp | 2 - login_server/src/cpp/model/Session.cpp | 8 +-- .../src/cpp/tasks/PrepareEmailTask.cpp | 65 ------------------- login_server/src/cpp/tasks/PrepareEmailTask.h | 25 ------- login_server/src/cpp/tasks/SendEmailTask.cpp | 64 ------------------ login_server/src/cpp/tasks/SendEmailTask.h | 36 ---------- 6 files changed, 4 insertions(+), 196 deletions(-) delete mode 100644 login_server/src/cpp/tasks/PrepareEmailTask.cpp delete mode 100644 login_server/src/cpp/tasks/PrepareEmailTask.h delete mode 100644 login_server/src/cpp/tasks/SendEmailTask.cpp delete mode 100644 login_server/src/cpp/tasks/SendEmailTask.h diff --git a/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp b/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp index eb551b821..808695e10 100644 --- a/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp +++ b/login_server/src/cpp/HTTPInterface/ElopageWebhook.cpp @@ -15,8 +15,6 @@ using namespace Poco::Data::Keywords; #include "../ServerConfig.h" -#include "../tasks/PrepareEmailTask.h" -#include "../tasks/SendEmailTask.h" #include "../controller/EmailVerificationCode.h" #include "../model/table/ElopageBuy.h" diff --git a/login_server/src/cpp/model/Session.cpp b/login_server/src/cpp/model/Session.cpp index 86fad389c..dcad0d40a 100644 --- a/login_server/src/cpp/model/Session.cpp +++ b/login_server/src/cpp/model/Session.cpp @@ -12,8 +12,6 @@ #include "../SingletonManager/EmailManager.h" #include "../SingletonManager/SingletonTaskObserver.h" -#include "../tasks/PrepareEmailTask.h" -#include "../tasks/SendEmailTask.h" #include "../tasks/SigningTransaction.h" #include "../tasks/AuthenticatedEncryptionCreateKeyTask.h" #include "../tasks/VerificationEmailResendTask.h" @@ -348,12 +346,14 @@ bool Session::createUser(const std::string& first_name, const std::string& last_ */ //UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1)); //Email(AutoPtr emailVerification, AutoPtr user, EmailType type); - UniLib::controller::TaskPtr sendEmail(new SendEmailTask(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_VERIFICATION_CODE), ServerConfig::g_CPUScheduler, 1)); + auto em = EmailManager::getInstance(); + em->addEmail(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_VERIFICATION_CODE)); + /*UniLib::controller::TaskPtr sendEmail(new SendEmailTask(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_VERIFICATION_CODE), ServerConfig::g_CPUScheduler, 1)); //sendEmail->setParentTaskPtrInArray(prepareEmail, 0); sendEmail->setParentTaskPtrInArray(writeEmailVerification, 0); sendEmail->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_SEND, this)); sendEmail->scheduleTask(sendEmail); - + */ // write user into db // generate and write email verification into db // send email diff --git a/login_server/src/cpp/tasks/PrepareEmailTask.cpp b/login_server/src/cpp/tasks/PrepareEmailTask.cpp deleted file mode 100644 index cc20d9472..000000000 --- a/login_server/src/cpp/tasks/PrepareEmailTask.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "PrepareEmailTask.h" -#include "../lib/Profiler.h" -#include "../ServerConfig.h" -#include "../SingletonManager/ErrorManager.h" - -#include "Poco/Net/SSLException.h" - -PrepareEmailTask::PrepareEmailTask(UniLib::controller::CPUSheduler* cpuScheduler) - : UniLib::controller::CPUTask(cpuScheduler), mMailClientSession(nullptr) -{ - -} - -PrepareEmailTask::~PrepareEmailTask() -{ - if (mMailClientSession) { - delete mMailClientSession; - } -} - -int PrepareEmailTask::run() -{ - if (ServerConfig::g_disableEmail) return 0; - Profiler timeUsed; - mMailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port); - mMailClientSession->login(); - try { - mMailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context); - mMailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password); - } catch(Poco::Net::SSLException& ex) { - printf("[PrepareEmailTask] ssl certificate error: %s\nPlease make sure you have cacert.pem (CA/root certificates) next to binary from https://curl.haxx.se/docs/caextract.html\n", ex.displayText().data()); - return -1; - } - - //printf("[PrepareEmailTask] time: %s\n", timeUsed.string().data()); - /* - session.login(); - session.startTLS(pContext); - if (!username.empty()) - { - session.login(SMTPClientSession::AUTH_LOGIN, username, password); - } - session.sendMessage(message); - session.close(); - */ - - return 0; -} - -int PrepareEmailTask::send(Poco::Net::MailMessage* message) -{ - if (ServerConfig::g_disableEmail) return 0; - - auto er = ErrorManager::getInstance(); - try { - mMailClientSession->sendMessage(*message); - mMailClientSession->close(); - } - catch (Poco::Exception& exc) { - er->addError(new ParamError("PrepareEmailTask::send", "error sending email", exc.displayText().data())); - printf("[PrepareEmailTask::%s] error sending email: %s\n", __FUNCTION__, exc.displayText().data()); - return -1; - } - return 0; -} \ No newline at end of file diff --git a/login_server/src/cpp/tasks/PrepareEmailTask.h b/login_server/src/cpp/tasks/PrepareEmailTask.h deleted file mode 100644 index 4036eefc2..000000000 --- a/login_server/src/cpp/tasks/PrepareEmailTask.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE -#define GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE - -#include "CPUTask.h" -#include "Poco/Net/SecureSMTPClientSession.h" - - - -class PrepareEmailTask : public UniLib::controller::CPUTask -{ -public: - PrepareEmailTask(UniLib::controller::CPUSheduler* cpuScheduler); - virtual ~PrepareEmailTask(); - - virtual int run(); - int send(Poco::Net::MailMessage* message); - virtual const char* getResourceType() const { return "PrepareEmailTask"; }; -protected: - -private: - Poco::Net::SecureSMTPClientSession* mMailClientSession; -}; - - -#endif //GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE \ No newline at end of file diff --git a/login_server/src/cpp/tasks/SendEmailTask.cpp b/login_server/src/cpp/tasks/SendEmailTask.cpp deleted file mode 100644 index e3a5e0069..000000000 --- a/login_server/src/cpp/tasks/SendEmailTask.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "SendEmailTask.h" -#include "PrepareEmailTask.h" -#include "../lib/Profiler.h" -#include "../SingletonManager/ErrorManager.h" -#include "../SingletonManager/EmailManager.h" -#include "../ServerConfig.h" - -#include "Poco/Net/MediaType.h" - -SendEmailTask::SendEmailTask(Poco::Net::MailMessage* mailMessage, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount/* = 0*/) - : UniLib::controller::CPUTask(cpuScheduler, additionalTaskDependenceCount+1), mMailMessage(mailMessage), mEmail(nullptr) -{ -} - -SendEmailTask::SendEmailTask(model::Email*email, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount/* = 0*/) - : UniLib::controller::CPUTask(cpuScheduler, additionalTaskDependenceCount), mMailMessage(nullptr), mEmail(email) -{ - -} - -SendEmailTask::~SendEmailTask() -{ - if (mMailMessage) { - delete mMailMessage; - mMailMessage = nullptr; - } - if (mEmail) { - delete mEmail; - mEmail = nullptr; - } - -} - -int SendEmailTask::run() -{ - if(ServerConfig::g_disableEmail) return 0; - - Profiler timeUsed; - auto er = ErrorManager::getInstance(); - auto parent = getParent(0); - - if (mMailMessage) { - - if (strcmp(parent->getResourceType(), "PrepareEmailTask") != 0) { - er->addError(new Error("SendEmailTask", "first parent isn't PrepareEmailTask")); - er->sendErrorsAsEmail(); - return -1; - } - PrepareEmailTask* prepare = (PrepareEmailTask*)&(*parent); - mMailMessage->setSender(ServerConfig::g_EmailAccount.sender); - - if (prepare->send(mMailMessage)) { - er->sendErrorsAsEmail(); - return -1; - } - } - else if (mEmail) { - auto em = EmailManager::getInstance(); - em->addEmail(mEmail); - mEmail = nullptr; - } - //printf("[SendEmailTask] time: %s\n", timeUsed.string().data()); - return 0; -} \ No newline at end of file diff --git a/login_server/src/cpp/tasks/SendEmailTask.h b/login_server/src/cpp/tasks/SendEmailTask.h deleted file mode 100644 index 86e4b76dc..000000000 --- a/login_server/src/cpp/tasks/SendEmailTask.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE -#define GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE - -#include "CPUTask.h" -#include "Poco/Net/MailMessage.h" - -#include "../model/email/Email.h" - -/* - * @author: Dario Rekowski - * - * @date: 29.09.19 - * @desc: Task for send an email, the first parent dependence pointer must be a prepare email task -*/ - - -class SendEmailTask : public UniLib::controller::CPUTask -{ -public: - - SendEmailTask(Poco::Net::MailMessage* mailMessage, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount = 0); - SendEmailTask(model::Email* email, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount = 0); - virtual ~SendEmailTask(); - - virtual int run(); - - virtual const char* getResourceType() const { return "SendEmailTask"; }; -protected: - -private: - Poco::Net::MailMessage* mMailMessage; - model::Email* mEmail; -}; - - -#endif //GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE \ No newline at end of file From ad85e838ea80a7ce399e1508d575cd9680142b1b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 25 Mar 2021 11:22:05 +0100 Subject: [PATCH 07/44] fix linting errors --- frontend/vue.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 860adc517..81b4f3d75 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -27,8 +27,8 @@ module.exports = { }, css: { // Enable CSS source maps. - sourceMap: process.env.NODE_ENV !== 'production' + sourceMap: process.env.NODE_ENV !== 'production', }, - outputDir: path.resolve(__dirname, "./dist/vue" ), -}; + outputDir: path.resolve(__dirname, './dist/vue' ), +} From 96a2e4c6fb20db3ae0fe98687ccbcbd0b0c832bb Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 25 Mar 2021 13:35:38 +0100 Subject: [PATCH 08/44] fix linting errors --- frontend/vue.config.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 81b4f3d75..5b031b7cf 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -29,6 +29,5 @@ module.exports = { // Enable CSS source maps. sourceMap: process.env.NODE_ENV !== 'production', }, - outputDir: path.resolve(__dirname, './dist/vue' ), + outputDir: path.resolve(__dirname, './dist/vue'), } - From 7f294ef28dec65cde0237cbaf5eab238f2754fec Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Mar 2021 14:07:40 +0100 Subject: [PATCH 09/44] remove error by notification email send failure --- .../src/Model/Transactions/TransactionCreation.php | 4 ++-- .../src/Model/Transactions/TransactionTransfer.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/community_server/src/Model/Transactions/TransactionCreation.php b/community_server/src/Model/Transactions/TransactionCreation.php index e8c6133cb..71bb7fbf0 100644 --- a/community_server/src/Model/Transactions/TransactionCreation.php +++ b/community_server/src/Model/Transactions/TransactionCreation.php @@ -76,7 +76,7 @@ class TransactionCreation extends TransactionBase { { for ($i=0; $i < strlen($str); $i++) { - $ret = TransactionCreation::DRHashRotateLeft($ret, 7) + ord($str{$i}); +// $ret = TransactionCreation::DRHashRotateLeft($ret, 7) + ord($str{$i}); } } return $ret; @@ -272,7 +272,7 @@ class TransactionCreation extends TransactionBase { ->setSubject(__('Gradido Schöpfung erhalten')) ->send(); } catch(Exception $e) { - $this->addError('TransactionCreation::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage()); +// $this->addError('TransactionCreation::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage()); return false; } return true; diff --git a/community_server/src/Model/Transactions/TransactionTransfer.php b/community_server/src/Model/Transactions/TransactionTransfer.php index a509cf0f3..2c53559d6 100644 --- a/community_server/src/Model/Transactions/TransactionTransfer.php +++ b/community_server/src/Model/Transactions/TransactionTransfer.php @@ -253,7 +253,7 @@ class TransactionTransfer extends TransactionBase { ->setSubject(__('Gradidos erhalten')) ->send(); } catch(Exception $e) { - $this->addError('TransactionTransfer::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage()); + //$this->addError('TransactionTransfer::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage()); return false; } return true; From 4a05fe5dcf54ed1f9754b67e926a4a8a93d7ff5b Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 26 Mar 2021 09:14:36 +0100 Subject: [PATCH 10/44] bug fix, store.tate.email not null --- frontend/src/store/store.js | 10 ++++++++-- frontend/src/views/KontoOverview/GddAddWork2.vue | 4 ++-- frontend/src/views/KontoOverview/GddSend.vue | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 01cc6ff10..87029b35b 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -8,7 +8,7 @@ import communityAPI from '../apis/communityAPI' export const store = new Vuex.Store({ state: { session_id: null, - email: null, + email: '', language: 'en', sizeDE: 'normal', sizeGB: 'big', @@ -153,9 +153,15 @@ export const store = new Vuex.Store({ state.ajaxCreateData.amount, state.ajaxCreateData.memo, ) + console.log(result) + if (result.success) { + console.log('send success') + } else { + console.log('send error') + } return result - //console.log(result) + }, ajaxListTransactions: async ({ commit, dispatch, state }) => { // console.log('action: ajaxListTransactions', state.session_id) diff --git a/frontend/src/views/KontoOverview/GddAddWork2.vue b/frontend/src/views/KontoOverview/GddAddWork2.vue index 2e328cc9a..a7b8f77ad 100644 --- a/frontend/src/views/KontoOverview/GddAddWork2.vue +++ b/frontend/src/views/KontoOverview/GddAddWork2.vue @@ -9,7 +9,7 @@ type="number" size="lg" placeholder="23" - style="font-size: xx-large; padding-left: 20px" + style="font-size: xx-large; padding-left: 5px" /> @@ -17,7 +17,7 @@ class="form-control" v-model="date" :config="config" - style="font-size: x-large; padding-left: 20px" + style="font-size: 0.5em; padding-left: 5px" > diff --git a/frontend/src/views/KontoOverview/GddSend.vue b/frontend/src/views/KontoOverview/GddSend.vue index 7dc36efc7..5f8208366 100644 --- a/frontend/src/views/KontoOverview/GddSend.vue +++ b/frontend/src/views/KontoOverview/GddSend.vue @@ -226,7 +226,7 @@ export default { }, methods: { async onDecode(decodedString) { - //console.log('onDecode JSON.parse(decodedString)', JSON.parse(decodedString)) + console.log('onDecode JSON.parse(decodedString)', JSON.parse(decodedString)) const arr = JSON.parse(decodedString) //console.log('qr-email', arr[0].email) //console.log('qr-amount', arr[0].amount) From 61b15beaf5e8f234c546c46c45d197b963677d92 Mon Sep 17 00:00:00 2001 From: Dario Rekowski on RockPI Date: Fri, 26 Mar 2021 10:00:07 +0000 Subject: [PATCH 11/44] insert updateBalance method from stage2, ignore decay for the time beeing --- .../Controller/StateBalancesController.php | 158 +++++++++++++++--- .../src/Model/Entity/StateBalance.php | 6 +- community_server/webroot/js/userSearch.min.js | 2 +- 3 files changed, 141 insertions(+), 25 deletions(-) diff --git a/community_server/src/Controller/StateBalancesController.php b/community_server/src/Controller/StateBalancesController.php index 4afeabd8b..da2a58e65 100644 --- a/community_server/src/Controller/StateBalancesController.php +++ b/community_server/src/Controller/StateBalancesController.php @@ -39,32 +39,143 @@ class StateBalancesController extends AppController $this->set(compact('stateBalances')); } - private function updateBalances($state_user_id) + private function updateBalances($stateUserId) { - $state_balances = $this->StateBalances->find('all')->where(['state_user_id' => $state_user_id]); - if($state_balances->count() == 1) { - $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); - $state_user_transactions = $stateUserTransactionsTable + $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); + $transactionsTable = TableRegistry::getTableLocator()->get('Transactions'); + // info: cakephp use lazy loading, query will be executed later only if needed + $state_balances = $this->StateBalances->find('all')->where(['state_user_id' => $stateUserId]); + $state_user_transactions = $stateUserTransactionsTable ->find('all') - ->where(['state_user_id' => $state_user_id]) + ->where(['state_user_id' => $stateUserId]) ->order(['transaction_id ASC']) - ->contain(['']); - if($state_user_transactions->count() == 0){ - return; - } - $last_state_user_transaction = $state_user_transactions->last(); - $last_transaction = $this->StateBalance->newEntity(); - $last_transaction->amount = $last_state_user_transaction->balance; - $last_transaction->record_date = $last_state_user_transaction->balance_date; - // if entrys are nearly the same, we don't need doing anything - if(abs($last_transaction->decay - $state_balances->decay) < 100) { - return; - } - foreach($state_user_transactions as $state_user_transaction) { - - } - + ->contain(false); + + if(!$state_user_transactions) { + //debug($state_user_transactions); + return true; } + + // first: decide what todo + $create_state_balance = false; + $recalculate_state_user_transactions_balance = false; + $clear_state_balance = false; + $update_state_balance = false; + if($state_balances->count() == 0) { + $create_state_balance = true; + } + if($state_balances->count() > 1) { + $clear_state_balance = true; + $create_state_balance = true; + } + if($state_balances->count() == 1) { + if($state_user_transactions->count() == 0){ + $clear_state_balance = true; + } else { + $last_state_user_transaction = $state_user_transactions->last(); + $last_transaction = $this->StateBalances->newEntity(); + $last_transaction->amount = $last_state_user_transaction->balance; + $last_transaction->record_date = $last_state_user_transaction->balance_date; + // if entrys are nearly the same, we don't need doing anything + if(abs($last_transaction->decay - $state_balances->first()->decay) > 100) { + $recalculate_state_user_transactions_balance = true; + $update_state_balance = true; + } + } + } + + if(!$recalculate_state_user_transactions_balance) { + $last_state_user_transaction = $state_user_transactions->last(); + if($last_state_user_transaction->balance <= 0) { + $recalculate_state_user_transactions_balance = true; + if(!$create_state_balance) { + $update_state_balance = true; + } + } + } + // second: do what is needed + if($clear_state_balance) { + $this->StateBalances->deleteAll(['state_user_id' => $stateUserId]); + } + + $transaction_ids = []; + if($recalculate_state_user_transactions_balance) { + $state_user_transactions_array = $state_user_transactions->toArray(); + foreach($state_user_transactions_array as $i => $state_user_transaction) { + $transaction_ids[$state_user_transaction->transaction_id] = $i; + } + + $transactions = $transactionsTable + ->find('all') + ->where(['id IN' => array_keys($transaction_ids)]) + ->contain(['TransactionCreations', 'TransactionSendCoins']); + + $balance_cursor = $this->StateBalances->newEntity(); + $i = 0; + foreach($transactions as $transaction) { + if($transaction->transaction_type_id > 2) { + continue; + } + $amount_date = null; + $amount = 0; + + if($transaction->transaction_type_id == 1) { + $temp = $transaction->transaction_creations[0]; + + $balance_temp = $this->StateBalances->newEntity(); + $balance_temp->amount = $temp->amount; + $balance_temp->record_date = $temp->target_date; + + $amount = $balance_temp->partDecay($transaction->received); + $amount_date = $transaction->received; + //$amount_date = + } else if($transaction->transaction_type_id == 2) { + $temp = $transaction->transaction_send_coins[0]; + $amount = intval($temp->amount); + // reverse if sender + if($stateUserId == $temp->state_user_id) { + $amount *= -1.0; + } + $amount_date = $transaction->received; + } + if($i == 0) { + $balance_cursor->amount = $amount; + } else { + $balance_cursor->amount = $balance_cursor->partDecay($amount_date) + $amount; + } + $balance_cursor->record_date = $amount_date; + $state_user_transaction_index = $transaction_ids[$transaction->id]; + $state_user_transactions_array[$state_user_transaction_index]->balance = $balance_cursor->amount; + $state_user_transactions_array[$state_user_transaction_index]->balance_date = $balance_cursor->record_date; + $i++; + } + $results = $stateUserTransactionsTable->saveMany($state_user_transactions_array); + $errors = []; + foreach($results as $i => $result) { + if(!$result) { + $errors[$i] = $state_user_transactions_array[$i]->getErrors(); + } + } + if(count($errors)) { + return ['success' => false, 'error' => 'error saving one ore more state user transactions', 'details' => $errors]; + } + } + $state_balance = null; + if($update_state_balance) { + $state_balance = $state_balances->first(); + } + else if($create_state_balance) { + $state_balance = $this->StateBalances->newEntity(); + $state_balance->state_user_id = $stateUserId; + } + if($state_balance) { + $state_balance->amount = $state_user_transactions->last()->balance; + $state_balance->record_date = $state_user_transactions->last()->balance_date; + if(!$this->StateBalances->save($state_balance)) { + return ['success' => false, 'error' => 'error saving state balance', 'details' => $state_balance->getErrors()]; + } + } + return true; } public function overview() @@ -82,7 +193,9 @@ class StateBalancesController extends AppController if ($result !== true) { return $result; } + $user = $session->read('StateUser'); + $this->updateBalances($user['id']); // sendRequestGDT // listPerEmailApi @@ -214,6 +327,7 @@ class StateBalancesController extends AppController } $session = $this->getRequest()->getSession(); $user = $session->read('StateUser'); + $this->updateBalances($user['id']); $public_key_bin = hex2bin($user['public_hex']); $stateUserQuery = $this->StateBalances->StateUsers diff --git a/community_server/src/Model/Entity/StateBalance.php b/community_server/src/Model/Entity/StateBalance.php index 871c55bd8..b1c237a3b 100644 --- a/community_server/src/Model/Entity/StateBalance.php +++ b/community_server/src/Model/Entity/StateBalance.php @@ -57,7 +57,8 @@ class StateBalance extends Entity if($decay_duration === 0) { return $this->amount; } - return $this->amount * pow(0.99999997802044727, $decay_duration); + return $this->amount; + //return $this->amount * pow(0.99999997802044727, $decay_duration); } public function partDecay($target_date) @@ -67,7 +68,8 @@ class StateBalance extends Entity if($decay_duration <= 0) { return $this->amount; } - return $this->amount * pow(0.99999997802044727, $decay_duration); + return $this->amount; + //return $this->amount * pow(0.99999997802044727, $decay_duration); } public function decayDuration($target_date) diff --git a/community_server/webroot/js/userSearch.min.js b/community_server/webroot/js/userSearch.min.js index 9dd4f94f9..d6c3f0872 100644 --- a/community_server/webroot/js/userSearch.min.js +++ b/community_server/webroot/js/userSearch.min.js @@ -1 +1 @@ -!function r(i,o,a){function s(t,e){if(!o[t]){if(!i[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(l)return l(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=o[t]={exports:{}},i[t][0].call(n.exports,function(e){return s(i[t][1][e]||e)},n,n.exports,r,i,o,a)}return o[t].exports}for(var l="function"==typeof require&&require,e=0;e>>0,o=0;if(3===arguments.length)n=arguments[2];else{for(;o=n.clientWidth&&e>=n.clientHeight}),t=(0l[e]&&!r.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),y({},t,n)}};return s.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=E({},u,c[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,r=t.reference,i=e.placement.split("-")[0],o=Math.floor,a=-1!==["top","bottom"].indexOf(i),t=a?"right":"bottom",i=a?"left":"top",a=a?"width":"height";return n[t]o(r[t])&&(e.offsets.popper[i]=o(r[t])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){if(!U(e.instance.modifiers,"arrow","keepTogether"))return e;var n=t.element;if("string"==typeof n){if(!(n=e.instance.popper.querySelector(n)))return e}else if(!e.instance.popper.contains(n))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],i=e.offsets,o=i.popper,a=i.reference,s=-1!==["left","right"].indexOf(r),l=s?"height":"width",u=s?"Top":"Left",c=u.toLowerCase(),t=s?"left":"top",i=s?"bottom":"right",r=A(n)[l];a[i]-ro[i]&&(e.offsets.popper[c]+=a[c]+r-o[i]),e.offsets.popper=x(e.offsets.popper);s=a[c]+a[l]/2-r/2,i=d(e.instance.popper),a=parseFloat(i["margin"+u]),u=parseFloat(i["border"+u+"Width"]),u=s-e.offsets.popper[c]-a-u,u=Math.max(Math.min(o[l]-r,u),0);return e.arrowElement=n,e.offsets.arrow=(y(n={},c,Math.round(u)),y(n,t,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(u,c){if(F(u.instance.modifiers,"inner"))return u;if(u.flipped&&u.placement===u.originalPlacement)return u;var d=C(u.instance.popper,u.instance.reference,c.padding,c.boundariesElement,u.positionFixed),f=u.placement.split("-")[0],p=M(f),m=u.placement.split("-")[1]||"",h=[];switch(c.behavior){case K:h=[f,p];break;case G:h=Y(f);break;case W:h=Y(f,!0);break;default:h=c.behavior}return h.forEach(function(e,t){if(f!==e||h.length===t+1)return u;f=u.placement.split("-")[0],p=M(f);var n=u.offsets.popper,r=u.offsets.reference,i=Math.floor,o="left"===f&&i(n.right)>i(r.left)||"right"===f&&i(n.left)i(r.top)||"bottom"===f&&i(n.top)i(d.right),l=i(n.top)i(d.bottom),r="left"===f&&a||"right"===f&&s||"top"===f&&l||"bottom"===f&&e,n=-1!==["top","bottom"].indexOf(f),i=!!c.flipVariations&&(n&&"start"===m&&a||n&&"end"===m&&s||!n&&"start"===m&&l||!n&&"end"===m&&e),l=!!c.flipVariationsByContent&&(n&&"start"===m&&s||n&&"end"===m&&a||!n&&"start"===m&&e||!n&&"end"===m&&l),l=i||l;(o||r||l)&&(u.flipped=!0,(o||r)&&(f=h[t+1]),l&&(m="end"===m?"start":"start"===m?"end":m),u.placement=f+(m?"-"+m:""),u.offsets.popper=E({},u.offsets.popper,D(u.instance.popper,u.offsets.reference,u.placement)),u=I(u.instance.modifiers,u,"flip"))}),u},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],r=e.offsets,i=r.popper,o=r.reference,a=-1!==["left","right"].indexOf(n),r=-1===["top","left"].indexOf(n);return i[a?"left":"top"]=o[n]-(r?i[a?"width":"height"]:0),e.placement=M(t),e.offsets.popper=x(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!U(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=L(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.right or , not
","or ."].join(" ")),Ee("showOnInit"===t,"The `showOnInit` prop was renamed to `showOnCreate` in v5."),Ee("arrowType"===t,["The `arrowType` prop was removed in v5 in favor of overloading the `arrow`","prop.","\n\n",'"round" string was replaced with importing the string from the package.',"\n\n","* import {roundArrow} from 'tippy.js'; (ESM version)\n","* const {roundArrow} = tippy; (IIFE CDN version)","\n\n",'Before: {arrow: true, arrowType: "round"}\n',"After: {arrow: roundArrow}`"].join(" ")),Ee("touchHold"===t,["The `touchHold` prop was removed in v5 in favor of overloading the `touch`","prop.","\n\n","Before: {touchHold: true}\n",'After: {touch: "hold"}'].join(" ")),Ee("size"===t,["The `size` prop was removed in v5. Instead, use a theme that specifies","CSS padding and font-size properties."].join(" ")),Ee("theme"===t&&"google"===e,'The included theme "google" was renamed to "material" in v5.'),Ee(n,["Specifying placement in `popperOptions` is not supported. Use the base-level","`placement` prop instead.","\n\n",'Before: {popperOptions: {placement: "bottom"}}\n','After: {placement: "bottom"}'].join(" ")),Ee(r,["`"+t+"`","is not a valid prop. You may have spelled it incorrectly, or if it's a","plugin, forgot to pass it in an array as props.plugins.","\n\n","In v5, the following props were turned into plugins:","\n\n","* animateFill\n","* followCursor\n","* sticky","\n\n","All props: https://atomiks.github.io/tippyjs/all-props/\n","Plugins: https://atomiks.github.io/tippyjs/plugins/"].join(" "))})}var Ce={passive:!0},d="tippy-iOS",Ne="tippy-popper",ke="tippy-tooltip",Ae="tippy-content",f="tippy-arrow",p="tippy-svg-arrow",Me="."+Ne,m="."+ke,h="."+Ae,v="."+f,g="."+p,De={isTouch:!1},b=0;function y(){De.isTouch||(De.isTouch=!0,window.performance&&document.addEventListener("mousemove",w))}function w(){var e=performance.now();e-b<20&&(De.isTouch=!1,document.removeEventListener("mousemove",w)),b=e}function E(){var e,t=document.activeElement;i(t)&&(e=t._tippy,t.blur&&!e.state.isVisible&&t.blur())}var e="undefined"!=typeof window&&"undefined"!=typeof document,T=e?navigator.userAgent:"",Le=/MSIE |Trident\//.test(T),x=e&&/iPhone|iPad|iPod/.test(navigator.platform);function Ie(e){e=e&&x&&De.isTouch;document.body.classList[e?"add":"remove"](d)}function Fe(e){return e.split("-")[0]}function Pe(e){e.setAttribute("data-inertia","")}function Re(e){e.setAttribute("data-interactive","")}function je(e,t){l(t.content)?(n(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(e[t.allowHTML?"innerHTML":"textContent"]=t.content)}function Be(e){return{tooltip:e.querySelector(m),content:e.querySelector(h),arrow:e.querySelector(v)||e.querySelector(g)}}function ze(e){var t=oe();return!0===e?t.className=f:(t.className=p,l(e)?t.appendChild(e):n(t,e)),t}function Ue(e,t,n){var r=Be(e),i=r.tooltip,o=r.content,r=r.arrow;e.style.zIndex=""+n.zIndex,i.setAttribute("data-animation",n.animation),i.style.maxWidth="number"==typeof(e=n.maxWidth)?e+"px":e,n.role?i.setAttribute("role",n.role):i.removeAttribute("role"),t.content!==n.content&&je(o,n),!t.arrow&&n.arrow?(i.appendChild(ze(n.arrow)),i.setAttribute("data-arrow","")):t.arrow&&!n.arrow?(i.removeChild(r),i.removeAttribute("data-arrow")):t.arrow!==n.arrow&&(i.removeChild(r),i.appendChild(ze(n.arrow))),!t.interactive&&n.interactive?Re(i):t.interactive&&!n.interactive&&i.removeAttribute("data-interactive"),!t.inertia&&n.inertia?Pe(i):t.inertia&&!n.inertia&&i.removeAttribute("data-inertia"),t.theme!==n.theme&&(He(i,"remove",t.theme),He(i,"add",n.theme))}function Ve(t,n,r){["transitionend","webkitTransitionEnd"].forEach(function(e){t[n+"EventListener"](e,r)})}function He(t,n,e){pe(e).forEach(function(e){t.classList[n](e+"-theme")})}var Ye=1,Ke=[],Ge=[];function _(l,e){var i,t,n,r=Se(l,Z({},Te,{},_e(e)));if(!r.multiple&&l._tippy)return null;var o,a,s,u,c,d=!1,f=!1,p=!1,m=0,h=[],v=le(H,r.interactiveDebounce),g=ve(r.triggerTarget||l),b=Ye++,y=function(e,t){var n=oe();n.className=Ne,n.style.position="absolute",n.style.top="0",n.style.left="0";var r=oe();return r.className=ke,r.id="tippy-"+e,r.setAttribute("data-state","hidden"),r.setAttribute("tabindex","-1"),He(r,"add",t.theme),(e=oe()).className=Ae,e.setAttribute("data-state","hidden"),t.interactive&&Re(r),t.arrow&&(r.setAttribute("data-arrow",""),r.appendChild(ze(t.arrow))),t.inertia&&Pe(r),je(e,t),r.appendChild(e),n.appendChild(r),Ue(n,t,t),n}(b,r),w=Be(y),E=(c=r.plugins).filter(function(e,t){return c.indexOf(e)===t}),T=w.tooltip,e=w.content,x=[T,e],_={id:b,reference:l,popper:y,popperChildren:w,popperInstance:null,props:r,state:{currentPlacement:null,isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:E,clearDelayTimeouts:function(){clearTimeout(i),clearTimeout(t),cancelAnimationFrame(n)},setProps:function(n){var t,e;"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("setProps")),_.state.isDestroyed||("production"!==We.env.NODE_ENV&&(Oe(n,E),Ee(!!n.plugins&&(n.plugins.length!==E.length||E.some(function(e,t){return!n.plugins||!n.plugins[t]||e!==n.plugins[t]})),"Cannot update plugins")),M("onBeforeUpdate",[_,n]),U(),t=_.props,(e=Se(l,Z({},_.props,{},n,{ignoreAttributes:!0}))).ignoreAttributes=me(n.ignoreAttributes,t.ignoreAttributes),_.props=e,z(),t.interactiveDebounce!==e.interactiveDebounce&&(I(),v=le(H,e.interactiveDebounce)),Ue(y,t,e),_.popperChildren=Be(y),t.triggerTarget&&!e.triggerTarget?he(t.triggerTarget).forEach(function(e){e.removeAttribute("aria-expanded")}):e.triggerTarget&&l.removeAttribute("aria-expanded"),L(),_.popperInstance&&(xe.some(function(e){return Q(n,e)&&n[e]!==t[e]})?(e=_.popperInstance.reference,_.popperInstance.destroy(),W(),_.popperInstance.reference=e,_.state.isVisible&&_.popperInstance.enableEventListeners()):_.popperInstance.update()),M("onAfterUpdate",[_,n]))},setContent:function(e){_.setProps({content:e})},show:function(e){void 0===e&&(e=ee(_.props.duration,0,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("show"));var t=_.state.isVisible,n=_.state.isDestroyed,r=!_.state.isEnabled,i=De.isTouch&&!_.props.touch;t||n||r||i||k().hasAttribute("disabled")||(_.popperInstance||W(),M("onShow",[_],!1),!1!==_.props.onShow(_)&&(P(),y.style.visibility="visible",_.state.isVisible=!0,_.state.isMounted||ae(x.concat(y),0),a=function(){_.state.isVisible&&(ae([y],_.props.updateDuration),ae(x,e),se(x,"visible"),D(),L(),ge(Ge,_),Ie(!0),_.state.isMounted=!0,M("onMount",[_]),j(e,function(){_.state.isShown=!0,M("onShown",[_])}))},function(){m=0;var e=_.props.appendTo,t=k(),n=_.props.interactive&&e===Te.appendTo||"parent"===e?t.parentNode:re(e,[t]);n.contains(y)||n.appendChild(y),"production"!==We.env.NODE_ENV&&Ee(_.props.interactive&&e===Te.appendTo&&t.nextElementSibling!==y,["Interactive tippy element may not be accessible via keyboard navigation","because it is not directly after the reference element in the DOM source","order.","\n\n","Using a wrapper
or tag around the reference element solves","this by creating a new parentNode context.","\n\n","Specifying `appendTo: document.body` silences this warning, but it","assumes you are using a focus management solution to handle keyboard","navigation.","\n\n","See: https://atomiks.github.io/tippyjs/accessibility/#interactivity"].join(" ")),ie(_.popperInstance.modifiers,"flip","enabled",_.props.flip),_.popperInstance.enableEventListeners(),_.popperInstance.update()}()))},hide:function(e){void 0===e&&(e=ee(_.props.duration,1,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("hide"));var t=!_.state.isVisible&&!d,n=_.state.isDestroyed,r=!_.state.isEnabled&&!d;t||n||r||(M("onHide",[_],!1),!1===_.props.onHide(_)&&!d||(R(),y.style.visibility="hidden",_.state.isVisible=!1,_.state.isShown=!1,ae(x,e),se(x,"hidden"),D(),L(),j(e,function(){!_.state.isVisible&&y.parentNode&&y.parentNode.contains(y)&&(_.popperInstance.disableEventListeners(),_.popperInstance.options.placement=_.props.placement,y.parentNode.removeChild(y),0===(Ge=Ge.filter(function(e){return e!==_})).length&&Ie(!1),_.state.isMounted=!1,M("onHidden",[_]))})))},enable:function(){_.state.isEnabled=!0},disable:function(){_.hide(),_.state.isEnabled=!1},destroy:function(){"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("destroy")),_.state.isDestroyed||(d=!0,_.clearDelayTimeouts(),_.hide(0),U(),delete l._tippy,_.popperInstance&&_.popperInstance.destroy(),d=!1,_.state.isDestroyed=!0,M("onDestroy",[_]))}};l._tippy=_,y._tippy=_;var S=E.map(function(e){return e.fn(_)}),O=l.hasAttribute("aria-expanded");return z(),L(),r.lazy||W(),M("onCreate",[_]),r.showOnCreate&&$(),y.addEventListener("mouseenter",function(){_.props.interactive&&_.state.isVisible&&_.clearDelayTimeouts()}),y.addEventListener("mouseleave",function(e){_.props.interactive&&fe(_.props.trigger,"mouseenter")&&(v(e),g.addEventListener("mousemove",v))}),_;function C(){var e=_.props.touch;return Array.isArray(e)?e:[e,0]}function N(){return"hold"===C()[0]}function k(){return u||l}function A(e){return _.state.isMounted&&!_.state.isVisible||De.isTouch||o&&"focus"===o.type?0:ee(_.props.delay,e?0:1,Te.delay)}function M(t,n,e){void 0===e&&(e=!0),S.forEach(function(e){Q(e,t)&&e[t].apply(e,n)}),e&&(e=_.props)[t].apply(e,n)}function D(){var n,r,e=_.props.aria;e&&(n="aria-"+e,r=T.id,he(_.props.triggerTarget||l).forEach(function(e){var t=e.getAttribute(n);_.state.isVisible?e.setAttribute(n,t?t+" "+r:r):(t=t&&t.replace(r,"").trim())?e.setAttribute(n,t):e.removeAttribute(n)}))}function L(){O||he(_.props.triggerTarget||l).forEach(function(e){_.props.interactive?e.setAttribute("aria-expanded",_.state.isVisible&&e===k()?"true":"false"):e.removeAttribute("aria-expanded")})}function I(){g.body.removeEventListener("mouseleave",X),g.removeEventListener("mousemove",v),Ke=Ke.filter(function(e){return e!==v})}function F(e){if(!_.props.interactive||!y.contains(e.target)){if(k().contains(e.target)){if(De.isTouch)return;if(_.state.isVisible&&fe(_.props.trigger,"click"))return}!0===_.props.hideOnClick&&(f=!1,_.clearDelayTimeouts(),_.hide(),p=!0,setTimeout(function(){p=!1}),_.state.isMounted||R())}}function P(){g.addEventListener("mousedown",F,!0)}function R(){g.removeEventListener("mousedown",F,!0)}function j(e,t){function n(e){e.target===T&&(Ve(T,"remove",n),t())}if(0===e)return t();Ve(T,"remove",s),Ve(T,"add",n),s=n}function B(t,n,r){void 0===r&&(r=!1),he(_.props.triggerTarget||l).forEach(function(e){e.addEventListener(t,n,r),h.push({node:e,eventType:t,handler:n,options:r})})}function z(){N()&&(B("touchstart",V,Ce),B("touchend",Y,Ce)),pe(_.props.trigger).forEach(function(e){if("manual"!==e)switch(B(e,V),e){case"mouseenter":B("mouseleave",Y);break;case"focus":B(Le?"focusout":"blur",K);break;case"focusin":B("focusout",K)}})}function U(){h.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),h=[]}function V(t){var e,n,r=!1;!_.state.isEnabled||G(t)||p||(u=(o=t).currentTarget,L(),!_.state.isVisible&&ne(t)&&Ke.forEach(function(e){return e(t)}),"click"!==t.type||fe(_.props.trigger,"mouseenter")&&!f||!1===_.props.hideOnClick||!_.state.isVisible?(e=(n=C())[0],n=n[1],De.isTouch&&"hold"===e&&n?i=setTimeout(function(){$(t)},n):$(t)):r=!0,"click"===t.type&&(f=!r),r&&X(t))}function H(e){var a,s,t=de(e.target,function(e){return e===l||e===y});"mousemove"===e.type&&t||(t=ce(y.querySelectorAll(Me)).concat(y).map(function(e){var t=e._tippy,n=t.popperChildren.tooltip,t=t.props.interactiveBorder;return{popperRect:e.getBoundingClientRect(),tooltipRect:n.getBoundingClientRect(),interactiveBorder:t}}),a=e.clientX,s=e.clientY,t.every(function(e){var t=e.popperRect,n=e.tooltipRect,r=e.interactiveBorder,i=Math.min(t.top,n.top),o=Math.max(t.right,n.right),e=Math.max(t.bottom,n.bottom),n=Math.min(t.left,n.left);return r element.cloneNode(true)"].join(" ")));t=t.reduce(function(e,t){t=t&&_(t,a);return t&&e.push(t),e},[]);return l(e)?t[0]:t}S.version="5.2.1",S.defaultProps=Te,S.setDefaultProps=o,S.currentInput=De,C.BACKDROP_CLASS="tippy-backdrop",C.ROUND_ARROW='',C._extends=Z,C.arrayFrom=ce,C.closestCallback=de,C.createTippyWithPlugins=function(r){"production"!==We.env.NODE_ENV&&Ee(!0,["createTippyWithPlugins([...]) has been deprecated.","\n\n","Use tippy.setDefaultProps({plugins: [...]}) instead."].join(" "));function e(e,t,n){return void 0===n&&(n=[]),n=(t=void 0===t?{}:t).plugins||n,S(e,Z({},t,{plugins:[].concat(r,n)}))}return e.version="5.2.1",e.defaultProps=Te,e.setDefaultProps=o,e.currentInput=De,e},C.currentInput=De,C.defaultProps=Te,C.div=oe,C.errorWhen=u,C.getBasePlacement=Fe,C.getOwnerDocument=ve,C.hideAll=function(e){var e=void 0===e?{}:e,n=e.exclude,r=e.duration;Ge.forEach(function(e){var t=!1;(t=n?i(n)?e.reference===n:e.popper===n.popper:t)||e.hide(r)})},C.includes=fe,C.isBrowser=e,C.isMouseEvent=ne,C.normalizeToArray=he,C.removeProperties=a,C.setVisibilityState=se,C.tippy=S,C.useIfDefined=me,C.warnWhen=Ee}.call(this)}.call(this,O("_process"))},{_process:4,"popper.js":3}],7:[function(i,e,o){!function(f){!function(){"use strict";Object.defineProperty(o,"__esModule",{value:!0});var y=i("./tippy.chunk.cjs.js");i("popper.js");var d={mouseover:"mouseenter",focusin:"focus",click:"click"},e={name:"animateFill",defaultValue:!1,fn:function(e){var t=e.popperChildren,n=t.tooltip,r=t.content,i=e.props.animateFill?((t=y.div()).className=y.BACKDROP_CLASS,y.setVisibilityState([t],"hidden"),t):null;function o(){e.popperChildren.backdrop=i}return{onCreate:function(){i&&(o(),n.insertBefore(i,n.firstElementChild),n.setAttribute("data-animatefill",""),n.style.overflow="hidden",e.setProps({animation:"shift-away",arrow:!1}))},onMount:function(){var e,t;i&&(e=n.style.transitionDuration,t=Number(e.replace("ms","")),r.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y.setVisibilityState([i],"visible"),"production"!==f.env.NODE_ENV&&(y.warnWhen("absolute"!==getComputedStyle(i).position,"The `tippy.js/dist/backdrop.css` stylesheet has not been\n imported!\n \n The `animateFill` plugin requires this stylesheet to work."),y.warnWhen("none"===getComputedStyle(n).transform,"The `tippy.js/animations/shift-away.css` stylesheet has not\n been imported!\n \n The `animateFill` plugin requires this stylesheet to work.")))},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y.setVisibilityState([i],"hidden")},onAfterUpdate:function(){o()}}}},t={name:"followCursor",defaultValue:!1,fn:function(c){var d,f=c.reference,p=c.popper,m=null,e=y.getOwnerDocument(c.props.triggerTarget||f),n=null,r=!1,i=c.props;function t(){return"manual"===c.props.trigger.trim()}function o(){var e=!!t()||null!==n&&!(0===n.clientX&&0===n.clientY);return c.props.followCursor&&e}function h(){return y.currentInput.isTouch||"initial"===c.props.followCursor&&c.state.isVisible}function a(){c.popperInstance&&m&&(c.popperInstance.reference=m)}function s(){var e,t;!o()&&c.props.placement===i.placement||(t=(e=i.placement).split("-")[1],r=!0,c.setProps({placement:o()&&t?e.replace(t,"start"===t?"end":"start"):e}),r=!1)}function l(){c.popperInstance&&o()&&h()&&c.popperInstance.disableEventListeners()}function u(){o()?e.addEventListener("mousemove",b):a()}function v(){o()&&b(d)}function g(){e.removeEventListener("mousemove",b)}function b(e){var t,n,r,i,o,a,s=d=e,l=s.clientX,u=s.clientY;c.popperInstance&&c.state.currentPlacement&&(s=y.closestCallback(e.target,function(e){return e===f}),e=c.props.followCursor,t="horizontal"===e,n="vertical"===e,r=y.includes(["top","bottom"],y.getBasePlacement(c.state.currentPlacement)),e={size:e=r?p.offsetWidth:p.offsetHeight,x:r?e:0,y:r?0:e},i=e.size,o=e.x,a=e.y,!s&&c.props.interactive||(null===m&&(m=c.popperInstance.reference),c.popperInstance.reference={referenceNode:f,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){var e=f.getBoundingClientRect();return{width:r?i:0,height:r?0:i,top:(t?e.top:u)-a,bottom:(t?e.bottom:u)+a,left:(n?e.left:l)-o,right:(n?e.right:l)+o}}},c.popperInstance.update()),h()&&g())}return{onAfterUpdate:function(e,t){var n;r||(n=t,Object.keys(n).forEach(function(e){i[e]=y.useIfDefined(n[e],i[e])}),t.placement&&s()),t.placement&&l(),requestAnimationFrame(v)},onMount:function(){v(),l()},onShow:function(){t()&&(d=n={clientX:0,clientY:0},s(),u())},onTrigger:function(e,t){n||(y.isMouseEvent(t)&&(n={clientX:t.clientX,clientY:t.clientY},d=t),s(),u())},onUntrigger:function(){c.state.isVisible||(g(),n=null)},onHidden:function(){g(),a(),n=null}}}},n={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t=e.reference;function n(){return e.props.inlinePositioning}return{onHidden:function(){n()&&(e.popperInstance.reference=t)},onShow:function(){n()&&(e.popperInstance.reference={referenceNode:t,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){return function(t,e,n){if(n.length<2||null===t)return e;switch(t){case"top":case"bottom":var r=n[0],i=n[n.length-1],o="top"===t,a=r.top,s=i.bottom,l=(o?r:i).left,i=(o?r:i).right;return{top:a,bottom:s,left:l,right:i,width:i-l,height:s-a};case"left":case"right":var u=Math.min.apply(Math,n.map(function(e){return e.left})),c=Math.max.apply(Math,n.map(function(e){return e.right})),s=n.filter(function(e){return"left"===t?e.left===u:e.right===c}),a=s[0].top,s=s[s.length-1].bottom;return{top:a,bottom:s,left:u,right:c,width:c-u,height:s-a};default:return e}}(e.state.currentPlacement&&y.getBasePlacement(e.state.currentPlacement),t.getBoundingClientRect(),y.arrayFrom(t.getClientRects()))}})}}}},r={name:"sticky",defaultValue:!1,fn:function(n){var r=n.reference,i=n.popper;function o(e){return!0===n.props.sticky||n.props.sticky===e}var a=null,s=null;function l(){var e=o("reference")?(n.popperInstance?n.popperInstance.reference:r).getBoundingClientRect():null,t=o("popper")?i.getBoundingClientRect():null;(e&&u(a,e)||t&&u(s,t))&&n.popperInstance.update(),a=e,s=t,n.state.isMounted&&requestAnimationFrame(l)}return{onMount:function(){n.props.sticky&&l()}}}};function u(e,t){return!e||!t||e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left}o.createTippyWithPlugins=y.createTippyWithPlugins,o.default=y.tippy,o.hideAll=y.hideAll,o.roundArrow=y.ROUND_ARROW,o.animateFill=e,o.createSingleton=function(o,e,t){void 0===e&&(e={}),void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!Array.isArray(o),["The first argument passed to createSingleton() must be an array of tippy","instances. The passed value was",String(o)].join(" ")),t=e.plugins||t,o.forEach(function(e){e.disable()});var a,s,l=y._extends({},y.defaultProps,{},e).aria,n=!1,u=o.map(function(e){return e.reference}),r={fn:function(r){function i(e){var t;a&&(t="aria-"+a,e&&!r.props.interactive?s.setAttribute(t,r.popperChildren.tooltip.id):s.removeAttribute(t))}return{onAfterUpdate:function(e,t){t=t.aria;void 0!==t&&t!==l&&(n?(n=!0,r.setProps({aria:null}),n=!1):l=t)},onDestroy:function(){o.forEach(function(e){e.enable()})},onMount:function(){i(!0)},onUntrigger:function(){i(!1)},onTrigger:function(e,t){var n=t.currentTarget,t=u.indexOf(n);n!==s&&(s=n,a=l,r.state.isVisible&&i(!0),r.popperInstance.reference=n,r.setContent(o[t].props.content))}}}};return y.tippy(y.div(),y._extends({},e,{plugins:[r].concat(t),aria:null,triggerTarget:u}))},o.delegate=function(e,r,t){void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!(r&&r.target),["You must specity a `target` prop indicating a CSS selector string matching","the target elements that should receive a tippy."].join(" ")),t=r.plugins||t;var i=[],o=[],a=r.target,n=y.removeProperties(r,["target"]),s=y._extends({},n,{plugins:t,trigger:"manual"}),l=y._extends({},n,{plugins:t,showOnCreate:!0}),s=y.tippy(e,s);function u(e){var t,n;!e.target||(n=e.target.closest(a))&&(t=n.getAttribute("data-tippy-trigger")||r.trigger||y.defaultProps.trigger,!y.includes(t,d[e.type])||(n=y.tippy(n,l))&&(o=o.concat(n)))}function c(e,t,n,r){e.addEventListener(t,n,r=void 0===r?!1:r),i.push({node:e,eventType:t,handler:n,options:r})}return y.normalizeToArray(s).forEach(function(e){var t=e.destroy;e.destroy=function(e){(e=void 0===e?!0:e)&&o.forEach(function(e){e.destroy()}),o=[],i.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),i=[],t()},c(e=e.reference,"mouseover",u),c(e,"focusin",u),c(e,"click",u)}),s},o.followCursor=t,o.inlinePositioning=n,o.sticky=r}.call(this)}.call(this,i("_process"))},{"./tippy.chunk.cjs.js":6,_process:4,"popper.js":3}],8:[function(e,t,n){"use strict";var r=a(e("mithril")),i=a(e("./view")),o=a(e("./texte/de"));function a(e){return e&&e.__esModule?e:{default:e}}document,window,domIsReady(function(){window.texte=o.default,r.default.mount(window.document.getElementById("gradido-mithril-user-search"),i.default)})},{"./texte/de":13,"./view":14,mithril:1}],9:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=o(e("mithril")),i=o(e("numeral"));function o(e){return e&&e.__esModule?e:{default:e}}var a=!1;n.default={view:function(e){return(0,r.default)("span",(e=e.attrs.centAmount/1e4,i.default.locale("de"),(0,i.default)(e).format("0,0.00")+" GDD"))},oninit:function(e){a||(i.default.register("locale","de",{delimiters:{thousands:".",decimal:","},currency:{symbol:"€"}}),i.default.locale("de"),a=!0)}}},{mithril:1,numeral:2}],10:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e){return{oncreate:function(e){(0,r.default)(e.dom,{content:e.attrs.accountState.getTooltipText(),placement:"right-start",theme:"rippleUI-theme",arrow:!1})},onremove:function(){},view:function(){return e}}},i(e("mithril"));var r=i(e("tippy.js"));function i(e){return e&&e.__esModule?e:{default:e}}},{mithril:1,"tippy.js":7}],11:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};n.default={view:function(e){return(0,r.default)("div.modal.visible-modal",{tabindex:"-1",role:"dialog"},(0,r.default)("div.modal-dialog",{role:"document"},(0,r.default)("div.modal-content",[(0,r.default)("p.grid-header",e.attrs.title),(0,r.default)("div.modal-body",e.attrs.body),(0,r.default)("div.modal-footer",[(0,r.default)("button.btn.btn-primary",{type:"button",disabled:e.attrs.btnDisabled,"data-dismiss":"modal",onclick:e.attrs.dismiss},"Ok")])])))},oninit:function(e){}}},{mithril:1}],12:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};function i(e,t){for(var n=0;n';if(t+='
'+e.description.title+"
",null!=e.description.lines){for(var n in t+="
    ",e.description.lines){var r=e.description.lines[n],i=window.texte.ACCOUNT_STATES.LINES[n],n="mdi ";if("+"===r)n+=" mdi-check color-primary";else if("-"===r)n+=" mdi-close color-danger";else{if("/"!==r)continue;n+=" mdi-alert-circle-outline color-warning"}t+="
  • ",t+='',t+=" ",t+=""+i.title+": "+i[r]+"",t+="
  • "}t+="
"}return t+"
"}}]),e=o;function o(e){!function(e){if(!(e instanceof o))throw new TypeError("Cannot call a class as a function")}(this),this.stateName=e,this.texte=window.texte.ACCOUNT_STATES[e]}n.default=e},{mithril:1}],13:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0,n.default={ACCOUNT_STATES:{LINES:[{title:"Login-Server","+":"Das Benutzer-Konto liegt auf dem Login-Server!","-":"Das Benutzer-Konto existiert nicht auf dem Login-Server!"},{title:"Konto Aktivierung","+":"Der Benutzer hat sein Konto aktiviert!","-":"Der Benutzer hat noch nicht auf den Link in seiner E-Mail geklickt!"},{title:"Schlüsselpaar","+":"Es wurde bereits ein Schlüsselpaar für den Benutzer erzeugt und ihm eine Passphrase gezeigt!","-":"Es wurden noch keine Schlüssel und Passphrase für den Benutzer erzeugt!"},{title:"Gemeinschafts-Server","+":"Das Benutzer-Konto wurde auf auf den Gemeinschafts-Server kopiert!","-":"Das Benutzer-Konto wurde noch nicht auf den Gemeinschafts-Server kopiert!"},{title:"Gradidos","+":"Es können Gradidos an den Benutzer geschickt werden!","-":"Es können noch keine Gradidos an den Benutzer geschickt werden!","/":"Es können theoretisch Gradidos an den Benutzer geschickt werden, aber es ist nicht sicher ob er sie verwenden kann!"}],"account created":{title:"Konto angelegt",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung.",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account not on login-server":{title:"Konto nicht auf Login-Server",color:"danger",description:{title:"Das Konto existiert nicht (mehr) auf dem Login-Server: ein Fehler oder es wurde nur teilweise gelöscht.",lines:["-","+","+","+","/"]},todo:{title:"Wenn der Benutzer seine Passphrase hat, kann er sein Konto auf dem Login-Server wiederherstellen, ansonsten bleibt eigentlich nur das Konto auf dem Gemeinschafts-Server zu löschen",lines:["Nachdem das Konto auf dem Login-Server kopiert wurde, bekommt der Benutzer automatisch eine E-Mail mit einem Link zur Kontoaktivierung zugeschickt.","{{copy-from-community-to-login-server}}","
","Es wird nur der Benutzer gelöscht, nicht seine Transaktionen. Zu diesen hat er wieder Zugriff wenn er sein Konto mit Hilfe seiner Passphrase wiederhergestellt hat.","{{user-transactions-overview}}","{{delete-from-community-server}}"]}},"email activated":{title:"Konto aktiviert",color:"primary",description:{title:"Das Konto wurde vom Benutzer aktiviert und Schlüssel erzeugt, aber noch nicht auf diesen Gemeinschafts-Server kopiert.",lines:["+","+","+","-","-"]},todo:{title:"Das Benutzer Konto müsste vom Login-Server auf den Gemeinschafts-Server kopiert werden.",lines:["{{copy-from-login-to-community-server}}"]}},"account copied to community":{title:"Konto auf Gemeinschafts-Server",color:"success",description:{title:"Das Konto ist vollständig aktiviert und es können Gradidos überwiesen werden.",lines:["+","+","+","+","+"]}},"email not activated":{title:"Konto nicht aktiviert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung. Es wurde aber noch nicht aktiviert!",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account multiple times on login-server":{title:"Konto mehrfach vorhanden",color:"danger",description:{title:"Das Konto ist auf dem Login-Server mehrfach vorhanden, das sollte nicht sein, auf dem Login-Server muss ein Fehler passiert sein!!"},todo:{title:"Du solltest mit dem Entwickler in Kontakt treten.",lines:["{{mailto-developer}}"]}},"account not on community server":{title:"Konto nicht auf Gemeinschafts-Server",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber existiert noch nicht auf dem Gemeinschafts-Server!",lines:["+",null,"-","-","-"]},todo:{title:"Das sollte eigentlich nicht passieren, du solltest mit dem Entwickler sprechen.",lines:["{{mailto-developer}}"]}},"no keys":{title:"Keine Schlüssel generiert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber es wurden noch keine Schlüssel generiert!",lines:["+","+","-","-","-"]},todo:{title:"Der Benutzer muss sich mit seinen Daten einloggen, dann müsste er automatisch durch die Schlüsselgenerierung/Konto-Wiederherstellung geführt werden",lines:["{{mailto-user-login}}","Wenn der Benutzer sein Passwort vergessen hat, kann er sich auch eine Passwort Reset E-Mail zuschicken lassen.","{{reset-password}}","{{mailto-reset-password}}"]}}},NO_USER_FOUND:"Keine Benutzer gefunden",USER_FOUND:"Benutzer gefunden",NAME:"Name",EMAIL:"E-Mail",BALANCE:"Kontostand",PUBLIC_KEY:"Öffentlicher
Schlüssel",CREATED:"Erstellt",COPY_FROM_LOGIN_TO_COMMUNITY:"Vom Login-Server zum Gemeinschafts-Server kopieren",COPY_FROM_LOGIN_TO_COMMUNITY_SUCCESS:"Kontodaten wurden erfolgreich vom Login-Server zu diesem Gemeinschafts-Server kopiert!",COPY_FROM_COMMUNITY_TO_LOGIN:"Vom Gemeinschafts-Server zum Login-Server kopieren",COPY_FROM_COMMUNITY_TO_LOGIN_SUCCESS:"Kontodaten wurden erfolgreich vom Gemeinschafts-Server zum Login-Server kopiert!",DELETE_FROM_COMMUNITY:"Benutzer Konto vom Gemeinschafts-Server löschen",DELETE_FROM_COMMUNITY_SUCCESS:"Benutzer Konto vom Gemeinschafts-Server erfolgreich gelöscht",VERIFICATION_EMAIL_RESEND:"Verification Email erneut zusenden",VERIFICATION_EMAIL_RESEND_SUCCESS:"Verification Email wird erneut zugestellt",COPY_FAILED:"Fehler beim Kopieren",DELETE_FAILED:"Fehler beim löschen",RESEND_FAILED:"Senden fehlgeschlagen",AJAX_CRITICAL:"Kritischer Fehler beim Ajax-Request",COPY_IN_PROGRESS:"Wird kopiert",DELETE_IN_PROGRESS:"Wird gelöscht",RESEND_IN_PROGRESS:"Wird gesendet",RECEIVE_TRANSACTIONS_COUNT:"Erhaltene Transaktionen: ",SENDED_TRANSACTIONS_COUNT:"gesendete Transaktionen: ",CREATION_TRANSACTIONS_COUNT:"erhaltende Schöpfungs-Transaktionen: "}},{}],14:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=a(e("mithril")),i=a(e("./userTable")),o=a(e("../model/AccountState"));function a(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(e){return void 0===g_users?(0,r.default)(""):0"===e)return i.default.trust("
    ");if(t){var n=function(){switch(t[1]){case"copy-from-login-to-community-server":return o.default;case"user-transactions-overview":return a.default;case"copy-from-community-to-login-server":return s.default;case"verification-resend":return l.default}return null}();return n?(0,i.default)("li",(0,i.default)(n,{user:r.attrs.user,updateState:r.attrs.updateState,deleteUser:r.attrs.deleteUser})):(0,i.default)("li",e)}return(0,i.default)("li",e)})]):null]))},oninit:function(e){e.state.accountStateTexte=window.texte.ACCOUNT_STATES[e.attrs.user.indicator.name]}}},{"./actions/copyCommunityLogin":16,"./actions/copyLoginCommunity":17,"./actions/deleteCommunityServer":18,"./actions/userTransactionsOverview":19,"./actions/verificationResend":20,mithril:1}],23:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var d=r(e("mithril")),f=r(e("../../model/AccountState")),p=r(e("../../lib/Gradido")),m=r(e("../../lib/Tooltip")),h=r(e("../../lib/dialog"));function r(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(t){var e=new f.default(t.attrs.user.indicator.name),n=t.attrs.user,r=e.getColor(),i=e.getTitle(),o=n.pubkeyhex;""!==o&&(o=n.pubkeyhex.substring(0,10)+"...");var a=n.created;a&&(a=(c=new Date(a)).toLocaleDateString()+"
    "+c.toLocaleTimeString());var s=!1;e.hasActions()&&(s=e.getRawColor(),s="primary");var l="down";t.attrs.open&&(l="up");var u=n.email,c=!1;return 25>>0,o=0;if(3===arguments.length)n=arguments[2];else{for(;o=n.clientWidth&&e>=n.clientHeight}),t=(0l[e]&&!r.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),y({},t,n)}};return s.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=E({},u,c[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,r=t.reference,i=e.placement.split("-")[0],o=Math.floor,a=-1!==["top","bottom"].indexOf(i),t=a?"right":"bottom",i=a?"left":"top",a=a?"width":"height";return n[t]o(r[t])&&(e.offsets.popper[i]=o(r[t])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){if(!z(e.instance.modifiers,"arrow","keepTogether"))return e;var n=t.element;if("string"==typeof n){if(!(n=e.instance.popper.querySelector(n)))return e}else if(!e.instance.popper.contains(n))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],i=e.offsets,o=i.popper,a=i.reference,s=-1!==["left","right"].indexOf(r),l=s?"height":"width",u=s?"Top":"Left",c=u.toLowerCase(),t=s?"left":"top",i=s?"bottom":"right",r=k(n)[l];a[i]-ro[i]&&(e.offsets.popper[c]+=a[c]+r-o[i]),e.offsets.popper=_(e.offsets.popper);s=a[c]+a[l]/2-r/2,i=d(e.instance.popper),a=parseFloat(i["margin"+u]),u=parseFloat(i["border"+u+"Width"]),u=s-e.offsets.popper[c]-a-u,u=Math.max(Math.min(o[l]-r,u),0);return e.arrowElement=n,e.offsets.arrow=(y(n={},c,Math.round(u)),y(n,t,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(u,c){if(F(u.instance.modifiers,"inner"))return u;if(u.flipped&&u.placement===u.originalPlacement)return u;var d=C(u.instance.popper,u.instance.reference,c.padding,c.boundariesElement,u.positionFixed),f=u.placement.split("-")[0],p=M(f),m=u.placement.split("-")[1]||"",h=[];switch(c.behavior){case K:h=[f,p];break;case G:h=Y(f);break;case W:h=Y(f,!0);break;default:h=c.behavior}return h.forEach(function(e,t){if(f!==e||h.length===t+1)return u;f=u.placement.split("-")[0],p=M(f);var n=u.offsets.popper,r=u.offsets.reference,i=Math.floor,o="left"===f&&i(n.right)>i(r.left)||"right"===f&&i(n.left)i(r.top)||"bottom"===f&&i(n.top)i(d.right),l=i(n.top)i(d.bottom),r="left"===f&&a||"right"===f&&s||"top"===f&&l||"bottom"===f&&e,n=-1!==["top","bottom"].indexOf(f),i=!!c.flipVariations&&(n&&"start"===m&&a||n&&"end"===m&&s||!n&&"start"===m&&l||!n&&"end"===m&&e),l=!!c.flipVariationsByContent&&(n&&"start"===m&&s||n&&"end"===m&&a||!n&&"start"===m&&e||!n&&"end"===m&&l),l=i||l;(o||r||l)&&(u.flipped=!0,(o||r)&&(f=h[t+1]),l&&(m="end"===m?"start":"start"===m?"end":m),u.placement=f+(m?"-"+m:""),u.offsets.popper=E({},u.offsets.popper,D(u.instance.popper,u.offsets.reference,u.placement)),u=L(u.instance.modifiers,u,"flip"))}),u},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],r=e.offsets,i=r.popper,o=r.reference,a=-1!==["left","right"].indexOf(n),r=-1===["top","left"].indexOf(n);return i[a?"left":"top"]=o[n]-(r?i[a?"width":"height"]:0),e.placement=M(t),e.offsets.popper=_(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!z(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=I(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.right or , not
    ","or ."].join(" ")),Ee("showOnInit"===t,"The `showOnInit` prop was renamed to `showOnCreate` in v5."),Ee("arrowType"===t,["The `arrowType` prop was removed in v5 in favor of overloading the `arrow`","prop.","\n\n",'"round" string was replaced with importing the string from the package.',"\n\n","* import {roundArrow} from 'tippy.js'; (ESM version)\n","* const {roundArrow} = tippy; (IIFE CDN version)","\n\n",'Before: {arrow: true, arrowType: "round"}\n',"After: {arrow: roundArrow}`"].join(" ")),Ee("touchHold"===t,["The `touchHold` prop was removed in v5 in favor of overloading the `touch`","prop.","\n\n","Before: {touchHold: true}\n",'After: {touch: "hold"}'].join(" ")),Ee("size"===t,["The `size` prop was removed in v5. Instead, use a theme that specifies","CSS padding and font-size properties."].join(" ")),Ee("theme"===t&&"google"===e,'The included theme "google" was renamed to "material" in v5.'),Ee(n,["Specifying placement in `popperOptions` is not supported. Use the base-level","`placement` prop instead.","\n\n",'Before: {popperOptions: {placement: "bottom"}}\n','After: {placement: "bottom"}'].join(" ")),Ee(r,["`"+t+"`","is not a valid prop. You may have spelled it incorrectly, or if it's a","plugin, forgot to pass it in an array as props.plugins.","\n\n","In v5, the following props were turned into plugins:","\n\n","* animateFill\n","* followCursor\n","* sticky","\n\n","All props: https://atomiks.github.io/tippyjs/all-props/\n","Plugins: https://atomiks.github.io/tippyjs/plugins/"].join(" "))})}var Ce={passive:!0},d="tippy-iOS",Ne="tippy-popper",Ae="tippy-tooltip",ke="tippy-content",f="tippy-arrow",p="tippy-svg-arrow",Me="."+Ne,m="."+Ae,h="."+ke,v="."+f,g="."+p,De={isTouch:!1},b=0;function y(){De.isTouch||(De.isTouch=!0,window.performance&&document.addEventListener("mousemove",w))}function w(){var e=performance.now();e-b<20&&(De.isTouch=!1,document.removeEventListener("mousemove",w)),b=e}function E(){var e,t=document.activeElement;i(t)&&(e=t._tippy,t.blur&&!e.state.isVisible&&t.blur())}var e="undefined"!=typeof window&&"undefined"!=typeof document,T=e?navigator.userAgent:"",Ie=/MSIE |Trident\//.test(T),_=e&&/iPhone|iPad|iPod/.test(navigator.platform);function Le(e){e=e&&_&&De.isTouch;document.body.classList[e?"add":"remove"](d)}function Fe(e){return e.split("-")[0]}function Pe(e){e.setAttribute("data-inertia","")}function Re(e){e.setAttribute("data-interactive","")}function je(e,t){l(t.content)?(n(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(e[t.allowHTML?"innerHTML":"textContent"]=t.content)}function Be(e){return{tooltip:e.querySelector(m),content:e.querySelector(h),arrow:e.querySelector(v)||e.querySelector(g)}}function Ue(e){var t=oe();return!0===e?t.className=f:(t.className=p,l(e)?t.appendChild(e):n(t,e)),t}function ze(e,t,n){var r=Be(e),i=r.tooltip,o=r.content,r=r.arrow;e.style.zIndex=""+n.zIndex,i.setAttribute("data-animation",n.animation),i.style.maxWidth="number"==typeof(e=n.maxWidth)?e+"px":e,n.role?i.setAttribute("role",n.role):i.removeAttribute("role"),t.content!==n.content&&je(o,n),!t.arrow&&n.arrow?(i.appendChild(Ue(n.arrow)),i.setAttribute("data-arrow","")):t.arrow&&!n.arrow?(i.removeChild(r),i.removeAttribute("data-arrow")):t.arrow!==n.arrow&&(i.removeChild(r),i.appendChild(Ue(n.arrow))),!t.interactive&&n.interactive?Re(i):t.interactive&&!n.interactive&&i.removeAttribute("data-interactive"),!t.inertia&&n.inertia?Pe(i):t.inertia&&!n.inertia&&i.removeAttribute("data-inertia"),t.theme!==n.theme&&(He(i,"remove",t.theme),He(i,"add",n.theme))}function Ve(t,n,r){["transitionend","webkitTransitionEnd"].forEach(function(e){t[n+"EventListener"](e,r)})}function He(t,n,e){pe(e).forEach(function(e){t.classList[n](e+"-theme")})}var Ye=1,Ke=[],Ge=[];function x(l,e){var i,t,n,r=Se(l,Z({},Te,{},xe(e)));if(!r.multiple&&l._tippy)return null;var o,a,s,u,c,d=!1,f=!1,p=!1,m=0,h=[],v=le(H,r.interactiveDebounce),g=ve(r.triggerTarget||l),b=Ye++,y=function(e,t){var n=oe();n.className=Ne,n.style.position="absolute",n.style.top="0",n.style.left="0";var r=oe();return r.className=Ae,r.id="tippy-"+e,r.setAttribute("data-state","hidden"),r.setAttribute("tabindex","-1"),He(r,"add",t.theme),(e=oe()).className=ke,e.setAttribute("data-state","hidden"),t.interactive&&Re(r),t.arrow&&(r.setAttribute("data-arrow",""),r.appendChild(Ue(t.arrow))),t.inertia&&Pe(r),je(e,t),r.appendChild(e),n.appendChild(r),ze(n,t,t),n}(b,r),w=Be(y),E=(c=r.plugins).filter(function(e,t){return c.indexOf(e)===t}),T=w.tooltip,e=w.content,_=[T,e],x={id:b,reference:l,popper:y,popperChildren:w,popperInstance:null,props:r,state:{currentPlacement:null,isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:E,clearDelayTimeouts:function(){clearTimeout(i),clearTimeout(t),cancelAnimationFrame(n)},setProps:function(n){var t,e;"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("setProps")),x.state.isDestroyed||("production"!==We.env.NODE_ENV&&(Oe(n,E),Ee(!!n.plugins&&(n.plugins.length!==E.length||E.some(function(e,t){return!n.plugins||!n.plugins[t]||e!==n.plugins[t]})),"Cannot update plugins")),M("onBeforeUpdate",[x,n]),z(),t=x.props,(e=Se(l,Z({},x.props,{},n,{ignoreAttributes:!0}))).ignoreAttributes=me(n.ignoreAttributes,t.ignoreAttributes),x.props=e,U(),t.interactiveDebounce!==e.interactiveDebounce&&(L(),v=le(H,e.interactiveDebounce)),ze(y,t,e),x.popperChildren=Be(y),t.triggerTarget&&!e.triggerTarget?he(t.triggerTarget).forEach(function(e){e.removeAttribute("aria-expanded")}):e.triggerTarget&&l.removeAttribute("aria-expanded"),I(),x.popperInstance&&(_e.some(function(e){return Q(n,e)&&n[e]!==t[e]})?(e=x.popperInstance.reference,x.popperInstance.destroy(),W(),x.popperInstance.reference=e,x.state.isVisible&&x.popperInstance.enableEventListeners()):x.popperInstance.update()),M("onAfterUpdate",[x,n]))},setContent:function(e){x.setProps({content:e})},show:function(e){void 0===e&&(e=ee(x.props.duration,0,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("show"));var t=x.state.isVisible,n=x.state.isDestroyed,r=!x.state.isEnabled,i=De.isTouch&&!x.props.touch;t||n||r||i||A().hasAttribute("disabled")||(x.popperInstance||W(),M("onShow",[x],!1),!1!==x.props.onShow(x)&&(P(),y.style.visibility="visible",x.state.isVisible=!0,x.state.isMounted||ae(_.concat(y),0),a=function(){x.state.isVisible&&(ae([y],x.props.updateDuration),ae(_,e),se(_,"visible"),D(),I(),ge(Ge,x),Le(!0),x.state.isMounted=!0,M("onMount",[x]),j(e,function(){x.state.isShown=!0,M("onShown",[x])}))},function(){m=0;var e=x.props.appendTo,t=A(),n=x.props.interactive&&e===Te.appendTo||"parent"===e?t.parentNode:re(e,[t]);n.contains(y)||n.appendChild(y),"production"!==We.env.NODE_ENV&&Ee(x.props.interactive&&e===Te.appendTo&&t.nextElementSibling!==y,["Interactive tippy element may not be accessible via keyboard navigation","because it is not directly after the reference element in the DOM source","order.","\n\n","Using a wrapper
    or tag around the reference element solves","this by creating a new parentNode context.","\n\n","Specifying `appendTo: document.body` silences this warning, but it","assumes you are using a focus management solution to handle keyboard","navigation.","\n\n","See: https://atomiks.github.io/tippyjs/accessibility/#interactivity"].join(" ")),ie(x.popperInstance.modifiers,"flip","enabled",x.props.flip),x.popperInstance.enableEventListeners(),x.popperInstance.update()}()))},hide:function(e){void 0===e&&(e=ee(x.props.duration,1,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("hide"));var t=!x.state.isVisible&&!d,n=x.state.isDestroyed,r=!x.state.isEnabled&&!d;t||n||r||(M("onHide",[x],!1),!1===x.props.onHide(x)&&!d||(R(),y.style.visibility="hidden",x.state.isVisible=!1,x.state.isShown=!1,ae(_,e),se(_,"hidden"),D(),I(),j(e,function(){!x.state.isVisible&&y.parentNode&&y.parentNode.contains(y)&&(x.popperInstance.disableEventListeners(),x.popperInstance.options.placement=x.props.placement,y.parentNode.removeChild(y),0===(Ge=Ge.filter(function(e){return e!==x})).length&&Le(!1),x.state.isMounted=!1,M("onHidden",[x]))})))},enable:function(){x.state.isEnabled=!0},disable:function(){x.hide(),x.state.isEnabled=!1},destroy:function(){"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("destroy")),x.state.isDestroyed||(d=!0,x.clearDelayTimeouts(),x.hide(0),z(),delete l._tippy,x.popperInstance&&x.popperInstance.destroy(),d=!1,x.state.isDestroyed=!0,M("onDestroy",[x]))}};l._tippy=x,y._tippy=x;var S=E.map(function(e){return e.fn(x)}),O=l.hasAttribute("aria-expanded");return U(),I(),r.lazy||W(),M("onCreate",[x]),r.showOnCreate&&$(),y.addEventListener("mouseenter",function(){x.props.interactive&&x.state.isVisible&&x.clearDelayTimeouts()}),y.addEventListener("mouseleave",function(e){x.props.interactive&&fe(x.props.trigger,"mouseenter")&&(v(e),g.addEventListener("mousemove",v))}),x;function C(){var e=x.props.touch;return Array.isArray(e)?e:[e,0]}function N(){return"hold"===C()[0]}function A(){return u||l}function k(e){return x.state.isMounted&&!x.state.isVisible||De.isTouch||o&&"focus"===o.type?0:ee(x.props.delay,e?0:1,Te.delay)}function M(t,n,e){void 0===e&&(e=!0),S.forEach(function(e){Q(e,t)&&e[t].apply(e,n)}),e&&(e=x.props)[t].apply(e,n)}function D(){var n,r,e=x.props.aria;e&&(n="aria-"+e,r=T.id,he(x.props.triggerTarget||l).forEach(function(e){var t=e.getAttribute(n);x.state.isVisible?e.setAttribute(n,t?t+" "+r:r):(t=t&&t.replace(r,"").trim())?e.setAttribute(n,t):e.removeAttribute(n)}))}function I(){O||he(x.props.triggerTarget||l).forEach(function(e){x.props.interactive?e.setAttribute("aria-expanded",x.state.isVisible&&e===A()?"true":"false"):e.removeAttribute("aria-expanded")})}function L(){g.body.removeEventListener("mouseleave",X),g.removeEventListener("mousemove",v),Ke=Ke.filter(function(e){return e!==v})}function F(e){if(!x.props.interactive||!y.contains(e.target)){if(A().contains(e.target)){if(De.isTouch)return;if(x.state.isVisible&&fe(x.props.trigger,"click"))return}!0===x.props.hideOnClick&&(f=!1,x.clearDelayTimeouts(),x.hide(),p=!0,setTimeout(function(){p=!1}),x.state.isMounted||R())}}function P(){g.addEventListener("mousedown",F,!0)}function R(){g.removeEventListener("mousedown",F,!0)}function j(e,t){function n(e){e.target===T&&(Ve(T,"remove",n),t())}if(0===e)return t();Ve(T,"remove",s),Ve(T,"add",n),s=n}function B(t,n,r){void 0===r&&(r=!1),he(x.props.triggerTarget||l).forEach(function(e){e.addEventListener(t,n,r),h.push({node:e,eventType:t,handler:n,options:r})})}function U(){N()&&(B("touchstart",V,Ce),B("touchend",Y,Ce)),pe(x.props.trigger).forEach(function(e){if("manual"!==e)switch(B(e,V),e){case"mouseenter":B("mouseleave",Y);break;case"focus":B(Ie?"focusout":"blur",K);break;case"focusin":B("focusout",K)}})}function z(){h.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),h=[]}function V(t){var e,n,r=!1;!x.state.isEnabled||G(t)||p||(u=(o=t).currentTarget,I(),!x.state.isVisible&&ne(t)&&Ke.forEach(function(e){return e(t)}),"click"!==t.type||fe(x.props.trigger,"mouseenter")&&!f||!1===x.props.hideOnClick||!x.state.isVisible?(e=(n=C())[0],n=n[1],De.isTouch&&"hold"===e&&n?i=setTimeout(function(){$(t)},n):$(t)):r=!0,"click"===t.type&&(f=!r),r&&X(t))}function H(e){var a,s,t=de(e.target,function(e){return e===l||e===y});"mousemove"===e.type&&t||(t=ce(y.querySelectorAll(Me)).concat(y).map(function(e){var t=e._tippy,n=t.popperChildren.tooltip,t=t.props.interactiveBorder;return{popperRect:e.getBoundingClientRect(),tooltipRect:n.getBoundingClientRect(),interactiveBorder:t}}),a=e.clientX,s=e.clientY,t.every(function(e){var t=e.popperRect,n=e.tooltipRect,r=e.interactiveBorder,i=Math.min(t.top,n.top),o=Math.max(t.right,n.right),e=Math.max(t.bottom,n.bottom),n=Math.min(t.left,n.left);return r element.cloneNode(true)"].join(" ")));t=t.reduce(function(e,t){t=t&&x(t,a);return t&&e.push(t),e},[]);return l(e)?t[0]:t}S.version="5.2.1",S.defaultProps=Te,S.setDefaultProps=o,S.currentInput=De,C.BACKDROP_CLASS="tippy-backdrop",C.ROUND_ARROW='',C._extends=Z,C.arrayFrom=ce,C.closestCallback=de,C.createTippyWithPlugins=function(r){"production"!==We.env.NODE_ENV&&Ee(!0,["createTippyWithPlugins([...]) has been deprecated.","\n\n","Use tippy.setDefaultProps({plugins: [...]}) instead."].join(" "));function e(e,t,n){return void 0===n&&(n=[]),n=(t=void 0===t?{}:t).plugins||n,S(e,Z({},t,{plugins:[].concat(r,n)}))}return e.version="5.2.1",e.defaultProps=Te,e.setDefaultProps=o,e.currentInput=De,e},C.currentInput=De,C.defaultProps=Te,C.div=oe,C.errorWhen=u,C.getBasePlacement=Fe,C.getOwnerDocument=ve,C.hideAll=function(e){var e=void 0===e?{}:e,n=e.exclude,r=e.duration;Ge.forEach(function(e){var t=!1;(t=n?i(n)?e.reference===n:e.popper===n.popper:t)||e.hide(r)})},C.includes=fe,C.isBrowser=e,C.isMouseEvent=ne,C.normalizeToArray=he,C.removeProperties=a,C.setVisibilityState=se,C.tippy=S,C.useIfDefined=me,C.warnWhen=Ee}.call(this)}.call(this,O("_process"))},{_process:4,"popper.js":3}],7:[function(i,e,o){!function(f){!function(){"use strict";Object.defineProperty(o,"__esModule",{value:!0});var y=i("./tippy.chunk.cjs.js");i("popper.js");var d={mouseover:"mouseenter",focusin:"focus",click:"click"},e={name:"animateFill",defaultValue:!1,fn:function(e){var t=e.popperChildren,n=t.tooltip,r=t.content,i=e.props.animateFill?((t=y.div()).className=y.BACKDROP_CLASS,y.setVisibilityState([t],"hidden"),t):null;function o(){e.popperChildren.backdrop=i}return{onCreate:function(){i&&(o(),n.insertBefore(i,n.firstElementChild),n.setAttribute("data-animatefill",""),n.style.overflow="hidden",e.setProps({animation:"shift-away",arrow:!1}))},onMount:function(){var e,t;i&&(e=n.style.transitionDuration,t=Number(e.replace("ms","")),r.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y.setVisibilityState([i],"visible"),"production"!==f.env.NODE_ENV&&(y.warnWhen("absolute"!==getComputedStyle(i).position,"The `tippy.js/dist/backdrop.css` stylesheet has not been\n imported!\n \n The `animateFill` plugin requires this stylesheet to work."),y.warnWhen("none"===getComputedStyle(n).transform,"The `tippy.js/animations/shift-away.css` stylesheet has not\n been imported!\n \n The `animateFill` plugin requires this stylesheet to work.")))},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y.setVisibilityState([i],"hidden")},onAfterUpdate:function(){o()}}}},t={name:"followCursor",defaultValue:!1,fn:function(c){var d,f=c.reference,p=c.popper,m=null,e=y.getOwnerDocument(c.props.triggerTarget||f),n=null,r=!1,i=c.props;function t(){return"manual"===c.props.trigger.trim()}function o(){var e=!!t()||null!==n&&!(0===n.clientX&&0===n.clientY);return c.props.followCursor&&e}function h(){return y.currentInput.isTouch||"initial"===c.props.followCursor&&c.state.isVisible}function a(){c.popperInstance&&m&&(c.popperInstance.reference=m)}function s(){var e,t;!o()&&c.props.placement===i.placement||(t=(e=i.placement).split("-")[1],r=!0,c.setProps({placement:o()&&t?e.replace(t,"start"===t?"end":"start"):e}),r=!1)}function l(){c.popperInstance&&o()&&h()&&c.popperInstance.disableEventListeners()}function u(){o()?e.addEventListener("mousemove",b):a()}function v(){o()&&b(d)}function g(){e.removeEventListener("mousemove",b)}function b(e){var t,n,r,i,o,a,s=d=e,l=s.clientX,u=s.clientY;c.popperInstance&&c.state.currentPlacement&&(s=y.closestCallback(e.target,function(e){return e===f}),e=c.props.followCursor,t="horizontal"===e,n="vertical"===e,r=y.includes(["top","bottom"],y.getBasePlacement(c.state.currentPlacement)),e={size:e=r?p.offsetWidth:p.offsetHeight,x:r?e:0,y:r?0:e},i=e.size,o=e.x,a=e.y,!s&&c.props.interactive||(null===m&&(m=c.popperInstance.reference),c.popperInstance.reference={referenceNode:f,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){var e=f.getBoundingClientRect();return{width:r?i:0,height:r?0:i,top:(t?e.top:u)-a,bottom:(t?e.bottom:u)+a,left:(n?e.left:l)-o,right:(n?e.right:l)+o}}},c.popperInstance.update()),h()&&g())}return{onAfterUpdate:function(e,t){var n;r||(n=t,Object.keys(n).forEach(function(e){i[e]=y.useIfDefined(n[e],i[e])}),t.placement&&s()),t.placement&&l(),requestAnimationFrame(v)},onMount:function(){v(),l()},onShow:function(){t()&&(d=n={clientX:0,clientY:0},s(),u())},onTrigger:function(e,t){n||(y.isMouseEvent(t)&&(n={clientX:t.clientX,clientY:t.clientY},d=t),s(),u())},onUntrigger:function(){c.state.isVisible||(g(),n=null)},onHidden:function(){g(),a(),n=null}}}},n={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t=e.reference;function n(){return e.props.inlinePositioning}return{onHidden:function(){n()&&(e.popperInstance.reference=t)},onShow:function(){n()&&(e.popperInstance.reference={referenceNode:t,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){return function(t,e,n){if(n.length<2||null===t)return e;switch(t){case"top":case"bottom":var r=n[0],i=n[n.length-1],o="top"===t,a=r.top,s=i.bottom,l=(o?r:i).left,i=(o?r:i).right;return{top:a,bottom:s,left:l,right:i,width:i-l,height:s-a};case"left":case"right":var u=Math.min.apply(Math,n.map(function(e){return e.left})),c=Math.max.apply(Math,n.map(function(e){return e.right})),s=n.filter(function(e){return"left"===t?e.left===u:e.right===c}),a=s[0].top,s=s[s.length-1].bottom;return{top:a,bottom:s,left:u,right:c,width:c-u,height:s-a};default:return e}}(e.state.currentPlacement&&y.getBasePlacement(e.state.currentPlacement),t.getBoundingClientRect(),y.arrayFrom(t.getClientRects()))}})}}}},r={name:"sticky",defaultValue:!1,fn:function(n){var r=n.reference,i=n.popper;function o(e){return!0===n.props.sticky||n.props.sticky===e}var a=null,s=null;function l(){var e=o("reference")?(n.popperInstance?n.popperInstance.reference:r).getBoundingClientRect():null,t=o("popper")?i.getBoundingClientRect():null;(e&&u(a,e)||t&&u(s,t))&&n.popperInstance.update(),a=e,s=t,n.state.isMounted&&requestAnimationFrame(l)}return{onMount:function(){n.props.sticky&&l()}}}};function u(e,t){return!e||!t||e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left}o.createTippyWithPlugins=y.createTippyWithPlugins,o.default=y.tippy,o.hideAll=y.hideAll,o.roundArrow=y.ROUND_ARROW,o.animateFill=e,o.createSingleton=function(o,e,t){void 0===e&&(e={}),void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!Array.isArray(o),["The first argument passed to createSingleton() must be an array of tippy","instances. The passed value was",String(o)].join(" ")),t=e.plugins||t,o.forEach(function(e){e.disable()});var a,s,l=y._extends({},y.defaultProps,{},e).aria,n=!1,u=o.map(function(e){return e.reference}),r={fn:function(r){function i(e){var t;a&&(t="aria-"+a,e&&!r.props.interactive?s.setAttribute(t,r.popperChildren.tooltip.id):s.removeAttribute(t))}return{onAfterUpdate:function(e,t){t=t.aria;void 0!==t&&t!==l&&(n?(n=!0,r.setProps({aria:null}),n=!1):l=t)},onDestroy:function(){o.forEach(function(e){e.enable()})},onMount:function(){i(!0)},onUntrigger:function(){i(!1)},onTrigger:function(e,t){var n=t.currentTarget,t=u.indexOf(n);n!==s&&(s=n,a=l,r.state.isVisible&&i(!0),r.popperInstance.reference=n,r.setContent(o[t].props.content))}}}};return y.tippy(y.div(),y._extends({},e,{plugins:[r].concat(t),aria:null,triggerTarget:u}))},o.delegate=function(e,r,t){void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!(r&&r.target),["You must specity a `target` prop indicating a CSS selector string matching","the target elements that should receive a tippy."].join(" ")),t=r.plugins||t;var i=[],o=[],a=r.target,n=y.removeProperties(r,["target"]),s=y._extends({},n,{plugins:t,trigger:"manual"}),l=y._extends({},n,{plugins:t,showOnCreate:!0}),s=y.tippy(e,s);function u(e){var t,n;!e.target||(n=e.target.closest(a))&&(t=n.getAttribute("data-tippy-trigger")||r.trigger||y.defaultProps.trigger,!y.includes(t,d[e.type])||(n=y.tippy(n,l))&&(o=o.concat(n)))}function c(e,t,n,r){e.addEventListener(t,n,r=void 0===r?!1:r),i.push({node:e,eventType:t,handler:n,options:r})}return y.normalizeToArray(s).forEach(function(e){var t=e.destroy;e.destroy=function(e){(e=void 0===e?!0:e)&&o.forEach(function(e){e.destroy()}),o=[],i.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),i=[],t()},c(e=e.reference,"mouseover",u),c(e,"focusin",u),c(e,"click",u)}),s},o.followCursor=t,o.inlinePositioning=n,o.sticky=r}.call(this)}.call(this,i("_process"))},{"./tippy.chunk.cjs.js":6,_process:4,"popper.js":3}],8:[function(e,t,n){"use strict";var r=a(e("mithril")),i=a(e("./view")),o=a(e("./texte/de"));function a(e){return e&&e.__esModule?e:{default:e}}document,window,domIsReady(function(){window.texte=o.default,r.default.mount(window.document.getElementById("gradido-mithril-user-search"),i.default)})},{"./texte/de":13,"./view":14,mithril:1}],9:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=o(e("mithril")),i=o(e("numeral"));function o(e){return e&&e.__esModule?e:{default:e}}var a=!1;n.default={view:function(e){return(0,r.default)("span",(e=e.attrs.centAmount/1e4,i.default.locale("de"),(0,i.default)(e).format("0,0.00")+" GDD"))},oninit:function(e){a||(i.default.register("locale","de",{delimiters:{thousands:".",decimal:","},currency:{symbol:"€"}}),i.default.locale("de"),a=!0)}}},{mithril:1,numeral:2}],10:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e){return{oncreate:function(e){(0,r.default)(e.dom,{content:e.attrs.accountState.getTooltipText(),placement:"right-start",theme:"rippleUI-theme",arrow:!1})},onremove:function(){},view:function(){return e}}},i(e("mithril"));var r=i(e("tippy.js"));function i(e){return e&&e.__esModule?e:{default:e}}},{mithril:1,"tippy.js":7}],11:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};n.default={view:function(e){return(0,r.default)("div.modal.visible-modal",{tabindex:"-1",role:"dialog"},(0,r.default)("div.modal-dialog",{role:"document"},(0,r.default)("div.modal-content",[(0,r.default)("p.grid-header",e.attrs.title),(0,r.default)("div.modal-body",e.attrs.body),(0,r.default)("div.modal-footer",[(0,r.default)("button.btn.btn-primary",{type:"button",disabled:e.attrs.btnDisabled,"data-dismiss":"modal",onclick:e.attrs.dismiss},"Ok")])])))},oninit:function(e){}}},{mithril:1}],12:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};function i(e,t){for(var n=0;n';if(t+='
    '+e.description.title+"
    ",null!=e.description.lines){for(var n in t+="
      ",e.description.lines){var r=e.description.lines[n],i=window.texte.ACCOUNT_STATES.LINES[n],n="mdi ";if("+"===r)n+=" mdi-check color-primary";else if("-"===r)n+=" mdi-close color-danger";else{if("/"!==r)continue;n+=" mdi-alert-circle-outline color-warning"}t+="
    • ",t+='',t+=" ",t+=""+i.title+": "+i[r]+"",t+="
    • "}t+="
    "}return t+"
    "}}]),e=o;function o(e){!function(e){if(!(e instanceof o))throw new TypeError("Cannot call a class as a function")}(this),this.stateName=e,this.texte=window.texte.ACCOUNT_STATES[e]}n.default=e},{mithril:1}],13:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0,n.default={ACCOUNT_STATES:{LINES:[{title:"Login-Server","+":"Das Benutzer-Konto liegt auf dem Login-Server!","-":"Das Benutzer-Konto existiert nicht auf dem Login-Server!"},{title:"Konto Aktivierung","+":"Der Benutzer hat sein Konto aktiviert!","-":"Der Benutzer hat noch nicht auf den Link in seiner E-Mail geklickt!"},{title:"Schlüsselpaar","+":"Es wurde bereits ein Schlüsselpaar für den Benutzer erzeugt und ihm eine Passphrase gezeigt!","-":"Es wurden noch keine Schlüssel und Passphrase für den Benutzer erzeugt!"},{title:"Gemeinschafts-Server","+":"Das Benutzer-Konto wurde auf auf den Gemeinschafts-Server kopiert!","-":"Das Benutzer-Konto wurde noch nicht auf den Gemeinschafts-Server kopiert!"},{title:"Gradidos","+":"Es können Gradidos an den Benutzer geschickt werden!","-":"Es können noch keine Gradidos an den Benutzer geschickt werden!","/":"Es können theoretisch Gradidos an den Benutzer geschickt werden, aber es ist nicht sicher ob er sie verwenden kann!"}],"account created":{title:"Konto angelegt",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung.",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account not on login-server":{title:"Konto nicht auf Login-Server",color:"danger",description:{title:"Das Konto existiert nicht (mehr) auf dem Login-Server: ein Fehler oder es wurde nur teilweise gelöscht.",lines:["-","+","+","+","/"]},todo:{title:"Wenn der Benutzer seine Passphrase hat, kann er sein Konto auf dem Login-Server wiederherstellen, ansonsten bleibt eigentlich nur das Konto auf dem Gemeinschafts-Server zu löschen",lines:["Nachdem das Konto auf dem Login-Server kopiert wurde, bekommt der Benutzer automatisch eine E-Mail mit einem Link zur Kontoaktivierung zugeschickt.","{{copy-from-community-to-login-server}}","
    ","Es wird nur der Benutzer gelöscht, nicht seine Transaktionen. Zu diesen hat er wieder Zugriff wenn er sein Konto mit Hilfe seiner Passphrase wiederhergestellt hat.","{{user-transactions-overview}}","{{delete-from-community-server}}"]}},"email activated":{title:"Konto aktiviert",color:"primary",description:{title:"Das Konto wurde vom Benutzer aktiviert und Schlüssel erzeugt, aber noch nicht auf diesen Gemeinschafts-Server kopiert.",lines:["+","+","+","-","-"]},todo:{title:"Das Benutzer Konto müsste vom Login-Server auf den Gemeinschafts-Server kopiert werden.",lines:["{{copy-from-login-to-community-server}}"]}},"account copied to community":{title:"Konto auf Gemeinschafts-Server",color:"success",description:{title:"Das Konto ist vollständig aktiviert und es können Gradidos überwiesen werden.",lines:["+","+","+","+","+"]}},"email not activated":{title:"Konto nicht aktiviert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung. Es wurde aber noch nicht aktiviert!",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account multiple times on login-server":{title:"Konto mehrfach vorhanden",color:"danger",description:{title:"Das Konto ist auf dem Login-Server mehrfach vorhanden, das sollte nicht sein, auf dem Login-Server muss ein Fehler passiert sein!!"},todo:{title:"Du solltest mit dem Entwickler in Kontakt treten.",lines:["{{mailto-developer}}"]}},"account not on community server":{title:"Konto nicht auf Gemeinschafts-Server",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber existiert noch nicht auf dem Gemeinschafts-Server!",lines:["+",null,"-","-","-"]},todo:{title:"Das sollte eigentlich nicht passieren, du solltest mit dem Entwickler sprechen.",lines:["{{mailto-developer}}"]}},"no keys":{title:"Keine Schlüssel generiert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber es wurden noch keine Schlüssel generiert!",lines:["+","+","-","-","-"]},todo:{title:"Der Benutzer muss sich mit seinen Daten einloggen, dann müsste er automatisch durch die Schlüsselgenerierung/Konto-Wiederherstellung geführt werden",lines:["{{mailto-user-login}}","Wenn der Benutzer sein Passwort vergessen hat, kann er sich auch eine Passwort Reset E-Mail zuschicken lassen.","{{reset-password}}","{{mailto-reset-password}}"]}}},NO_USER_FOUND:"Keine Benutzer gefunden",USER_FOUND:"Benutzer gefunden",NAME:"Name",EMAIL:"E-Mail",BALANCE:"Kontostand",PUBLIC_KEY:"Öffentlicher
    Schlüssel",CREATED:"Erstellt",COPY_FROM_LOGIN_TO_COMMUNITY:"Vom Login-Server zum Gemeinschafts-Server kopieren",COPY_FROM_LOGIN_TO_COMMUNITY_SUCCESS:"Kontodaten wurden erfolgreich vom Login-Server zu diesem Gemeinschafts-Server kopiert!",COPY_FROM_COMMUNITY_TO_LOGIN:"Vom Gemeinschafts-Server zum Login-Server kopieren",COPY_FROM_COMMUNITY_TO_LOGIN_SUCCESS:"Kontodaten wurden erfolgreich vom Gemeinschafts-Server zum Login-Server kopiert!",DELETE_FROM_COMMUNITY:"Benutzer Konto vom Gemeinschafts-Server löschen",DELETE_FROM_COMMUNITY_SUCCESS:"Benutzer Konto vom Gemeinschafts-Server erfolgreich gelöscht",VERIFICATION_EMAIL_RESEND:"Verification Email erneut zusenden",VERIFICATION_EMAIL_RESEND_SUCCESS:"Verification Email wird erneut zugestellt",COPY_FAILED:"Fehler beim Kopieren",DELETE_FAILED:"Fehler beim löschen",RESEND_FAILED:"Senden fehlgeschlagen",AJAX_CRITICAL:"Kritischer Fehler beim Ajax-Request",COPY_IN_PROGRESS:"Wird kopiert",DELETE_IN_PROGRESS:"Wird gelöscht",RESEND_IN_PROGRESS:"Wird gesendet",RECEIVE_TRANSACTIONS_COUNT:"Erhaltene Transaktionen: ",SENDED_TRANSACTIONS_COUNT:"gesendete Transaktionen: ",CREATION_TRANSACTIONS_COUNT:"erhaltende Schöpfungs-Transaktionen: "}},{}],14:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=a(e("mithril")),i=a(e("./userTable")),o=a(e("../model/AccountState"));function a(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(e){return void 0===g_users?(0,r.default)(""):0"===e)return i.default.trust("

    ");if(t){var n=function(){switch(t[1]){case"copy-from-login-to-community-server":return o.default;case"user-transactions-overview":return s.default;case"copy-from-community-to-login-server":return l.default;case"verification-resend":return u.default;case"mailto-verification-resend":return a.default}return null}();return n?(0,i.default)("li",(0,i.default)(n,{user:r.attrs.user,updateState:r.attrs.updateState,deleteUser:r.attrs.deleteUser})):(0,i.default)("li",e)}return(0,i.default)("li",e)})]):null]))},oninit:function(e){e.state.accountStateTexte=window.texte.ACCOUNT_STATES[e.attrs.user.indicator.name]}}},{"./actions/copyCommunityLogin":16,"./actions/copyLoginCommunity":17,"./actions/deleteCommunityServer":18,"./actions/mailtoVerificationResend":19,"./actions/userTransactionsOverview":20,"./actions/verificationResend":21,mithril:1}],24:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var d=r(e("mithril")),f=r(e("../../model/AccountState")),p=r(e("../../lib/Gradido")),m=r(e("../../lib/Tooltip")),h=r(e("../../lib/dialog"));function r(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(t){var e=new f.default(t.attrs.user.indicator.name),n=t.attrs.user,r=e.getColor(),i=e.getTitle(),o=n.pubkeyhex;""!==o&&(o=n.pubkeyhex.substring(0,10)+"...");var a=n.created;a&&(a=(c=new Date(a)).toLocaleDateString()+"
    "+c.toLocaleTimeString());var s=!1;e.hasActions()&&(s=e.getRawColor(),s="primary");var l="down";t.attrs.open&&(l="up");var u=n.email,c=!1;return 25 Date: Fri, 26 Mar 2021 10:36:05 +0000 Subject: [PATCH 12/44] remove make string hash function which is not working in php --- .../TransactionCreationsController.php | 15 ++++++++++++++- .../Model/Transactions/TransactionCreation.php | 18 ------------------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/community_server/src/Controller/TransactionCreationsController.php b/community_server/src/Controller/TransactionCreationsController.php index af7b9a018..3530e3306 100644 --- a/community_server/src/Controller/TransactionCreationsController.php +++ b/community_server/src/Controller/TransactionCreationsController.php @@ -369,7 +369,20 @@ class TransactionCreationsController extends AppController $pendings[$id] = $localAmountCent; } $pubKeyHex = bin2hex(stream_get_contents($receiverUser->public_key)); - $identHash = TransactionCreation::DRMakeStringHash($receiverUser->email); + $requestAnswear = $this->JsonRequestClient->sendRequest(json_encode([ + 'session_id' => $session->read('session_id'), + 'email' => $receiverUser->email, + 'ask' => ['user.identHash'] + ]), '/getUserInfos'); + + $identHash = 0; + if('success' == $requestAnswear['state'] && 'success' == $requestAnswear['data']['state']) { + $identHash = $requestAnswear['data']['userData']['identHash']; + } else { + $this->Flash->error(__('Error by requesting LoginServer, please try again')); + } + + //$identHash = TransactionCreation::DRMakeStringHash($receiverUser->email); $localTargetDateFrozen = FrozenDate::now(); $localTargetDateFrozen = $localTargetDateFrozen ->year($localTargetDate['year']) diff --git a/community_server/src/Model/Transactions/TransactionCreation.php b/community_server/src/Model/Transactions/TransactionCreation.php index e8c6133cb..4939c5400 100644 --- a/community_server/src/Model/Transactions/TransactionCreation.php +++ b/community_server/src/Model/Transactions/TransactionCreation.php @@ -63,24 +63,6 @@ class TransactionCreation extends TransactionBase { return ['state' => 'success', 'transactionBody' => $transactionBody]; } - static protected function DRHashRotateLeft( $hash, $rotateBy ) - { - return ($hash<<$rotateBy)|($hash>>(32-$rotateBy)); - } - - static public function DRMakeStringHash($str) - { - $ret = 0; - - if( $str ) - { - for ($i=0; $i < strlen($str); $i++) - { - $ret = TransactionCreation::DRHashRotateLeft($ret, 7) + ord($str{$i}); - } - } - return $ret; - } public function getAmount() { return $this->protoTransactionCreation->getReceiverAmount()->getAmount(); From 6ea10e2572c6325d2bfb8053be6752c6dd40f50d Mon Sep 17 00:00:00 2001 From: Dario Rekowski on RockPI Date: Fri, 26 Mar 2021 10:49:00 +0000 Subject: [PATCH 13/44] remove calls to removed DRMakeStringHash function --- community_server/src/Controller/StateUsersController.php | 4 ++-- .../src/Controller/TransactionCreationsController.php | 8 ++++---- .../src/Model/Transactions/TransactionCreation.php | 2 +- .../src/Model/Transactions/TransactionTransfer.php | 6 ++++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/community_server/src/Controller/StateUsersController.php b/community_server/src/Controller/StateUsersController.php index 9f07fffdb..43c6664c0 100644 --- a/community_server/src/Controller/StateUsersController.php +++ b/community_server/src/Controller/StateUsersController.php @@ -69,14 +69,14 @@ class StateUsersController extends AppController $this->set(compact('stateUsers')); } - public function listIdentHashes() + /*public function listIdentHashes() { $stateUsers = $this->StateUsers->find('all')->toArray(); foreach ($stateUsers as $i => $user) { $stateUsers[$i]->identHash = TransactionCreation::DRMakeStringHash($user->email); } $this->set('stateUsers', $stateUsers); - } + }*/ public function search() { diff --git a/community_server/src/Controller/TransactionCreationsController.php b/community_server/src/Controller/TransactionCreationsController.php index 3530e3306..46a7a37f4 100644 --- a/community_server/src/Controller/TransactionCreationsController.php +++ b/community_server/src/Controller/TransactionCreationsController.php @@ -54,12 +54,12 @@ class TransactionCreationsController extends AppController ]; $transactionCreations = $this->paginate($this->TransactionCreations); $identHashes = []; - foreach ($transactionCreations as $creation) { + /*foreach ($transactionCreations as $creation) { $identHash = TransactionCreation::DRMakeStringHash($creation->state_user->email); $identHashes[$creation->state_user->id] = $identHash; - } + }*/ - $this->set(compact('transactionCreations', 'identHashes')); + //$this->set(compact('transactionCreations', 'identHashes')); } /** @@ -125,7 +125,7 @@ class TransactionCreationsController extends AppController if (count($receiverProposal) > $receiverIndex) { $pubKeyHex = $receiverProposal[$receiverIndex]['key']; - $identHash = TransactionCreation::DRMakeStringHash($receiverProposal[$receiverIndex]['email']); + //$identHash = TransactionCreation::DRMakeStringHash($receiverProposal[$receiverIndex]['email']); } $builderResult = TransactionCreation::build( $amountCent, diff --git a/community_server/src/Model/Transactions/TransactionCreation.php b/community_server/src/Model/Transactions/TransactionCreation.php index 4939c5400..72f42b3df 100644 --- a/community_server/src/Model/Transactions/TransactionCreation.php +++ b/community_server/src/Model/Transactions/TransactionCreation.php @@ -285,7 +285,7 @@ class TransactionCreation extends TransactionBase { // intval //$protoCreation->setIdentHash(intval($identHashBytes)); - $protoCreation->setIdentHash(self::DRMakeStringHash($stateUser->email)); + //$protoCreation->setIdentHash(self::DRMakeStringHash($stateUser->email)); return new TransactionCreation($protoCreation); } } diff --git a/community_server/src/Model/Transactions/TransactionTransfer.php b/community_server/src/Model/Transactions/TransactionTransfer.php index a509cf0f3..68203914d 100644 --- a/community_server/src/Model/Transactions/TransactionTransfer.php +++ b/community_server/src/Model/Transactions/TransactionTransfer.php @@ -75,7 +75,8 @@ class TransactionTransfer extends TransactionBase { foreach($sigPairs as $sigPair) { //echo 'sig Pair: '; var_dump($sigPair); echo "
    "; $pubkey = bin2hex($sigPair->getPubKey()); - $hash = TransactionCreation::DRMakeStringHash($pubkey); + //$hash = TransactionCreation::DRMakeStringHash($pubkey); + $hash = $pubkey; if(!isset($sigPubHexs[$hash])) { $sigPubHexs[$hash] = [$pubkey]; } else { @@ -100,7 +101,8 @@ class TransactionTransfer extends TransactionBase { return false; } // check if signature exist for sender - $hash = TransactionCreation::DRMakeStringHash($senderPublicHex); + //$hash = TransactionCreation::DRMakeStringHash($senderPublicHex); + $hash = $senderPublicHex; if(!isset($sigPubHexs[$hash]) || in_array($senderPublicHex, $sigPubHexs[$hash]) === FALSE) { $this->addError($functionName, 'missing signature for sender'); return false; From eda1b2ad7b780af991fb7a46548cad720f518432 Mon Sep 17 00:00:00 2001 From: Dario Rekowski on RockPI Date: Fri, 26 Mar 2021 11:04:21 +0000 Subject: [PATCH 14/44] fix bug in user search (wrong action) --- community_server/webroot/js/userSearch.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community_server/webroot/js/userSearch.min.js b/community_server/webroot/js/userSearch.min.js index d6c3f0872..9dd4f94f9 100644 --- a/community_server/webroot/js/userSearch.min.js +++ b/community_server/webroot/js/userSearch.min.js @@ -1 +1 @@ -!function r(i,o,a){function s(t,e){if(!o[t]){if(!i[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(l)return l(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=o[t]={exports:{}},i[t][0].call(n.exports,function(e){return s(i[t][1][e]||e)},n,n.exports,r,i,o,a)}return o[t].exports}for(var l="function"==typeof require&&require,e=0;e>>0,o=0;if(3===arguments.length)n=arguments[2];else{for(;o=n.clientWidth&&e>=n.clientHeight}),t=(0l[e]&&!r.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),y({},t,n)}};return s.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=E({},u,c[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,r=t.reference,i=e.placement.split("-")[0],o=Math.floor,a=-1!==["top","bottom"].indexOf(i),t=a?"right":"bottom",i=a?"left":"top",a=a?"width":"height";return n[t]o(r[t])&&(e.offsets.popper[i]=o(r[t])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){if(!z(e.instance.modifiers,"arrow","keepTogether"))return e;var n=t.element;if("string"==typeof n){if(!(n=e.instance.popper.querySelector(n)))return e}else if(!e.instance.popper.contains(n))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],i=e.offsets,o=i.popper,a=i.reference,s=-1!==["left","right"].indexOf(r),l=s?"height":"width",u=s?"Top":"Left",c=u.toLowerCase(),t=s?"left":"top",i=s?"bottom":"right",r=k(n)[l];a[i]-ro[i]&&(e.offsets.popper[c]+=a[c]+r-o[i]),e.offsets.popper=_(e.offsets.popper);s=a[c]+a[l]/2-r/2,i=d(e.instance.popper),a=parseFloat(i["margin"+u]),u=parseFloat(i["border"+u+"Width"]),u=s-e.offsets.popper[c]-a-u,u=Math.max(Math.min(o[l]-r,u),0);return e.arrowElement=n,e.offsets.arrow=(y(n={},c,Math.round(u)),y(n,t,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(u,c){if(F(u.instance.modifiers,"inner"))return u;if(u.flipped&&u.placement===u.originalPlacement)return u;var d=C(u.instance.popper,u.instance.reference,c.padding,c.boundariesElement,u.positionFixed),f=u.placement.split("-")[0],p=M(f),m=u.placement.split("-")[1]||"",h=[];switch(c.behavior){case K:h=[f,p];break;case G:h=Y(f);break;case W:h=Y(f,!0);break;default:h=c.behavior}return h.forEach(function(e,t){if(f!==e||h.length===t+1)return u;f=u.placement.split("-")[0],p=M(f);var n=u.offsets.popper,r=u.offsets.reference,i=Math.floor,o="left"===f&&i(n.right)>i(r.left)||"right"===f&&i(n.left)i(r.top)||"bottom"===f&&i(n.top)i(d.right),l=i(n.top)i(d.bottom),r="left"===f&&a||"right"===f&&s||"top"===f&&l||"bottom"===f&&e,n=-1!==["top","bottom"].indexOf(f),i=!!c.flipVariations&&(n&&"start"===m&&a||n&&"end"===m&&s||!n&&"start"===m&&l||!n&&"end"===m&&e),l=!!c.flipVariationsByContent&&(n&&"start"===m&&s||n&&"end"===m&&a||!n&&"start"===m&&e||!n&&"end"===m&&l),l=i||l;(o||r||l)&&(u.flipped=!0,(o||r)&&(f=h[t+1]),l&&(m="end"===m?"start":"start"===m?"end":m),u.placement=f+(m?"-"+m:""),u.offsets.popper=E({},u.offsets.popper,D(u.instance.popper,u.offsets.reference,u.placement)),u=L(u.instance.modifiers,u,"flip"))}),u},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],r=e.offsets,i=r.popper,o=r.reference,a=-1!==["left","right"].indexOf(n),r=-1===["top","left"].indexOf(n);return i[a?"left":"top"]=o[n]-(r?i[a?"width":"height"]:0),e.placement=M(t),e.offsets.popper=_(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!z(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=I(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.right or , not
    ","or ."].join(" ")),Ee("showOnInit"===t,"The `showOnInit` prop was renamed to `showOnCreate` in v5."),Ee("arrowType"===t,["The `arrowType` prop was removed in v5 in favor of overloading the `arrow`","prop.","\n\n",'"round" string was replaced with importing the string from the package.',"\n\n","* import {roundArrow} from 'tippy.js'; (ESM version)\n","* const {roundArrow} = tippy; (IIFE CDN version)","\n\n",'Before: {arrow: true, arrowType: "round"}\n',"After: {arrow: roundArrow}`"].join(" ")),Ee("touchHold"===t,["The `touchHold` prop was removed in v5 in favor of overloading the `touch`","prop.","\n\n","Before: {touchHold: true}\n",'After: {touch: "hold"}'].join(" ")),Ee("size"===t,["The `size` prop was removed in v5. Instead, use a theme that specifies","CSS padding and font-size properties."].join(" ")),Ee("theme"===t&&"google"===e,'The included theme "google" was renamed to "material" in v5.'),Ee(n,["Specifying placement in `popperOptions` is not supported. Use the base-level","`placement` prop instead.","\n\n",'Before: {popperOptions: {placement: "bottom"}}\n','After: {placement: "bottom"}'].join(" ")),Ee(r,["`"+t+"`","is not a valid prop. You may have spelled it incorrectly, or if it's a","plugin, forgot to pass it in an array as props.plugins.","\n\n","In v5, the following props were turned into plugins:","\n\n","* animateFill\n","* followCursor\n","* sticky","\n\n","All props: https://atomiks.github.io/tippyjs/all-props/\n","Plugins: https://atomiks.github.io/tippyjs/plugins/"].join(" "))})}var Ce={passive:!0},d="tippy-iOS",Ne="tippy-popper",Ae="tippy-tooltip",ke="tippy-content",f="tippy-arrow",p="tippy-svg-arrow",Me="."+Ne,m="."+Ae,h="."+ke,v="."+f,g="."+p,De={isTouch:!1},b=0;function y(){De.isTouch||(De.isTouch=!0,window.performance&&document.addEventListener("mousemove",w))}function w(){var e=performance.now();e-b<20&&(De.isTouch=!1,document.removeEventListener("mousemove",w)),b=e}function E(){var e,t=document.activeElement;i(t)&&(e=t._tippy,t.blur&&!e.state.isVisible&&t.blur())}var e="undefined"!=typeof window&&"undefined"!=typeof document,T=e?navigator.userAgent:"",Ie=/MSIE |Trident\//.test(T),_=e&&/iPhone|iPad|iPod/.test(navigator.platform);function Le(e){e=e&&_&&De.isTouch;document.body.classList[e?"add":"remove"](d)}function Fe(e){return e.split("-")[0]}function Pe(e){e.setAttribute("data-inertia","")}function Re(e){e.setAttribute("data-interactive","")}function je(e,t){l(t.content)?(n(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(e[t.allowHTML?"innerHTML":"textContent"]=t.content)}function Be(e){return{tooltip:e.querySelector(m),content:e.querySelector(h),arrow:e.querySelector(v)||e.querySelector(g)}}function Ue(e){var t=oe();return!0===e?t.className=f:(t.className=p,l(e)?t.appendChild(e):n(t,e)),t}function ze(e,t,n){var r=Be(e),i=r.tooltip,o=r.content,r=r.arrow;e.style.zIndex=""+n.zIndex,i.setAttribute("data-animation",n.animation),i.style.maxWidth="number"==typeof(e=n.maxWidth)?e+"px":e,n.role?i.setAttribute("role",n.role):i.removeAttribute("role"),t.content!==n.content&&je(o,n),!t.arrow&&n.arrow?(i.appendChild(Ue(n.arrow)),i.setAttribute("data-arrow","")):t.arrow&&!n.arrow?(i.removeChild(r),i.removeAttribute("data-arrow")):t.arrow!==n.arrow&&(i.removeChild(r),i.appendChild(Ue(n.arrow))),!t.interactive&&n.interactive?Re(i):t.interactive&&!n.interactive&&i.removeAttribute("data-interactive"),!t.inertia&&n.inertia?Pe(i):t.inertia&&!n.inertia&&i.removeAttribute("data-inertia"),t.theme!==n.theme&&(He(i,"remove",t.theme),He(i,"add",n.theme))}function Ve(t,n,r){["transitionend","webkitTransitionEnd"].forEach(function(e){t[n+"EventListener"](e,r)})}function He(t,n,e){pe(e).forEach(function(e){t.classList[n](e+"-theme")})}var Ye=1,Ke=[],Ge=[];function x(l,e){var i,t,n,r=Se(l,Z({},Te,{},xe(e)));if(!r.multiple&&l._tippy)return null;var o,a,s,u,c,d=!1,f=!1,p=!1,m=0,h=[],v=le(H,r.interactiveDebounce),g=ve(r.triggerTarget||l),b=Ye++,y=function(e,t){var n=oe();n.className=Ne,n.style.position="absolute",n.style.top="0",n.style.left="0";var r=oe();return r.className=Ae,r.id="tippy-"+e,r.setAttribute("data-state","hidden"),r.setAttribute("tabindex","-1"),He(r,"add",t.theme),(e=oe()).className=ke,e.setAttribute("data-state","hidden"),t.interactive&&Re(r),t.arrow&&(r.setAttribute("data-arrow",""),r.appendChild(Ue(t.arrow))),t.inertia&&Pe(r),je(e,t),r.appendChild(e),n.appendChild(r),ze(n,t,t),n}(b,r),w=Be(y),E=(c=r.plugins).filter(function(e,t){return c.indexOf(e)===t}),T=w.tooltip,e=w.content,_=[T,e],x={id:b,reference:l,popper:y,popperChildren:w,popperInstance:null,props:r,state:{currentPlacement:null,isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:E,clearDelayTimeouts:function(){clearTimeout(i),clearTimeout(t),cancelAnimationFrame(n)},setProps:function(n){var t,e;"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("setProps")),x.state.isDestroyed||("production"!==We.env.NODE_ENV&&(Oe(n,E),Ee(!!n.plugins&&(n.plugins.length!==E.length||E.some(function(e,t){return!n.plugins||!n.plugins[t]||e!==n.plugins[t]})),"Cannot update plugins")),M("onBeforeUpdate",[x,n]),z(),t=x.props,(e=Se(l,Z({},x.props,{},n,{ignoreAttributes:!0}))).ignoreAttributes=me(n.ignoreAttributes,t.ignoreAttributes),x.props=e,U(),t.interactiveDebounce!==e.interactiveDebounce&&(L(),v=le(H,e.interactiveDebounce)),ze(y,t,e),x.popperChildren=Be(y),t.triggerTarget&&!e.triggerTarget?he(t.triggerTarget).forEach(function(e){e.removeAttribute("aria-expanded")}):e.triggerTarget&&l.removeAttribute("aria-expanded"),I(),x.popperInstance&&(_e.some(function(e){return Q(n,e)&&n[e]!==t[e]})?(e=x.popperInstance.reference,x.popperInstance.destroy(),W(),x.popperInstance.reference=e,x.state.isVisible&&x.popperInstance.enableEventListeners()):x.popperInstance.update()),M("onAfterUpdate",[x,n]))},setContent:function(e){x.setProps({content:e})},show:function(e){void 0===e&&(e=ee(x.props.duration,0,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("show"));var t=x.state.isVisible,n=x.state.isDestroyed,r=!x.state.isEnabled,i=De.isTouch&&!x.props.touch;t||n||r||i||A().hasAttribute("disabled")||(x.popperInstance||W(),M("onShow",[x],!1),!1!==x.props.onShow(x)&&(P(),y.style.visibility="visible",x.state.isVisible=!0,x.state.isMounted||ae(_.concat(y),0),a=function(){x.state.isVisible&&(ae([y],x.props.updateDuration),ae(_,e),se(_,"visible"),D(),I(),ge(Ge,x),Le(!0),x.state.isMounted=!0,M("onMount",[x]),j(e,function(){x.state.isShown=!0,M("onShown",[x])}))},function(){m=0;var e=x.props.appendTo,t=A(),n=x.props.interactive&&e===Te.appendTo||"parent"===e?t.parentNode:re(e,[t]);n.contains(y)||n.appendChild(y),"production"!==We.env.NODE_ENV&&Ee(x.props.interactive&&e===Te.appendTo&&t.nextElementSibling!==y,["Interactive tippy element may not be accessible via keyboard navigation","because it is not directly after the reference element in the DOM source","order.","\n\n","Using a wrapper
    or tag around the reference element solves","this by creating a new parentNode context.","\n\n","Specifying `appendTo: document.body` silences this warning, but it","assumes you are using a focus management solution to handle keyboard","navigation.","\n\n","See: https://atomiks.github.io/tippyjs/accessibility/#interactivity"].join(" ")),ie(x.popperInstance.modifiers,"flip","enabled",x.props.flip),x.popperInstance.enableEventListeners(),x.popperInstance.update()}()))},hide:function(e){void 0===e&&(e=ee(x.props.duration,1,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("hide"));var t=!x.state.isVisible&&!d,n=x.state.isDestroyed,r=!x.state.isEnabled&&!d;t||n||r||(M("onHide",[x],!1),!1===x.props.onHide(x)&&!d||(R(),y.style.visibility="hidden",x.state.isVisible=!1,x.state.isShown=!1,ae(_,e),se(_,"hidden"),D(),I(),j(e,function(){!x.state.isVisible&&y.parentNode&&y.parentNode.contains(y)&&(x.popperInstance.disableEventListeners(),x.popperInstance.options.placement=x.props.placement,y.parentNode.removeChild(y),0===(Ge=Ge.filter(function(e){return e!==x})).length&&Le(!1),x.state.isMounted=!1,M("onHidden",[x]))})))},enable:function(){x.state.isEnabled=!0},disable:function(){x.hide(),x.state.isEnabled=!1},destroy:function(){"production"!==We.env.NODE_ENV&&Ee(x.state.isDestroyed,we("destroy")),x.state.isDestroyed||(d=!0,x.clearDelayTimeouts(),x.hide(0),z(),delete l._tippy,x.popperInstance&&x.popperInstance.destroy(),d=!1,x.state.isDestroyed=!0,M("onDestroy",[x]))}};l._tippy=x,y._tippy=x;var S=E.map(function(e){return e.fn(x)}),O=l.hasAttribute("aria-expanded");return U(),I(),r.lazy||W(),M("onCreate",[x]),r.showOnCreate&&$(),y.addEventListener("mouseenter",function(){x.props.interactive&&x.state.isVisible&&x.clearDelayTimeouts()}),y.addEventListener("mouseleave",function(e){x.props.interactive&&fe(x.props.trigger,"mouseenter")&&(v(e),g.addEventListener("mousemove",v))}),x;function C(){var e=x.props.touch;return Array.isArray(e)?e:[e,0]}function N(){return"hold"===C()[0]}function A(){return u||l}function k(e){return x.state.isMounted&&!x.state.isVisible||De.isTouch||o&&"focus"===o.type?0:ee(x.props.delay,e?0:1,Te.delay)}function M(t,n,e){void 0===e&&(e=!0),S.forEach(function(e){Q(e,t)&&e[t].apply(e,n)}),e&&(e=x.props)[t].apply(e,n)}function D(){var n,r,e=x.props.aria;e&&(n="aria-"+e,r=T.id,he(x.props.triggerTarget||l).forEach(function(e){var t=e.getAttribute(n);x.state.isVisible?e.setAttribute(n,t?t+" "+r:r):(t=t&&t.replace(r,"").trim())?e.setAttribute(n,t):e.removeAttribute(n)}))}function I(){O||he(x.props.triggerTarget||l).forEach(function(e){x.props.interactive?e.setAttribute("aria-expanded",x.state.isVisible&&e===A()?"true":"false"):e.removeAttribute("aria-expanded")})}function L(){g.body.removeEventListener("mouseleave",X),g.removeEventListener("mousemove",v),Ke=Ke.filter(function(e){return e!==v})}function F(e){if(!x.props.interactive||!y.contains(e.target)){if(A().contains(e.target)){if(De.isTouch)return;if(x.state.isVisible&&fe(x.props.trigger,"click"))return}!0===x.props.hideOnClick&&(f=!1,x.clearDelayTimeouts(),x.hide(),p=!0,setTimeout(function(){p=!1}),x.state.isMounted||R())}}function P(){g.addEventListener("mousedown",F,!0)}function R(){g.removeEventListener("mousedown",F,!0)}function j(e,t){function n(e){e.target===T&&(Ve(T,"remove",n),t())}if(0===e)return t();Ve(T,"remove",s),Ve(T,"add",n),s=n}function B(t,n,r){void 0===r&&(r=!1),he(x.props.triggerTarget||l).forEach(function(e){e.addEventListener(t,n,r),h.push({node:e,eventType:t,handler:n,options:r})})}function U(){N()&&(B("touchstart",V,Ce),B("touchend",Y,Ce)),pe(x.props.trigger).forEach(function(e){if("manual"!==e)switch(B(e,V),e){case"mouseenter":B("mouseleave",Y);break;case"focus":B(Ie?"focusout":"blur",K);break;case"focusin":B("focusout",K)}})}function z(){h.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),h=[]}function V(t){var e,n,r=!1;!x.state.isEnabled||G(t)||p||(u=(o=t).currentTarget,I(),!x.state.isVisible&&ne(t)&&Ke.forEach(function(e){return e(t)}),"click"!==t.type||fe(x.props.trigger,"mouseenter")&&!f||!1===x.props.hideOnClick||!x.state.isVisible?(e=(n=C())[0],n=n[1],De.isTouch&&"hold"===e&&n?i=setTimeout(function(){$(t)},n):$(t)):r=!0,"click"===t.type&&(f=!r),r&&X(t))}function H(e){var a,s,t=de(e.target,function(e){return e===l||e===y});"mousemove"===e.type&&t||(t=ce(y.querySelectorAll(Me)).concat(y).map(function(e){var t=e._tippy,n=t.popperChildren.tooltip,t=t.props.interactiveBorder;return{popperRect:e.getBoundingClientRect(),tooltipRect:n.getBoundingClientRect(),interactiveBorder:t}}),a=e.clientX,s=e.clientY,t.every(function(e){var t=e.popperRect,n=e.tooltipRect,r=e.interactiveBorder,i=Math.min(t.top,n.top),o=Math.max(t.right,n.right),e=Math.max(t.bottom,n.bottom),n=Math.min(t.left,n.left);return r element.cloneNode(true)"].join(" ")));t=t.reduce(function(e,t){t=t&&x(t,a);return t&&e.push(t),e},[]);return l(e)?t[0]:t}S.version="5.2.1",S.defaultProps=Te,S.setDefaultProps=o,S.currentInput=De,C.BACKDROP_CLASS="tippy-backdrop",C.ROUND_ARROW='',C._extends=Z,C.arrayFrom=ce,C.closestCallback=de,C.createTippyWithPlugins=function(r){"production"!==We.env.NODE_ENV&&Ee(!0,["createTippyWithPlugins([...]) has been deprecated.","\n\n","Use tippy.setDefaultProps({plugins: [...]}) instead."].join(" "));function e(e,t,n){return void 0===n&&(n=[]),n=(t=void 0===t?{}:t).plugins||n,S(e,Z({},t,{plugins:[].concat(r,n)}))}return e.version="5.2.1",e.defaultProps=Te,e.setDefaultProps=o,e.currentInput=De,e},C.currentInput=De,C.defaultProps=Te,C.div=oe,C.errorWhen=u,C.getBasePlacement=Fe,C.getOwnerDocument=ve,C.hideAll=function(e){var e=void 0===e?{}:e,n=e.exclude,r=e.duration;Ge.forEach(function(e){var t=!1;(t=n?i(n)?e.reference===n:e.popper===n.popper:t)||e.hide(r)})},C.includes=fe,C.isBrowser=e,C.isMouseEvent=ne,C.normalizeToArray=he,C.removeProperties=a,C.setVisibilityState=se,C.tippy=S,C.useIfDefined=me,C.warnWhen=Ee}.call(this)}.call(this,O("_process"))},{_process:4,"popper.js":3}],7:[function(i,e,o){!function(f){!function(){"use strict";Object.defineProperty(o,"__esModule",{value:!0});var y=i("./tippy.chunk.cjs.js");i("popper.js");var d={mouseover:"mouseenter",focusin:"focus",click:"click"},e={name:"animateFill",defaultValue:!1,fn:function(e){var t=e.popperChildren,n=t.tooltip,r=t.content,i=e.props.animateFill?((t=y.div()).className=y.BACKDROP_CLASS,y.setVisibilityState([t],"hidden"),t):null;function o(){e.popperChildren.backdrop=i}return{onCreate:function(){i&&(o(),n.insertBefore(i,n.firstElementChild),n.setAttribute("data-animatefill",""),n.style.overflow="hidden",e.setProps({animation:"shift-away",arrow:!1}))},onMount:function(){var e,t;i&&(e=n.style.transitionDuration,t=Number(e.replace("ms","")),r.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y.setVisibilityState([i],"visible"),"production"!==f.env.NODE_ENV&&(y.warnWhen("absolute"!==getComputedStyle(i).position,"The `tippy.js/dist/backdrop.css` stylesheet has not been\n imported!\n \n The `animateFill` plugin requires this stylesheet to work."),y.warnWhen("none"===getComputedStyle(n).transform,"The `tippy.js/animations/shift-away.css` stylesheet has not\n been imported!\n \n The `animateFill` plugin requires this stylesheet to work.")))},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y.setVisibilityState([i],"hidden")},onAfterUpdate:function(){o()}}}},t={name:"followCursor",defaultValue:!1,fn:function(c){var d,f=c.reference,p=c.popper,m=null,e=y.getOwnerDocument(c.props.triggerTarget||f),n=null,r=!1,i=c.props;function t(){return"manual"===c.props.trigger.trim()}function o(){var e=!!t()||null!==n&&!(0===n.clientX&&0===n.clientY);return c.props.followCursor&&e}function h(){return y.currentInput.isTouch||"initial"===c.props.followCursor&&c.state.isVisible}function a(){c.popperInstance&&m&&(c.popperInstance.reference=m)}function s(){var e,t;!o()&&c.props.placement===i.placement||(t=(e=i.placement).split("-")[1],r=!0,c.setProps({placement:o()&&t?e.replace(t,"start"===t?"end":"start"):e}),r=!1)}function l(){c.popperInstance&&o()&&h()&&c.popperInstance.disableEventListeners()}function u(){o()?e.addEventListener("mousemove",b):a()}function v(){o()&&b(d)}function g(){e.removeEventListener("mousemove",b)}function b(e){var t,n,r,i,o,a,s=d=e,l=s.clientX,u=s.clientY;c.popperInstance&&c.state.currentPlacement&&(s=y.closestCallback(e.target,function(e){return e===f}),e=c.props.followCursor,t="horizontal"===e,n="vertical"===e,r=y.includes(["top","bottom"],y.getBasePlacement(c.state.currentPlacement)),e={size:e=r?p.offsetWidth:p.offsetHeight,x:r?e:0,y:r?0:e},i=e.size,o=e.x,a=e.y,!s&&c.props.interactive||(null===m&&(m=c.popperInstance.reference),c.popperInstance.reference={referenceNode:f,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){var e=f.getBoundingClientRect();return{width:r?i:0,height:r?0:i,top:(t?e.top:u)-a,bottom:(t?e.bottom:u)+a,left:(n?e.left:l)-o,right:(n?e.right:l)+o}}},c.popperInstance.update()),h()&&g())}return{onAfterUpdate:function(e,t){var n;r||(n=t,Object.keys(n).forEach(function(e){i[e]=y.useIfDefined(n[e],i[e])}),t.placement&&s()),t.placement&&l(),requestAnimationFrame(v)},onMount:function(){v(),l()},onShow:function(){t()&&(d=n={clientX:0,clientY:0},s(),u())},onTrigger:function(e,t){n||(y.isMouseEvent(t)&&(n={clientX:t.clientX,clientY:t.clientY},d=t),s(),u())},onUntrigger:function(){c.state.isVisible||(g(),n=null)},onHidden:function(){g(),a(),n=null}}}},n={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t=e.reference;function n(){return e.props.inlinePositioning}return{onHidden:function(){n()&&(e.popperInstance.reference=t)},onShow:function(){n()&&(e.popperInstance.reference={referenceNode:t,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){return function(t,e,n){if(n.length<2||null===t)return e;switch(t){case"top":case"bottom":var r=n[0],i=n[n.length-1],o="top"===t,a=r.top,s=i.bottom,l=(o?r:i).left,i=(o?r:i).right;return{top:a,bottom:s,left:l,right:i,width:i-l,height:s-a};case"left":case"right":var u=Math.min.apply(Math,n.map(function(e){return e.left})),c=Math.max.apply(Math,n.map(function(e){return e.right})),s=n.filter(function(e){return"left"===t?e.left===u:e.right===c}),a=s[0].top,s=s[s.length-1].bottom;return{top:a,bottom:s,left:u,right:c,width:c-u,height:s-a};default:return e}}(e.state.currentPlacement&&y.getBasePlacement(e.state.currentPlacement),t.getBoundingClientRect(),y.arrayFrom(t.getClientRects()))}})}}}},r={name:"sticky",defaultValue:!1,fn:function(n){var r=n.reference,i=n.popper;function o(e){return!0===n.props.sticky||n.props.sticky===e}var a=null,s=null;function l(){var e=o("reference")?(n.popperInstance?n.popperInstance.reference:r).getBoundingClientRect():null,t=o("popper")?i.getBoundingClientRect():null;(e&&u(a,e)||t&&u(s,t))&&n.popperInstance.update(),a=e,s=t,n.state.isMounted&&requestAnimationFrame(l)}return{onMount:function(){n.props.sticky&&l()}}}};function u(e,t){return!e||!t||e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left}o.createTippyWithPlugins=y.createTippyWithPlugins,o.default=y.tippy,o.hideAll=y.hideAll,o.roundArrow=y.ROUND_ARROW,o.animateFill=e,o.createSingleton=function(o,e,t){void 0===e&&(e={}),void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!Array.isArray(o),["The first argument passed to createSingleton() must be an array of tippy","instances. The passed value was",String(o)].join(" ")),t=e.plugins||t,o.forEach(function(e){e.disable()});var a,s,l=y._extends({},y.defaultProps,{},e).aria,n=!1,u=o.map(function(e){return e.reference}),r={fn:function(r){function i(e){var t;a&&(t="aria-"+a,e&&!r.props.interactive?s.setAttribute(t,r.popperChildren.tooltip.id):s.removeAttribute(t))}return{onAfterUpdate:function(e,t){t=t.aria;void 0!==t&&t!==l&&(n?(n=!0,r.setProps({aria:null}),n=!1):l=t)},onDestroy:function(){o.forEach(function(e){e.enable()})},onMount:function(){i(!0)},onUntrigger:function(){i(!1)},onTrigger:function(e,t){var n=t.currentTarget,t=u.indexOf(n);n!==s&&(s=n,a=l,r.state.isVisible&&i(!0),r.popperInstance.reference=n,r.setContent(o[t].props.content))}}}};return y.tippy(y.div(),y._extends({},e,{plugins:[r].concat(t),aria:null,triggerTarget:u}))},o.delegate=function(e,r,t){void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!(r&&r.target),["You must specity a `target` prop indicating a CSS selector string matching","the target elements that should receive a tippy."].join(" ")),t=r.plugins||t;var i=[],o=[],a=r.target,n=y.removeProperties(r,["target"]),s=y._extends({},n,{plugins:t,trigger:"manual"}),l=y._extends({},n,{plugins:t,showOnCreate:!0}),s=y.tippy(e,s);function u(e){var t,n;!e.target||(n=e.target.closest(a))&&(t=n.getAttribute("data-tippy-trigger")||r.trigger||y.defaultProps.trigger,!y.includes(t,d[e.type])||(n=y.tippy(n,l))&&(o=o.concat(n)))}function c(e,t,n,r){e.addEventListener(t,n,r=void 0===r?!1:r),i.push({node:e,eventType:t,handler:n,options:r})}return y.normalizeToArray(s).forEach(function(e){var t=e.destroy;e.destroy=function(e){(e=void 0===e?!0:e)&&o.forEach(function(e){e.destroy()}),o=[],i.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),i=[],t()},c(e=e.reference,"mouseover",u),c(e,"focusin",u),c(e,"click",u)}),s},o.followCursor=t,o.inlinePositioning=n,o.sticky=r}.call(this)}.call(this,i("_process"))},{"./tippy.chunk.cjs.js":6,_process:4,"popper.js":3}],8:[function(e,t,n){"use strict";var r=a(e("mithril")),i=a(e("./view")),o=a(e("./texte/de"));function a(e){return e&&e.__esModule?e:{default:e}}document,window,domIsReady(function(){window.texte=o.default,r.default.mount(window.document.getElementById("gradido-mithril-user-search"),i.default)})},{"./texte/de":13,"./view":14,mithril:1}],9:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=o(e("mithril")),i=o(e("numeral"));function o(e){return e&&e.__esModule?e:{default:e}}var a=!1;n.default={view:function(e){return(0,r.default)("span",(e=e.attrs.centAmount/1e4,i.default.locale("de"),(0,i.default)(e).format("0,0.00")+" GDD"))},oninit:function(e){a||(i.default.register("locale","de",{delimiters:{thousands:".",decimal:","},currency:{symbol:"€"}}),i.default.locale("de"),a=!0)}}},{mithril:1,numeral:2}],10:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e){return{oncreate:function(e){(0,r.default)(e.dom,{content:e.attrs.accountState.getTooltipText(),placement:"right-start",theme:"rippleUI-theme",arrow:!1})},onremove:function(){},view:function(){return e}}},i(e("mithril"));var r=i(e("tippy.js"));function i(e){return e&&e.__esModule?e:{default:e}}},{mithril:1,"tippy.js":7}],11:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};n.default={view:function(e){return(0,r.default)("div.modal.visible-modal",{tabindex:"-1",role:"dialog"},(0,r.default)("div.modal-dialog",{role:"document"},(0,r.default)("div.modal-content",[(0,r.default)("p.grid-header",e.attrs.title),(0,r.default)("div.modal-body",e.attrs.body),(0,r.default)("div.modal-footer",[(0,r.default)("button.btn.btn-primary",{type:"button",disabled:e.attrs.btnDisabled,"data-dismiss":"modal",onclick:e.attrs.dismiss},"Ok")])])))},oninit:function(e){}}},{mithril:1}],12:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};function i(e,t){for(var n=0;n';if(t+='
    '+e.description.title+"
    ",null!=e.description.lines){for(var n in t+="
      ",e.description.lines){var r=e.description.lines[n],i=window.texte.ACCOUNT_STATES.LINES[n],n="mdi ";if("+"===r)n+=" mdi-check color-primary";else if("-"===r)n+=" mdi-close color-danger";else{if("/"!==r)continue;n+=" mdi-alert-circle-outline color-warning"}t+="
    • ",t+='',t+=" ",t+=""+i.title+": "+i[r]+"",t+="
    • "}t+="
    "}return t+"
    "}}]),e=o;function o(e){!function(e){if(!(e instanceof o))throw new TypeError("Cannot call a class as a function")}(this),this.stateName=e,this.texte=window.texte.ACCOUNT_STATES[e]}n.default=e},{mithril:1}],13:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0,n.default={ACCOUNT_STATES:{LINES:[{title:"Login-Server","+":"Das Benutzer-Konto liegt auf dem Login-Server!","-":"Das Benutzer-Konto existiert nicht auf dem Login-Server!"},{title:"Konto Aktivierung","+":"Der Benutzer hat sein Konto aktiviert!","-":"Der Benutzer hat noch nicht auf den Link in seiner E-Mail geklickt!"},{title:"Schlüsselpaar","+":"Es wurde bereits ein Schlüsselpaar für den Benutzer erzeugt und ihm eine Passphrase gezeigt!","-":"Es wurden noch keine Schlüssel und Passphrase für den Benutzer erzeugt!"},{title:"Gemeinschafts-Server","+":"Das Benutzer-Konto wurde auf auf den Gemeinschafts-Server kopiert!","-":"Das Benutzer-Konto wurde noch nicht auf den Gemeinschafts-Server kopiert!"},{title:"Gradidos","+":"Es können Gradidos an den Benutzer geschickt werden!","-":"Es können noch keine Gradidos an den Benutzer geschickt werden!","/":"Es können theoretisch Gradidos an den Benutzer geschickt werden, aber es ist nicht sicher ob er sie verwenden kann!"}],"account created":{title:"Konto angelegt",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung.",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account not on login-server":{title:"Konto nicht auf Login-Server",color:"danger",description:{title:"Das Konto existiert nicht (mehr) auf dem Login-Server: ein Fehler oder es wurde nur teilweise gelöscht.",lines:["-","+","+","+","/"]},todo:{title:"Wenn der Benutzer seine Passphrase hat, kann er sein Konto auf dem Login-Server wiederherstellen, ansonsten bleibt eigentlich nur das Konto auf dem Gemeinschafts-Server zu löschen",lines:["Nachdem das Konto auf dem Login-Server kopiert wurde, bekommt der Benutzer automatisch eine E-Mail mit einem Link zur Kontoaktivierung zugeschickt.","{{copy-from-community-to-login-server}}","
    ","Es wird nur der Benutzer gelöscht, nicht seine Transaktionen. Zu diesen hat er wieder Zugriff wenn er sein Konto mit Hilfe seiner Passphrase wiederhergestellt hat.","{{user-transactions-overview}}","{{delete-from-community-server}}"]}},"email activated":{title:"Konto aktiviert",color:"primary",description:{title:"Das Konto wurde vom Benutzer aktiviert und Schlüssel erzeugt, aber noch nicht auf diesen Gemeinschafts-Server kopiert.",lines:["+","+","+","-","-"]},todo:{title:"Das Benutzer Konto müsste vom Login-Server auf den Gemeinschafts-Server kopiert werden.",lines:["{{copy-from-login-to-community-server}}"]}},"account copied to community":{title:"Konto auf Gemeinschafts-Server",color:"success",description:{title:"Das Konto ist vollständig aktiviert und es können Gradidos überwiesen werden.",lines:["+","+","+","+","+"]}},"email not activated":{title:"Konto nicht aktiviert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung. Es wurde aber noch nicht aktiviert!",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account multiple times on login-server":{title:"Konto mehrfach vorhanden",color:"danger",description:{title:"Das Konto ist auf dem Login-Server mehrfach vorhanden, das sollte nicht sein, auf dem Login-Server muss ein Fehler passiert sein!!"},todo:{title:"Du solltest mit dem Entwickler in Kontakt treten.",lines:["{{mailto-developer}}"]}},"account not on community server":{title:"Konto nicht auf Gemeinschafts-Server",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber existiert noch nicht auf dem Gemeinschafts-Server!",lines:["+",null,"-","-","-"]},todo:{title:"Das sollte eigentlich nicht passieren, du solltest mit dem Entwickler sprechen.",lines:["{{mailto-developer}}"]}},"no keys":{title:"Keine Schlüssel generiert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber es wurden noch keine Schlüssel generiert!",lines:["+","+","-","-","-"]},todo:{title:"Der Benutzer muss sich mit seinen Daten einloggen, dann müsste er automatisch durch die Schlüsselgenerierung/Konto-Wiederherstellung geführt werden",lines:["{{mailto-user-login}}","Wenn der Benutzer sein Passwort vergessen hat, kann er sich auch eine Passwort Reset E-Mail zuschicken lassen.","{{reset-password}}","{{mailto-reset-password}}"]}}},NO_USER_FOUND:"Keine Benutzer gefunden",USER_FOUND:"Benutzer gefunden",NAME:"Name",EMAIL:"E-Mail",BALANCE:"Kontostand",PUBLIC_KEY:"Öffentlicher
    Schlüssel",CREATED:"Erstellt",COPY_FROM_LOGIN_TO_COMMUNITY:"Vom Login-Server zum Gemeinschafts-Server kopieren",COPY_FROM_LOGIN_TO_COMMUNITY_SUCCESS:"Kontodaten wurden erfolgreich vom Login-Server zu diesem Gemeinschafts-Server kopiert!",COPY_FROM_COMMUNITY_TO_LOGIN:"Vom Gemeinschafts-Server zum Login-Server kopieren",COPY_FROM_COMMUNITY_TO_LOGIN_SUCCESS:"Kontodaten wurden erfolgreich vom Gemeinschafts-Server zum Login-Server kopiert!",DELETE_FROM_COMMUNITY:"Benutzer Konto vom Gemeinschafts-Server löschen",DELETE_FROM_COMMUNITY_SUCCESS:"Benutzer Konto vom Gemeinschafts-Server erfolgreich gelöscht",VERIFICATION_EMAIL_RESEND:"Verification Email erneut zusenden",VERIFICATION_EMAIL_RESEND_SUCCESS:"Verification Email wird erneut zugestellt",COPY_FAILED:"Fehler beim Kopieren",DELETE_FAILED:"Fehler beim löschen",RESEND_FAILED:"Senden fehlgeschlagen",AJAX_CRITICAL:"Kritischer Fehler beim Ajax-Request",COPY_IN_PROGRESS:"Wird kopiert",DELETE_IN_PROGRESS:"Wird gelöscht",RESEND_IN_PROGRESS:"Wird gesendet",RECEIVE_TRANSACTIONS_COUNT:"Erhaltene Transaktionen: ",SENDED_TRANSACTIONS_COUNT:"gesendete Transaktionen: ",CREATION_TRANSACTIONS_COUNT:"erhaltende Schöpfungs-Transaktionen: "}},{}],14:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=a(e("mithril")),i=a(e("./userTable")),o=a(e("../model/AccountState"));function a(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(e){return void 0===g_users?(0,r.default)(""):0"===e)return i.default.trust("

    ");if(t){var n=function(){switch(t[1]){case"copy-from-login-to-community-server":return o.default;case"user-transactions-overview":return s.default;case"copy-from-community-to-login-server":return l.default;case"verification-resend":return u.default;case"mailto-verification-resend":return a.default}return null}();return n?(0,i.default)("li",(0,i.default)(n,{user:r.attrs.user,updateState:r.attrs.updateState,deleteUser:r.attrs.deleteUser})):(0,i.default)("li",e)}return(0,i.default)("li",e)})]):null]))},oninit:function(e){e.state.accountStateTexte=window.texte.ACCOUNT_STATES[e.attrs.user.indicator.name]}}},{"./actions/copyCommunityLogin":16,"./actions/copyLoginCommunity":17,"./actions/deleteCommunityServer":18,"./actions/mailtoVerificationResend":19,"./actions/userTransactionsOverview":20,"./actions/verificationResend":21,mithril:1}],24:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var d=r(e("mithril")),f=r(e("../../model/AccountState")),p=r(e("../../lib/Gradido")),m=r(e("../../lib/Tooltip")),h=r(e("../../lib/dialog"));function r(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(t){var e=new f.default(t.attrs.user.indicator.name),n=t.attrs.user,r=e.getColor(),i=e.getTitle(),o=n.pubkeyhex;""!==o&&(o=n.pubkeyhex.substring(0,10)+"...");var a=n.created;a&&(a=(c=new Date(a)).toLocaleDateString()+"
    "+c.toLocaleTimeString());var s=!1;e.hasActions()&&(s=e.getRawColor(),s="primary");var l="down";t.attrs.open&&(l="up");var u=n.email,c=!1;return 25>>0,o=0;if(3===arguments.length)n=arguments[2];else{for(;o=n.clientWidth&&e>=n.clientHeight}),t=(0l[e]&&!r.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),y({},t,n)}};return s.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=E({},u,c[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,r=t.reference,i=e.placement.split("-")[0],o=Math.floor,a=-1!==["top","bottom"].indexOf(i),t=a?"right":"bottom",i=a?"left":"top",a=a?"width":"height";return n[t]o(r[t])&&(e.offsets.popper[i]=o(r[t])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){if(!U(e.instance.modifiers,"arrow","keepTogether"))return e;var n=t.element;if("string"==typeof n){if(!(n=e.instance.popper.querySelector(n)))return e}else if(!e.instance.popper.contains(n))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],i=e.offsets,o=i.popper,a=i.reference,s=-1!==["left","right"].indexOf(r),l=s?"height":"width",u=s?"Top":"Left",c=u.toLowerCase(),t=s?"left":"top",i=s?"bottom":"right",r=A(n)[l];a[i]-ro[i]&&(e.offsets.popper[c]+=a[c]+r-o[i]),e.offsets.popper=x(e.offsets.popper);s=a[c]+a[l]/2-r/2,i=d(e.instance.popper),a=parseFloat(i["margin"+u]),u=parseFloat(i["border"+u+"Width"]),u=s-e.offsets.popper[c]-a-u,u=Math.max(Math.min(o[l]-r,u),0);return e.arrowElement=n,e.offsets.arrow=(y(n={},c,Math.round(u)),y(n,t,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(u,c){if(F(u.instance.modifiers,"inner"))return u;if(u.flipped&&u.placement===u.originalPlacement)return u;var d=C(u.instance.popper,u.instance.reference,c.padding,c.boundariesElement,u.positionFixed),f=u.placement.split("-")[0],p=M(f),m=u.placement.split("-")[1]||"",h=[];switch(c.behavior){case K:h=[f,p];break;case G:h=Y(f);break;case W:h=Y(f,!0);break;default:h=c.behavior}return h.forEach(function(e,t){if(f!==e||h.length===t+1)return u;f=u.placement.split("-")[0],p=M(f);var n=u.offsets.popper,r=u.offsets.reference,i=Math.floor,o="left"===f&&i(n.right)>i(r.left)||"right"===f&&i(n.left)i(r.top)||"bottom"===f&&i(n.top)i(d.right),l=i(n.top)i(d.bottom),r="left"===f&&a||"right"===f&&s||"top"===f&&l||"bottom"===f&&e,n=-1!==["top","bottom"].indexOf(f),i=!!c.flipVariations&&(n&&"start"===m&&a||n&&"end"===m&&s||!n&&"start"===m&&l||!n&&"end"===m&&e),l=!!c.flipVariationsByContent&&(n&&"start"===m&&s||n&&"end"===m&&a||!n&&"start"===m&&e||!n&&"end"===m&&l),l=i||l;(o||r||l)&&(u.flipped=!0,(o||r)&&(f=h[t+1]),l&&(m="end"===m?"start":"start"===m?"end":m),u.placement=f+(m?"-"+m:""),u.offsets.popper=E({},u.offsets.popper,D(u.instance.popper,u.offsets.reference,u.placement)),u=I(u.instance.modifiers,u,"flip"))}),u},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],r=e.offsets,i=r.popper,o=r.reference,a=-1!==["left","right"].indexOf(n),r=-1===["top","left"].indexOf(n);return i[a?"left":"top"]=o[n]-(r?i[a?"width":"height"]:0),e.placement=M(t),e.offsets.popper=x(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!U(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=L(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.right or , not
    ","or ."].join(" ")),Ee("showOnInit"===t,"The `showOnInit` prop was renamed to `showOnCreate` in v5."),Ee("arrowType"===t,["The `arrowType` prop was removed in v5 in favor of overloading the `arrow`","prop.","\n\n",'"round" string was replaced with importing the string from the package.',"\n\n","* import {roundArrow} from 'tippy.js'; (ESM version)\n","* const {roundArrow} = tippy; (IIFE CDN version)","\n\n",'Before: {arrow: true, arrowType: "round"}\n',"After: {arrow: roundArrow}`"].join(" ")),Ee("touchHold"===t,["The `touchHold` prop was removed in v5 in favor of overloading the `touch`","prop.","\n\n","Before: {touchHold: true}\n",'After: {touch: "hold"}'].join(" ")),Ee("size"===t,["The `size` prop was removed in v5. Instead, use a theme that specifies","CSS padding and font-size properties."].join(" ")),Ee("theme"===t&&"google"===e,'The included theme "google" was renamed to "material" in v5.'),Ee(n,["Specifying placement in `popperOptions` is not supported. Use the base-level","`placement` prop instead.","\n\n",'Before: {popperOptions: {placement: "bottom"}}\n','After: {placement: "bottom"}'].join(" ")),Ee(r,["`"+t+"`","is not a valid prop. You may have spelled it incorrectly, or if it's a","plugin, forgot to pass it in an array as props.plugins.","\n\n","In v5, the following props were turned into plugins:","\n\n","* animateFill\n","* followCursor\n","* sticky","\n\n","All props: https://atomiks.github.io/tippyjs/all-props/\n","Plugins: https://atomiks.github.io/tippyjs/plugins/"].join(" "))})}var Ce={passive:!0},d="tippy-iOS",Ne="tippy-popper",ke="tippy-tooltip",Ae="tippy-content",f="tippy-arrow",p="tippy-svg-arrow",Me="."+Ne,m="."+ke,h="."+Ae,v="."+f,g="."+p,De={isTouch:!1},b=0;function y(){De.isTouch||(De.isTouch=!0,window.performance&&document.addEventListener("mousemove",w))}function w(){var e=performance.now();e-b<20&&(De.isTouch=!1,document.removeEventListener("mousemove",w)),b=e}function E(){var e,t=document.activeElement;i(t)&&(e=t._tippy,t.blur&&!e.state.isVisible&&t.blur())}var e="undefined"!=typeof window&&"undefined"!=typeof document,T=e?navigator.userAgent:"",Le=/MSIE |Trident\//.test(T),x=e&&/iPhone|iPad|iPod/.test(navigator.platform);function Ie(e){e=e&&x&&De.isTouch;document.body.classList[e?"add":"remove"](d)}function Fe(e){return e.split("-")[0]}function Pe(e){e.setAttribute("data-inertia","")}function Re(e){e.setAttribute("data-interactive","")}function je(e,t){l(t.content)?(n(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(e[t.allowHTML?"innerHTML":"textContent"]=t.content)}function Be(e){return{tooltip:e.querySelector(m),content:e.querySelector(h),arrow:e.querySelector(v)||e.querySelector(g)}}function ze(e){var t=oe();return!0===e?t.className=f:(t.className=p,l(e)?t.appendChild(e):n(t,e)),t}function Ue(e,t,n){var r=Be(e),i=r.tooltip,o=r.content,r=r.arrow;e.style.zIndex=""+n.zIndex,i.setAttribute("data-animation",n.animation),i.style.maxWidth="number"==typeof(e=n.maxWidth)?e+"px":e,n.role?i.setAttribute("role",n.role):i.removeAttribute("role"),t.content!==n.content&&je(o,n),!t.arrow&&n.arrow?(i.appendChild(ze(n.arrow)),i.setAttribute("data-arrow","")):t.arrow&&!n.arrow?(i.removeChild(r),i.removeAttribute("data-arrow")):t.arrow!==n.arrow&&(i.removeChild(r),i.appendChild(ze(n.arrow))),!t.interactive&&n.interactive?Re(i):t.interactive&&!n.interactive&&i.removeAttribute("data-interactive"),!t.inertia&&n.inertia?Pe(i):t.inertia&&!n.inertia&&i.removeAttribute("data-inertia"),t.theme!==n.theme&&(He(i,"remove",t.theme),He(i,"add",n.theme))}function Ve(t,n,r){["transitionend","webkitTransitionEnd"].forEach(function(e){t[n+"EventListener"](e,r)})}function He(t,n,e){pe(e).forEach(function(e){t.classList[n](e+"-theme")})}var Ye=1,Ke=[],Ge=[];function _(l,e){var i,t,n,r=Se(l,Z({},Te,{},_e(e)));if(!r.multiple&&l._tippy)return null;var o,a,s,u,c,d=!1,f=!1,p=!1,m=0,h=[],v=le(H,r.interactiveDebounce),g=ve(r.triggerTarget||l),b=Ye++,y=function(e,t){var n=oe();n.className=Ne,n.style.position="absolute",n.style.top="0",n.style.left="0";var r=oe();return r.className=ke,r.id="tippy-"+e,r.setAttribute("data-state","hidden"),r.setAttribute("tabindex","-1"),He(r,"add",t.theme),(e=oe()).className=Ae,e.setAttribute("data-state","hidden"),t.interactive&&Re(r),t.arrow&&(r.setAttribute("data-arrow",""),r.appendChild(ze(t.arrow))),t.inertia&&Pe(r),je(e,t),r.appendChild(e),n.appendChild(r),Ue(n,t,t),n}(b,r),w=Be(y),E=(c=r.plugins).filter(function(e,t){return c.indexOf(e)===t}),T=w.tooltip,e=w.content,x=[T,e],_={id:b,reference:l,popper:y,popperChildren:w,popperInstance:null,props:r,state:{currentPlacement:null,isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:E,clearDelayTimeouts:function(){clearTimeout(i),clearTimeout(t),cancelAnimationFrame(n)},setProps:function(n){var t,e;"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("setProps")),_.state.isDestroyed||("production"!==We.env.NODE_ENV&&(Oe(n,E),Ee(!!n.plugins&&(n.plugins.length!==E.length||E.some(function(e,t){return!n.plugins||!n.plugins[t]||e!==n.plugins[t]})),"Cannot update plugins")),M("onBeforeUpdate",[_,n]),U(),t=_.props,(e=Se(l,Z({},_.props,{},n,{ignoreAttributes:!0}))).ignoreAttributes=me(n.ignoreAttributes,t.ignoreAttributes),_.props=e,z(),t.interactiveDebounce!==e.interactiveDebounce&&(I(),v=le(H,e.interactiveDebounce)),Ue(y,t,e),_.popperChildren=Be(y),t.triggerTarget&&!e.triggerTarget?he(t.triggerTarget).forEach(function(e){e.removeAttribute("aria-expanded")}):e.triggerTarget&&l.removeAttribute("aria-expanded"),L(),_.popperInstance&&(xe.some(function(e){return Q(n,e)&&n[e]!==t[e]})?(e=_.popperInstance.reference,_.popperInstance.destroy(),W(),_.popperInstance.reference=e,_.state.isVisible&&_.popperInstance.enableEventListeners()):_.popperInstance.update()),M("onAfterUpdate",[_,n]))},setContent:function(e){_.setProps({content:e})},show:function(e){void 0===e&&(e=ee(_.props.duration,0,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("show"));var t=_.state.isVisible,n=_.state.isDestroyed,r=!_.state.isEnabled,i=De.isTouch&&!_.props.touch;t||n||r||i||k().hasAttribute("disabled")||(_.popperInstance||W(),M("onShow",[_],!1),!1!==_.props.onShow(_)&&(P(),y.style.visibility="visible",_.state.isVisible=!0,_.state.isMounted||ae(x.concat(y),0),a=function(){_.state.isVisible&&(ae([y],_.props.updateDuration),ae(x,e),se(x,"visible"),D(),L(),ge(Ge,_),Ie(!0),_.state.isMounted=!0,M("onMount",[_]),j(e,function(){_.state.isShown=!0,M("onShown",[_])}))},function(){m=0;var e=_.props.appendTo,t=k(),n=_.props.interactive&&e===Te.appendTo||"parent"===e?t.parentNode:re(e,[t]);n.contains(y)||n.appendChild(y),"production"!==We.env.NODE_ENV&&Ee(_.props.interactive&&e===Te.appendTo&&t.nextElementSibling!==y,["Interactive tippy element may not be accessible via keyboard navigation","because it is not directly after the reference element in the DOM source","order.","\n\n","Using a wrapper
    or tag around the reference element solves","this by creating a new parentNode context.","\n\n","Specifying `appendTo: document.body` silences this warning, but it","assumes you are using a focus management solution to handle keyboard","navigation.","\n\n","See: https://atomiks.github.io/tippyjs/accessibility/#interactivity"].join(" ")),ie(_.popperInstance.modifiers,"flip","enabled",_.props.flip),_.popperInstance.enableEventListeners(),_.popperInstance.update()}()))},hide:function(e){void 0===e&&(e=ee(_.props.duration,1,Te.duration)),"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("hide"));var t=!_.state.isVisible&&!d,n=_.state.isDestroyed,r=!_.state.isEnabled&&!d;t||n||r||(M("onHide",[_],!1),!1===_.props.onHide(_)&&!d||(R(),y.style.visibility="hidden",_.state.isVisible=!1,_.state.isShown=!1,ae(x,e),se(x,"hidden"),D(),L(),j(e,function(){!_.state.isVisible&&y.parentNode&&y.parentNode.contains(y)&&(_.popperInstance.disableEventListeners(),_.popperInstance.options.placement=_.props.placement,y.parentNode.removeChild(y),0===(Ge=Ge.filter(function(e){return e!==_})).length&&Ie(!1),_.state.isMounted=!1,M("onHidden",[_]))})))},enable:function(){_.state.isEnabled=!0},disable:function(){_.hide(),_.state.isEnabled=!1},destroy:function(){"production"!==We.env.NODE_ENV&&Ee(_.state.isDestroyed,we("destroy")),_.state.isDestroyed||(d=!0,_.clearDelayTimeouts(),_.hide(0),U(),delete l._tippy,_.popperInstance&&_.popperInstance.destroy(),d=!1,_.state.isDestroyed=!0,M("onDestroy",[_]))}};l._tippy=_,y._tippy=_;var S=E.map(function(e){return e.fn(_)}),O=l.hasAttribute("aria-expanded");return z(),L(),r.lazy||W(),M("onCreate",[_]),r.showOnCreate&&$(),y.addEventListener("mouseenter",function(){_.props.interactive&&_.state.isVisible&&_.clearDelayTimeouts()}),y.addEventListener("mouseleave",function(e){_.props.interactive&&fe(_.props.trigger,"mouseenter")&&(v(e),g.addEventListener("mousemove",v))}),_;function C(){var e=_.props.touch;return Array.isArray(e)?e:[e,0]}function N(){return"hold"===C()[0]}function k(){return u||l}function A(e){return _.state.isMounted&&!_.state.isVisible||De.isTouch||o&&"focus"===o.type?0:ee(_.props.delay,e?0:1,Te.delay)}function M(t,n,e){void 0===e&&(e=!0),S.forEach(function(e){Q(e,t)&&e[t].apply(e,n)}),e&&(e=_.props)[t].apply(e,n)}function D(){var n,r,e=_.props.aria;e&&(n="aria-"+e,r=T.id,he(_.props.triggerTarget||l).forEach(function(e){var t=e.getAttribute(n);_.state.isVisible?e.setAttribute(n,t?t+" "+r:r):(t=t&&t.replace(r,"").trim())?e.setAttribute(n,t):e.removeAttribute(n)}))}function L(){O||he(_.props.triggerTarget||l).forEach(function(e){_.props.interactive?e.setAttribute("aria-expanded",_.state.isVisible&&e===k()?"true":"false"):e.removeAttribute("aria-expanded")})}function I(){g.body.removeEventListener("mouseleave",X),g.removeEventListener("mousemove",v),Ke=Ke.filter(function(e){return e!==v})}function F(e){if(!_.props.interactive||!y.contains(e.target)){if(k().contains(e.target)){if(De.isTouch)return;if(_.state.isVisible&&fe(_.props.trigger,"click"))return}!0===_.props.hideOnClick&&(f=!1,_.clearDelayTimeouts(),_.hide(),p=!0,setTimeout(function(){p=!1}),_.state.isMounted||R())}}function P(){g.addEventListener("mousedown",F,!0)}function R(){g.removeEventListener("mousedown",F,!0)}function j(e,t){function n(e){e.target===T&&(Ve(T,"remove",n),t())}if(0===e)return t();Ve(T,"remove",s),Ve(T,"add",n),s=n}function B(t,n,r){void 0===r&&(r=!1),he(_.props.triggerTarget||l).forEach(function(e){e.addEventListener(t,n,r),h.push({node:e,eventType:t,handler:n,options:r})})}function z(){N()&&(B("touchstart",V,Ce),B("touchend",Y,Ce)),pe(_.props.trigger).forEach(function(e){if("manual"!==e)switch(B(e,V),e){case"mouseenter":B("mouseleave",Y);break;case"focus":B(Le?"focusout":"blur",K);break;case"focusin":B("focusout",K)}})}function U(){h.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),h=[]}function V(t){var e,n,r=!1;!_.state.isEnabled||G(t)||p||(u=(o=t).currentTarget,L(),!_.state.isVisible&&ne(t)&&Ke.forEach(function(e){return e(t)}),"click"!==t.type||fe(_.props.trigger,"mouseenter")&&!f||!1===_.props.hideOnClick||!_.state.isVisible?(e=(n=C())[0],n=n[1],De.isTouch&&"hold"===e&&n?i=setTimeout(function(){$(t)},n):$(t)):r=!0,"click"===t.type&&(f=!r),r&&X(t))}function H(e){var a,s,t=de(e.target,function(e){return e===l||e===y});"mousemove"===e.type&&t||(t=ce(y.querySelectorAll(Me)).concat(y).map(function(e){var t=e._tippy,n=t.popperChildren.tooltip,t=t.props.interactiveBorder;return{popperRect:e.getBoundingClientRect(),tooltipRect:n.getBoundingClientRect(),interactiveBorder:t}}),a=e.clientX,s=e.clientY,t.every(function(e){var t=e.popperRect,n=e.tooltipRect,r=e.interactiveBorder,i=Math.min(t.top,n.top),o=Math.max(t.right,n.right),e=Math.max(t.bottom,n.bottom),n=Math.min(t.left,n.left);return r element.cloneNode(true)"].join(" ")));t=t.reduce(function(e,t){t=t&&_(t,a);return t&&e.push(t),e},[]);return l(e)?t[0]:t}S.version="5.2.1",S.defaultProps=Te,S.setDefaultProps=o,S.currentInput=De,C.BACKDROP_CLASS="tippy-backdrop",C.ROUND_ARROW='',C._extends=Z,C.arrayFrom=ce,C.closestCallback=de,C.createTippyWithPlugins=function(r){"production"!==We.env.NODE_ENV&&Ee(!0,["createTippyWithPlugins([...]) has been deprecated.","\n\n","Use tippy.setDefaultProps({plugins: [...]}) instead."].join(" "));function e(e,t,n){return void 0===n&&(n=[]),n=(t=void 0===t?{}:t).plugins||n,S(e,Z({},t,{plugins:[].concat(r,n)}))}return e.version="5.2.1",e.defaultProps=Te,e.setDefaultProps=o,e.currentInput=De,e},C.currentInput=De,C.defaultProps=Te,C.div=oe,C.errorWhen=u,C.getBasePlacement=Fe,C.getOwnerDocument=ve,C.hideAll=function(e){var e=void 0===e?{}:e,n=e.exclude,r=e.duration;Ge.forEach(function(e){var t=!1;(t=n?i(n)?e.reference===n:e.popper===n.popper:t)||e.hide(r)})},C.includes=fe,C.isBrowser=e,C.isMouseEvent=ne,C.normalizeToArray=he,C.removeProperties=a,C.setVisibilityState=se,C.tippy=S,C.useIfDefined=me,C.warnWhen=Ee}.call(this)}.call(this,O("_process"))},{_process:4,"popper.js":3}],7:[function(i,e,o){!function(f){!function(){"use strict";Object.defineProperty(o,"__esModule",{value:!0});var y=i("./tippy.chunk.cjs.js");i("popper.js");var d={mouseover:"mouseenter",focusin:"focus",click:"click"},e={name:"animateFill",defaultValue:!1,fn:function(e){var t=e.popperChildren,n=t.tooltip,r=t.content,i=e.props.animateFill?((t=y.div()).className=y.BACKDROP_CLASS,y.setVisibilityState([t],"hidden"),t):null;function o(){e.popperChildren.backdrop=i}return{onCreate:function(){i&&(o(),n.insertBefore(i,n.firstElementChild),n.setAttribute("data-animatefill",""),n.style.overflow="hidden",e.setProps({animation:"shift-away",arrow:!1}))},onMount:function(){var e,t;i&&(e=n.style.transitionDuration,t=Number(e.replace("ms","")),r.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y.setVisibilityState([i],"visible"),"production"!==f.env.NODE_ENV&&(y.warnWhen("absolute"!==getComputedStyle(i).position,"The `tippy.js/dist/backdrop.css` stylesheet has not been\n imported!\n \n The `animateFill` plugin requires this stylesheet to work."),y.warnWhen("none"===getComputedStyle(n).transform,"The `tippy.js/animations/shift-away.css` stylesheet has not\n been imported!\n \n The `animateFill` plugin requires this stylesheet to work.")))},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y.setVisibilityState([i],"hidden")},onAfterUpdate:function(){o()}}}},t={name:"followCursor",defaultValue:!1,fn:function(c){var d,f=c.reference,p=c.popper,m=null,e=y.getOwnerDocument(c.props.triggerTarget||f),n=null,r=!1,i=c.props;function t(){return"manual"===c.props.trigger.trim()}function o(){var e=!!t()||null!==n&&!(0===n.clientX&&0===n.clientY);return c.props.followCursor&&e}function h(){return y.currentInput.isTouch||"initial"===c.props.followCursor&&c.state.isVisible}function a(){c.popperInstance&&m&&(c.popperInstance.reference=m)}function s(){var e,t;!o()&&c.props.placement===i.placement||(t=(e=i.placement).split("-")[1],r=!0,c.setProps({placement:o()&&t?e.replace(t,"start"===t?"end":"start"):e}),r=!1)}function l(){c.popperInstance&&o()&&h()&&c.popperInstance.disableEventListeners()}function u(){o()?e.addEventListener("mousemove",b):a()}function v(){o()&&b(d)}function g(){e.removeEventListener("mousemove",b)}function b(e){var t,n,r,i,o,a,s=d=e,l=s.clientX,u=s.clientY;c.popperInstance&&c.state.currentPlacement&&(s=y.closestCallback(e.target,function(e){return e===f}),e=c.props.followCursor,t="horizontal"===e,n="vertical"===e,r=y.includes(["top","bottom"],y.getBasePlacement(c.state.currentPlacement)),e={size:e=r?p.offsetWidth:p.offsetHeight,x:r?e:0,y:r?0:e},i=e.size,o=e.x,a=e.y,!s&&c.props.interactive||(null===m&&(m=c.popperInstance.reference),c.popperInstance.reference={referenceNode:f,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){var e=f.getBoundingClientRect();return{width:r?i:0,height:r?0:i,top:(t?e.top:u)-a,bottom:(t?e.bottom:u)+a,left:(n?e.left:l)-o,right:(n?e.right:l)+o}}},c.popperInstance.update()),h()&&g())}return{onAfterUpdate:function(e,t){var n;r||(n=t,Object.keys(n).forEach(function(e){i[e]=y.useIfDefined(n[e],i[e])}),t.placement&&s()),t.placement&&l(),requestAnimationFrame(v)},onMount:function(){v(),l()},onShow:function(){t()&&(d=n={clientX:0,clientY:0},s(),u())},onTrigger:function(e,t){n||(y.isMouseEvent(t)&&(n={clientX:t.clientX,clientY:t.clientY},d=t),s(),u())},onUntrigger:function(){c.state.isVisible||(g(),n=null)},onHidden:function(){g(),a(),n=null}}}},n={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t=e.reference;function n(){return e.props.inlinePositioning}return{onHidden:function(){n()&&(e.popperInstance.reference=t)},onShow:function(){n()&&(e.popperInstance.reference={referenceNode:t,clientWidth:0,clientHeight:0,getBoundingClientRect:function(){return function(t,e,n){if(n.length<2||null===t)return e;switch(t){case"top":case"bottom":var r=n[0],i=n[n.length-1],o="top"===t,a=r.top,s=i.bottom,l=(o?r:i).left,i=(o?r:i).right;return{top:a,bottom:s,left:l,right:i,width:i-l,height:s-a};case"left":case"right":var u=Math.min.apply(Math,n.map(function(e){return e.left})),c=Math.max.apply(Math,n.map(function(e){return e.right})),s=n.filter(function(e){return"left"===t?e.left===u:e.right===c}),a=s[0].top,s=s[s.length-1].bottom;return{top:a,bottom:s,left:u,right:c,width:c-u,height:s-a};default:return e}}(e.state.currentPlacement&&y.getBasePlacement(e.state.currentPlacement),t.getBoundingClientRect(),y.arrayFrom(t.getClientRects()))}})}}}},r={name:"sticky",defaultValue:!1,fn:function(n){var r=n.reference,i=n.popper;function o(e){return!0===n.props.sticky||n.props.sticky===e}var a=null,s=null;function l(){var e=o("reference")?(n.popperInstance?n.popperInstance.reference:r).getBoundingClientRect():null,t=o("popper")?i.getBoundingClientRect():null;(e&&u(a,e)||t&&u(s,t))&&n.popperInstance.update(),a=e,s=t,n.state.isMounted&&requestAnimationFrame(l)}return{onMount:function(){n.props.sticky&&l()}}}};function u(e,t){return!e||!t||e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left}o.createTippyWithPlugins=y.createTippyWithPlugins,o.default=y.tippy,o.hideAll=y.hideAll,o.roundArrow=y.ROUND_ARROW,o.animateFill=e,o.createSingleton=function(o,e,t){void 0===e&&(e={}),void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!Array.isArray(o),["The first argument passed to createSingleton() must be an array of tippy","instances. The passed value was",String(o)].join(" ")),t=e.plugins||t,o.forEach(function(e){e.disable()});var a,s,l=y._extends({},y.defaultProps,{},e).aria,n=!1,u=o.map(function(e){return e.reference}),r={fn:function(r){function i(e){var t;a&&(t="aria-"+a,e&&!r.props.interactive?s.setAttribute(t,r.popperChildren.tooltip.id):s.removeAttribute(t))}return{onAfterUpdate:function(e,t){t=t.aria;void 0!==t&&t!==l&&(n?(n=!0,r.setProps({aria:null}),n=!1):l=t)},onDestroy:function(){o.forEach(function(e){e.enable()})},onMount:function(){i(!0)},onUntrigger:function(){i(!1)},onTrigger:function(e,t){var n=t.currentTarget,t=u.indexOf(n);n!==s&&(s=n,a=l,r.state.isVisible&&i(!0),r.popperInstance.reference=n,r.setContent(o[t].props.content))}}}};return y.tippy(y.div(),y._extends({},e,{plugins:[r].concat(t),aria:null,triggerTarget:u}))},o.delegate=function(e,r,t){void 0===t&&(t=[]),"production"!==f.env.NODE_ENV&&y.errorWhen(!(r&&r.target),["You must specity a `target` prop indicating a CSS selector string matching","the target elements that should receive a tippy."].join(" ")),t=r.plugins||t;var i=[],o=[],a=r.target,n=y.removeProperties(r,["target"]),s=y._extends({},n,{plugins:t,trigger:"manual"}),l=y._extends({},n,{plugins:t,showOnCreate:!0}),s=y.tippy(e,s);function u(e){var t,n;!e.target||(n=e.target.closest(a))&&(t=n.getAttribute("data-tippy-trigger")||r.trigger||y.defaultProps.trigger,!y.includes(t,d[e.type])||(n=y.tippy(n,l))&&(o=o.concat(n)))}function c(e,t,n,r){e.addEventListener(t,n,r=void 0===r?!1:r),i.push({node:e,eventType:t,handler:n,options:r})}return y.normalizeToArray(s).forEach(function(e){var t=e.destroy;e.destroy=function(e){(e=void 0===e?!0:e)&&o.forEach(function(e){e.destroy()}),o=[],i.forEach(function(e){var t=e.node,n=e.eventType,r=e.handler,e=e.options;t.removeEventListener(n,r,e)}),i=[],t()},c(e=e.reference,"mouseover",u),c(e,"focusin",u),c(e,"click",u)}),s},o.followCursor=t,o.inlinePositioning=n,o.sticky=r}.call(this)}.call(this,i("_process"))},{"./tippy.chunk.cjs.js":6,_process:4,"popper.js":3}],8:[function(e,t,n){"use strict";var r=a(e("mithril")),i=a(e("./view")),o=a(e("./texte/de"));function a(e){return e&&e.__esModule?e:{default:e}}document,window,domIsReady(function(){window.texte=o.default,r.default.mount(window.document.getElementById("gradido-mithril-user-search"),i.default)})},{"./texte/de":13,"./view":14,mithril:1}],9:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=o(e("mithril")),i=o(e("numeral"));function o(e){return e&&e.__esModule?e:{default:e}}var a=!1;n.default={view:function(e){return(0,r.default)("span",(e=e.attrs.centAmount/1e4,i.default.locale("de"),(0,i.default)(e).format("0,0.00")+" GDD"))},oninit:function(e){a||(i.default.register("locale","de",{delimiters:{thousands:".",decimal:","},currency:{symbol:"€"}}),i.default.locale("de"),a=!0)}}},{mithril:1,numeral:2}],10:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e){return{oncreate:function(e){(0,r.default)(e.dom,{content:e.attrs.accountState.getTooltipText(),placement:"right-start",theme:"rippleUI-theme",arrow:!1})},onremove:function(){},view:function(){return e}}},i(e("mithril"));var r=i(e("tippy.js"));function i(e){return e&&e.__esModule?e:{default:e}}},{mithril:1,"tippy.js":7}],11:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};n.default={view:function(e){return(0,r.default)("div.modal.visible-modal",{tabindex:"-1",role:"dialog"},(0,r.default)("div.modal-dialog",{role:"document"},(0,r.default)("div.modal-content",[(0,r.default)("p.grid-header",e.attrs.title),(0,r.default)("div.modal-body",e.attrs.body),(0,r.default)("div.modal-footer",[(0,r.default)("button.btn.btn-primary",{type:"button",disabled:e.attrs.btnDisabled,"data-dismiss":"modal",onclick:e.attrs.dismiss},"Ok")])])))},oninit:function(e){}}},{mithril:1}],12:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=(e=e("mithril"))&&e.__esModule?e:{default:e};function i(e,t){for(var n=0;n';if(t+='
    '+e.description.title+"
    ",null!=e.description.lines){for(var n in t+="
      ",e.description.lines){var r=e.description.lines[n],i=window.texte.ACCOUNT_STATES.LINES[n],n="mdi ";if("+"===r)n+=" mdi-check color-primary";else if("-"===r)n+=" mdi-close color-danger";else{if("/"!==r)continue;n+=" mdi-alert-circle-outline color-warning"}t+="
    • ",t+='',t+=" ",t+=""+i.title+": "+i[r]+"",t+="
    • "}t+="
    "}return t+"
    "}}]),e=o;function o(e){!function(e){if(!(e instanceof o))throw new TypeError("Cannot call a class as a function")}(this),this.stateName=e,this.texte=window.texte.ACCOUNT_STATES[e]}n.default=e},{mithril:1}],13:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0,n.default={ACCOUNT_STATES:{LINES:[{title:"Login-Server","+":"Das Benutzer-Konto liegt auf dem Login-Server!","-":"Das Benutzer-Konto existiert nicht auf dem Login-Server!"},{title:"Konto Aktivierung","+":"Der Benutzer hat sein Konto aktiviert!","-":"Der Benutzer hat noch nicht auf den Link in seiner E-Mail geklickt!"},{title:"Schlüsselpaar","+":"Es wurde bereits ein Schlüsselpaar für den Benutzer erzeugt und ihm eine Passphrase gezeigt!","-":"Es wurden noch keine Schlüssel und Passphrase für den Benutzer erzeugt!"},{title:"Gemeinschafts-Server","+":"Das Benutzer-Konto wurde auf auf den Gemeinschafts-Server kopiert!","-":"Das Benutzer-Konto wurde noch nicht auf den Gemeinschafts-Server kopiert!"},{title:"Gradidos","+":"Es können Gradidos an den Benutzer geschickt werden!","-":"Es können noch keine Gradidos an den Benutzer geschickt werden!","/":"Es können theoretisch Gradidos an den Benutzer geschickt werden, aber es ist nicht sicher ob er sie verwenden kann!"}],"account created":{title:"Konto angelegt",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung.",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account not on login-server":{title:"Konto nicht auf Login-Server",color:"danger",description:{title:"Das Konto existiert nicht (mehr) auf dem Login-Server: ein Fehler oder es wurde nur teilweise gelöscht.",lines:["-","+","+","+","/"]},todo:{title:"Wenn der Benutzer seine Passphrase hat, kann er sein Konto auf dem Login-Server wiederherstellen, ansonsten bleibt eigentlich nur das Konto auf dem Gemeinschafts-Server zu löschen",lines:["Nachdem das Konto auf dem Login-Server kopiert wurde, bekommt der Benutzer automatisch eine E-Mail mit einem Link zur Kontoaktivierung zugeschickt.","{{copy-from-community-to-login-server}}","
    ","Es wird nur der Benutzer gelöscht, nicht seine Transaktionen. Zu diesen hat er wieder Zugriff wenn er sein Konto mit Hilfe seiner Passphrase wiederhergestellt hat.","{{user-transactions-overview}}","{{delete-from-community-server}}"]}},"email activated":{title:"Konto aktiviert",color:"primary",description:{title:"Das Konto wurde vom Benutzer aktiviert und Schlüssel erzeugt, aber noch nicht auf diesen Gemeinschafts-Server kopiert.",lines:["+","+","+","-","-"]},todo:{title:"Das Benutzer Konto müsste vom Login-Server auf den Gemeinschafts-Server kopiert werden.",lines:["{{copy-from-login-to-community-server}}"]}},"account copied to community":{title:"Konto auf Gemeinschafts-Server",color:"success",description:{title:"Das Konto ist vollständig aktiviert und es können Gradidos überwiesen werden.",lines:["+","+","+","+","+"]}},"email not activated":{title:"Konto nicht aktiviert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, vermutlich durch eine elopage-Anmeldung. Es wurde aber noch nicht aktiviert!",lines:["+","-","-","-","-"]},todo:{title:"Als nächstes müsste der Benutzer den Link in seiner E-Mail anklicken und den Anweisungen folgen. ",lines:["Stimmt die E-Mail Adresse? Hat der Benutzer in seinem Spam-Verzeichnis nachgesehen?","{{verification-resend}}","{{mailto-verification-resend}}"]}},"account multiple times on login-server":{title:"Konto mehrfach vorhanden",color:"danger",description:{title:"Das Konto ist auf dem Login-Server mehrfach vorhanden, das sollte nicht sein, auf dem Login-Server muss ein Fehler passiert sein!!"},todo:{title:"Du solltest mit dem Entwickler in Kontakt treten.",lines:["{{mailto-developer}}"]}},"account not on community server":{title:"Konto nicht auf Gemeinschafts-Server",color:"secondary",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber existiert noch nicht auf dem Gemeinschafts-Server!",lines:["+",null,"-","-","-"]},todo:{title:"Das sollte eigentlich nicht passieren, du solltest mit dem Entwickler sprechen.",lines:["{{mailto-developer}}"]}},"no keys":{title:"Keine Schlüssel generiert",color:"warning",description:{title:"Das Konto wurde auf dem Login-Server angelegt, aber es wurden noch keine Schlüssel generiert!",lines:["+","+","-","-","-"]},todo:{title:"Der Benutzer muss sich mit seinen Daten einloggen, dann müsste er automatisch durch die Schlüsselgenerierung/Konto-Wiederherstellung geführt werden",lines:["{{mailto-user-login}}","Wenn der Benutzer sein Passwort vergessen hat, kann er sich auch eine Passwort Reset E-Mail zuschicken lassen.","{{reset-password}}","{{mailto-reset-password}}"]}}},NO_USER_FOUND:"Keine Benutzer gefunden",USER_FOUND:"Benutzer gefunden",NAME:"Name",EMAIL:"E-Mail",BALANCE:"Kontostand",PUBLIC_KEY:"Öffentlicher
    Schlüssel",CREATED:"Erstellt",COPY_FROM_LOGIN_TO_COMMUNITY:"Vom Login-Server zum Gemeinschafts-Server kopieren",COPY_FROM_LOGIN_TO_COMMUNITY_SUCCESS:"Kontodaten wurden erfolgreich vom Login-Server zu diesem Gemeinschafts-Server kopiert!",COPY_FROM_COMMUNITY_TO_LOGIN:"Vom Gemeinschafts-Server zum Login-Server kopieren",COPY_FROM_COMMUNITY_TO_LOGIN_SUCCESS:"Kontodaten wurden erfolgreich vom Gemeinschafts-Server zum Login-Server kopiert!",DELETE_FROM_COMMUNITY:"Benutzer Konto vom Gemeinschafts-Server löschen",DELETE_FROM_COMMUNITY_SUCCESS:"Benutzer Konto vom Gemeinschafts-Server erfolgreich gelöscht",VERIFICATION_EMAIL_RESEND:"Verification Email erneut zusenden",VERIFICATION_EMAIL_RESEND_SUCCESS:"Verification Email wird erneut zugestellt",COPY_FAILED:"Fehler beim Kopieren",DELETE_FAILED:"Fehler beim löschen",RESEND_FAILED:"Senden fehlgeschlagen",AJAX_CRITICAL:"Kritischer Fehler beim Ajax-Request",COPY_IN_PROGRESS:"Wird kopiert",DELETE_IN_PROGRESS:"Wird gelöscht",RESEND_IN_PROGRESS:"Wird gesendet",RECEIVE_TRANSACTIONS_COUNT:"Erhaltene Transaktionen: ",SENDED_TRANSACTIONS_COUNT:"gesendete Transaktionen: ",CREATION_TRANSACTIONS_COUNT:"erhaltende Schöpfungs-Transaktionen: "}},{}],14:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r=a(e("mithril")),i=a(e("./userTable")),o=a(e("../model/AccountState"));function a(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(e){return void 0===g_users?(0,r.default)(""):0"===e)return i.default.trust("

    ");if(t){var n=function(){switch(t[1]){case"copy-from-login-to-community-server":return o.default;case"user-transactions-overview":return a.default;case"copy-from-community-to-login-server":return s.default;case"verification-resend":return l.default}return null}();return n?(0,i.default)("li",(0,i.default)(n,{user:r.attrs.user,updateState:r.attrs.updateState,deleteUser:r.attrs.deleteUser})):(0,i.default)("li",e)}return(0,i.default)("li",e)})]):null]))},oninit:function(e){e.state.accountStateTexte=window.texte.ACCOUNT_STATES[e.attrs.user.indicator.name]}}},{"./actions/copyCommunityLogin":16,"./actions/copyLoginCommunity":17,"./actions/deleteCommunityServer":18,"./actions/userTransactionsOverview":19,"./actions/verificationResend":20,mithril:1}],23:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var d=r(e("mithril")),f=r(e("../../model/AccountState")),p=r(e("../../lib/Gradido")),m=r(e("../../lib/Tooltip")),h=r(e("../../lib/dialog"));function r(e){return e&&e.__esModule?e:{default:e}}n.default={view:function(t){var e=new f.default(t.attrs.user.indicator.name),n=t.attrs.user,r=e.getColor(),i=e.getTitle(),o=n.pubkeyhex;""!==o&&(o=n.pubkeyhex.substring(0,10)+"...");var a=n.created;a&&(a=(c=new Date(a)).toLocaleDateString()+"
    "+c.toLocaleTimeString());var s=!1;e.hasActions()&&(s=e.getRawColor(),s="primary");var l="down";t.attrs.open&&(l="up");var u=n.email,c=!1;return 25 Date: Fri, 26 Mar 2021 11:46:35 +0000 Subject: [PATCH 15/44] fix temporaer calculating error --- community_server/src/Model/Entity/StateBalance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community_server/src/Model/Entity/StateBalance.php b/community_server/src/Model/Entity/StateBalance.php index b1c237a3b..c5fcf5976 100644 --- a/community_server/src/Model/Entity/StateBalance.php +++ b/community_server/src/Model/Entity/StateBalance.php @@ -68,7 +68,7 @@ class StateBalance extends Entity if($decay_duration <= 0) { return $this->amount; } - return $this->amount; + return 0; //return $this->amount * pow(0.99999997802044727, $decay_duration); } From be549b7501100933d8e0de53058bdaad8fabbd29 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 15:30:03 +0200 Subject: [PATCH 16/44] create docker container for build and offer protoc v3.9.1 to prevent rebuilding protoc every time --- login_server/Dockerfile.debug | 13 ---------- login_server/Dockerfiles/Dockefile.protoc | 30 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 login_server/Dockerfiles/Dockefile.protoc diff --git a/login_server/Dockerfile.debug b/login_server/Dockerfile.debug index e5603bbc1..15d78b0dd 100644 --- a/login_server/Dockerfile.debug +++ b/login_server/Dockerfile.debug @@ -1,17 +1,4 @@ -######################################################################################################### -# Build protoc -######################################################################################################### -FROM gcc:7.5 as protoc_build -RUN git clone --recurse-submodules https://github.com/protocolbuffers/protobuf.git -WORKDIR /protobuf -RUN git checkout v3.9.1 -RUN ./autogen.sh -RUN ./configure --enable-static=yes -RUN make -j$(nproc) -#RUN make check - -CMD ["./protobuf"] ######################################################################################################### # debug build preparation diff --git a/login_server/Dockerfiles/Dockefile.protoc b/login_server/Dockerfiles/Dockefile.protoc new file mode 100644 index 000000000..92cf80c2a --- /dev/null +++ b/login_server/Dockerfiles/Dockefile.protoc @@ -0,0 +1,30 @@ +######################################################################################################### +# Build protoc +######################################################################################################### +FROM gcc:7.5 as protoc3.9.1_build +RUN git clone --recurse-submodules https://github.com/protocolbuffers/protobuf.git +WORKDIR /protobuf + +RUN git checkout v3.9.1 +RUN ./autogen.sh +RUN ./configure --enable-static=yes +RUN make -j$(nproc) +#RUN make check + +CMD ["./protobuf"] + +######################################################################################################### +# Store protoc +######################################################################################################### +FROM alpine:3.10 as protoc + +COPY --from=protoc3.9.1_build /protobuf/src/.libs/protoc /usr/bin/ +COPY --from=protoc3.9.1_build /protobuf/src/.libs/libprotobuf.so.20.0.1 /usr/lib/libprotobuf.so.20 +COPY --from=protoc3.9.1_build /protobuf/src/.libs/libprotoc.so.20.0.1 /usr/lib/libprotoc.so.20 +COPY --from=protoc3.9.1_build /protobuf/src/google/protobuf/*.proto /usr/include/google/protobuf/ +COPY --from=protoc3.9.1_build /protobuf/src/google/protobuf/*.h /usr/include/google/protobuf/ + +## build with: +# docker build . -f Dockefile.protoc -t unicorny/protoc:3.9.1 +## upload (login to docker hub on shell before): +# docker push unicorny/protoc:3.9.1 \ No newline at end of file From de08bfe4594ac882f369c0570bec18ae61a3470a Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 29 Mar 2021 15:45:11 +0200 Subject: [PATCH 17/44] get tests workig including boostrap vue and element UI --- frontend/.babelrc | 22 - frontend/babel.config.js | 2 +- frontend/jest.config.js | 11 +- frontend/package.json | 6 + .../src/views/Layout/ContentFooter.spec.js | 30 + frontend/test/testSetup.js | 8 + frontend/yarn.lock | 1736 ++++++++++++++++- 7 files changed, 1695 insertions(+), 120 deletions(-) delete mode 100644 frontend/.babelrc create mode 100644 frontend/src/views/Layout/ContentFooter.spec.js create mode 100644 frontend/test/testSetup.js diff --git a/frontend/.babelrc b/frontend/.babelrc deleted file mode 100644 index af8a46469..000000000 --- a/frontend/.babelrc +++ /dev/null @@ -1,22 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env" - ] - ], - "env": { - "test": { - "plugins": ["require-context-hook"], - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "10" - } - } - ] - ] - } - } -} \ No newline at end of file diff --git a/frontend/babel.config.js b/frontend/babel.config.js index fdc4e9bd7..faf55f74f 100644 --- a/frontend/babel.config.js +++ b/frontend/babel.config.js @@ -1,5 +1,5 @@ module.exports = { - presets: ['@vue/app'], + presets: ['@babel/preset-env'], plugins: [ [ 'component', diff --git a/frontend/jest.config.js b/frontend/jest.config.js index 20b610b66..cc261f68c 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -10,16 +10,15 @@ module.exports = { coverageReporters: ['lcov'], moduleNameMapper: { '^@/(.*)$': '/src/$1', + '\\.(css|less)$': 'identity-obj-proxy', }, transform: { - '^.+\\.vue$': 'vue-jest', - // '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', - '^.+\\.(js|jsx)?$': 'babel-jest', + '^.+\\.vue$': '/node_modules/vue-jest', + '^.+\\.(js|jsx)?$': '/node_modules/babel-jest', }, - //setupFiles: [ - // "/test/registerContext.js" - //], + setupFiles: ['/test/testSetup.js'], testMatch: ['**/?(*.)+(spec|test).js?(x)'], // snapshotSerializers: ['jest-serializer-vue'], transformIgnorePatterns: ['/node_modules/'], + preset: '@vue/cli-plugin-unit-jest', } diff --git a/frontend/package.json b/frontend/package.json index 2a470f312..9243a65cc 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,11 +12,16 @@ "test": "jest" }, "dependencies": { + "@babel/core": "^7.13.13", + "@babel/node": "^7.13.13", + "@babel/preset-env": "^7.13.12", + "@vue/cli-plugin-unit-jest": "^4.5.12", "@vue/test-utils": "^1.1.3", "axios": "^0.21.1", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^26.6.3", "babel-plugin-require-context-hook": "^1.0.0", + "babel-preset-vue": "^2.0.2", "bootstrap": "4.3.1", "bootstrap-vue": "^2.5.0", "chart.js": "^2.9.3", @@ -41,6 +46,7 @@ "flatpickr": "^4.5.7", "fuse.js": "^3.2.0", "google-maps": "^3.2.1", + "identity-obj-proxy": "^3.0.0", "jest": "^26.6.3", "nouislider": "^12.1.0", "particles-bg-vue": "1.2.3", diff --git a/frontend/src/views/Layout/ContentFooter.spec.js b/frontend/src/views/Layout/ContentFooter.spec.js new file mode 100644 index 000000000..ea985a336 --- /dev/null +++ b/frontend/src/views/Layout/ContentFooter.spec.js @@ -0,0 +1,30 @@ +import { mount } from '@vue/test-utils' + +import ContentFooter from './ContentFooter' + +const localVue = global.localVue + +describe('ContentFooter', () => { + let wrapper + + let mocks = { + $i18n: { + locale: () => 'en', + }, + $t: jest.fn((t) => t), + } + + const Wrapper = () => { + return mount(ContentFooter, { localVue, mocks }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the content footer', () => { + expect(wrapper.find('footer.footer')).toBeDefined() + }) + }) +}) diff --git a/frontend/test/testSetup.js b/frontend/test/testSetup.js new file mode 100644 index 000000000..91c018096 --- /dev/null +++ b/frontend/test/testSetup.js @@ -0,0 +1,8 @@ +import { createLocalVue } from '@vue/test-utils' +import ElementUI from 'element-ui' +import BootstrapVue from 'bootstrap-vue' + +global.localVue = createLocalVue() + +global.localVue.use(ElementUI) +global.localVue.use(BootstrapVue) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 1f1bae1af..7640ea778 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -14,6 +14,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.6.tgz#11972d07db4c2317afdbf41d6feb3a730301ef4e" integrity sha512-VhgqKOWYVm7lQXlvbJnWOzwfAQATd2nV52koT0HZ/LdDH0m4DUDwkKYsH+IwpXb+bKPyBJzawA4I6nBKqZcpQw== +"@babel/compat-data@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1" + integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== + "@babel/compat-data@^7.13.8": version "7.13.11" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.11.tgz#9c8fe523c206979c9a81b1e12fe50c1254f1aa35" @@ -63,6 +68,28 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.11.0", "@babel/core@^7.13.13": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.13.tgz#bc44c4a2be2288ec4ddf56b66fc718019c76ac29" + integrity sha512-1xEs9jZAyKIouOoCmpsgk/I26PoKyvzQ2ixdRpRzfbcp1fL+ozw7TUgdDgwonbTovqRaTfRh50IXuw4QrWO0GA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-compilation-targets" "^7.13.13" + "@babel/helper-module-transforms" "^7.13.12" + "@babel/helpers" "^7.13.10" + "@babel/parser" "^7.13.13" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.13" + "@babel/types" "^7.13.13" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^6.3.0" + source-map "^0.5.0" + "@babel/generator@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.0.tgz#bd00d4394ca22f220390c56a0b5b85568ec1ec0c" @@ -72,7 +99,7 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.13.9": +"@babel/generator@^7.13.9", "@babel/generator@^7.4.0": version "7.13.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== @@ -116,6 +143,16 @@ browserslist "^4.14.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.13.13", "@babel/helper-compilation-targets@^7.13.8": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5" + integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== + dependencies: + "@babel/compat-data" "^7.13.12" + "@babel/helper-validator-option" "^7.12.17" + browserslist "^4.14.5" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.0.tgz#28d04ad9cfbd1ed1d8b988c9ea7b945263365846" @@ -172,7 +209,7 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-hoist-variables@^7.12.13": +"@babel/helper-hoist-variables@^7.12.13", "@babel/helper-hoist-variables@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz#5d5882e855b5c5eda91e0cadc26c6e7a2c8593d8" integrity sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g== @@ -187,6 +224,13 @@ dependencies: "@babel/types" "^7.13.0" +"@babel/helper-member-expression-to-functions@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" + integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== + dependencies: + "@babel/types" "^7.13.12" + "@babel/helper-module-imports@7.0.0-beta.35": version "7.0.0-beta.35" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.35.tgz#308e350e731752cdb4d0f058df1d704925c64e0a" @@ -202,6 +246,13 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-module-imports@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== + dependencies: + "@babel/types" "^7.13.12" + "@babel/helper-module-transforms@^7.12.13", "@babel/helper-module-transforms@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1" @@ -217,6 +268,20 @@ "@babel/types" "^7.13.0" lodash "^4.17.19" +"@babel/helper-module-transforms@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.12.tgz#600e58350490828d82282631a1422268e982ba96" + integrity sha512-7zVQqMO3V+K4JOOj40kxiCrMf6xlQAkewBB0eu2b03OO/Q21ZutOzjpfD79A5gtE/2OWi1nv625MrDlGlkbknQ== + dependencies: + "@babel/helper-module-imports" "^7.13.12" + "@babel/helper-replace-supers" "^7.13.12" + "@babel/helper-simple-access" "^7.13.12" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.12" + "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" @@ -224,7 +289,7 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== @@ -248,6 +313,16 @@ "@babel/traverse" "^7.13.0" "@babel/types" "^7.13.0" +"@babel/helper-replace-supers@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804" + integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.13.12" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.12" + "@babel/helper-simple-access@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4" @@ -255,6 +330,13 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-simple-access@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" + integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== + dependencies: + "@babel/types" "^7.13.12" + "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" @@ -316,6 +398,18 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/node@^7.13.13": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.13.13.tgz#9201214dab4ae41c1e1596b7d2e27098395b1509" + integrity sha512-gElSPunpriXoBGQxDkd5h9L13SVTyzFLTPv9jN1aXJNLR10iNs+MsfhYL/WGJGCJQFddHAdThY7CkmGVz2KPag== + dependencies: + "@babel/register" "^7.13.8" + commander "^4.0.1" + core-js "^3.2.1" + node-environment-flags "^1.0.5" + regenerator-runtime "^0.13.4" + v8flags "^3.1.1" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.10": version "7.13.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.11.tgz#f93ebfc99d21c1772afbbaa153f47e7ce2f50b88" @@ -326,6 +420,29 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.4.tgz#340211b0da94a351a6f10e63671fa727333d13ab" integrity sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA== +"@babel/parser@^7.13.13", "@babel/parser@^7.4.3": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" + integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" + integrity sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + +"@babel/plugin-proposal-async-generator-functions@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz#87aacb574b3bc4b5603f6fe41458d72a5a2ec4b1" + integrity sha512-rPBnhj+WgoSmgq+4gQUtXx/vOcU+UYtjy1AA/aeD61Hwj410fwYyqfUcRP3lR8ucgliVJL/G7sXcNUecC75IXA== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-remap-async-to-generator" "^7.13.0" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.13.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.5.tgz#69e3fbb9958949b09036e27b26eba1aafa1ba3db" @@ -335,7 +452,7 @@ "@babel/helper-remap-async-to-generator" "^7.13.0" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.0.0": +"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz#146376000b94efd001e57a40a88a525afaab9f37" integrity sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg== @@ -352,6 +469,30 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-decorators" "^7.12.13" +"@babel/plugin-proposal-dynamic-import@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz#876a1f6966e1dec332e8c9451afda3bebcdf2e1d" + integrity sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz#393be47a4acd03fa2af6e3cde9b06e33de1b446d" + integrity sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz#bf1fb362547075afda3634ed31571c5901afef7b" + integrity sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-proposal-json-strings@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.13.tgz#ced7888a2db92a3d520a2e35eb421fdb7fcc9b5d" @@ -360,6 +501,41 @@ "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-json-strings" "^7.8.0" +"@babel/plugin-proposal-logical-assignment-operators@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz#93fa78d63857c40ce3c8c3315220fd00bfbb4e1a" + integrity sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz#3730a31dafd3c10d8ccd10648ed80a2ac5472ef3" + integrity sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz#bd9da3188e787b5120b4f9d465a8261ce67ed1db" + integrity sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz#5d210a4d727d6ce3b18f9de82cc99a3964eed60a" + integrity sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g== + dependencies: + "@babel/compat-data" "^7.13.8" + "@babel/helper-compilation-targets" "^7.13.8" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.13.0" + "@babel/plugin-proposal-object-rest-spread@^7.3.4": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.0.tgz#8f19ad247bb96bd5ad2d4107e6eddfe0a789937b" @@ -369,6 +545,14 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.13.0" +"@babel/plugin-proposal-optional-catch-binding@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz#3ad6bd5901506ea996fc31bdcf3ccfa2bed71107" + integrity sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-catch-binding@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.13.tgz#4640520afe57728af14b4d1574ba844f263bcae5" @@ -377,7 +561,24 @@ "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.2.0": +"@babel/plugin-proposal-optional-chaining@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz#ba9feb601d422e0adea6760c2bd6bbb7bfec4866" + integrity sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz#04bd4c6d40f6e6bbfa2f57e2d8094bad900ef787" + integrity sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.13.0" + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.12.13", "@babel/plugin-proposal-unicode-property-regex@^7.2.0", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz#bebde51339be829c17aaaaced18641deb62b39ba" integrity sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg== @@ -399,7 +600,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== @@ -413,13 +614,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-dynamic-import@^7.0.0": +"@babel/plugin-syntax-dynamic-import@^7.0.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -441,7 +649,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -455,14 +663,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@^7.2.0", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== @@ -483,21 +691,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-top-level-await@^7.12.13", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-arrow-functions@^7.2.0": +"@babel/plugin-transform-arrow-functions@^7.13.0", "@babel/plugin-transform-arrow-functions@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz#10a59bebad52d637a027afa692e8d5ceff5e3dae" integrity sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg== dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-async-to-generator@^7.3.4": +"@babel/plugin-transform-async-to-generator@^7.13.0", "@babel/plugin-transform-async-to-generator@^7.3.4": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz#8e112bf6771b82bf1e974e5e26806c5c99aa516f" integrity sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg== @@ -506,21 +714,21 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-remap-async-to-generator" "^7.13.0" -"@babel/plugin-transform-block-scoped-functions@^7.2.0": +"@babel/plugin-transform-block-scoped-functions@^7.12.13", "@babel/plugin-transform-block-scoped-functions@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz#a9bf1836f2a39b4eb6cf09967739de29ea4bf4c4" integrity sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-block-scoping@^7.3.4": +"@babel/plugin-transform-block-scoping@^7.12.13", "@babel/plugin-transform-block-scoping@^7.3.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz#f36e55076d06f41dfd78557ea039c1b581642e61" integrity sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-classes@^7.3.4": +"@babel/plugin-transform-classes@^7.13.0", "@babel/plugin-transform-classes@^7.3.4": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz#0265155075c42918bf4d3a4053134176ad9b533b" integrity sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g== @@ -533,21 +741,21 @@ "@babel/helper-split-export-declaration" "^7.12.13" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.2.0": +"@babel/plugin-transform-computed-properties@^7.13.0", "@babel/plugin-transform-computed-properties@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz#845c6e8b9bb55376b1fa0b92ef0bdc8ea06644ed" integrity sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg== dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-destructuring@^7.2.0": +"@babel/plugin-transform-destructuring@^7.13.0", "@babel/plugin-transform-destructuring@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz#c5dce270014d4e1ebb1d806116694c12b7028963" integrity sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA== dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-dotall-regex@^7.2.0": +"@babel/plugin-transform-dotall-regex@^7.12.13", "@babel/plugin-transform-dotall-regex@^7.2.0", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad" integrity sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ== @@ -555,14 +763,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-duplicate-keys@^7.2.0": +"@babel/plugin-transform-duplicate-keys@^7.12.13", "@babel/plugin-transform-duplicate-keys@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz#6f06b87a8b803fd928e54b81c258f0a0033904de" integrity sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-exponentiation-operator@^7.2.0": +"@babel/plugin-transform-exponentiation-operator@^7.12.13", "@babel/plugin-transform-exponentiation-operator@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz#4d52390b9a273e651e4aba6aee49ef40e80cd0a1" integrity sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA== @@ -570,14 +778,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-for-of@^7.2.0": +"@babel/plugin-transform-for-of@^7.13.0", "@babel/plugin-transform-for-of@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz#c799f881a8091ac26b54867a845c3e97d2696062" integrity sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg== dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-function-name@^7.2.0": +"@babel/plugin-transform-function-name@^7.12.13", "@babel/plugin-transform-function-name@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz#bb024452f9aaed861d374c8e7a24252ce3a50051" integrity sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ== @@ -585,14 +793,21 @@ "@babel/helper-function-name" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-literals@^7.2.0": +"@babel/plugin-transform-literals@^7.12.13", "@babel/plugin-transform-literals@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz#2ca45bafe4a820197cf315794a4d26560fe4bdb9" integrity sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-modules-amd@^7.2.0": +"@babel/plugin-transform-member-expression-literals@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz#5ffa66cd59b9e191314c9f1f803b938e8c081e40" + integrity sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-modules-amd@^7.13.0", "@babel/plugin-transform-modules-amd@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz#19f511d60e3d8753cc5a6d4e775d3a5184866cc3" integrity sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ== @@ -601,6 +816,16 @@ "@babel/helper-plugin-utils" "^7.13.0" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.9.6": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b" + integrity sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw== + dependencies: + "@babel/helper-module-transforms" "^7.13.0" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-simple-access" "^7.12.13" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-commonjs@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.0.tgz#276932693a20d12c9776093fdc99c0d9995e34c6" @@ -611,6 +836,17 @@ "@babel/helper-simple-access" "^7.12.13" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-systemjs@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3" + integrity sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A== + dependencies: + "@babel/helper-hoist-variables" "^7.13.0" + "@babel/helper-module-transforms" "^7.13.0" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-validator-identifier" "^7.12.11" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-systemjs@^7.3.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.13.tgz#351937f392c7f07493fc79b2118201d50404a3c5" @@ -622,7 +858,7 @@ "@babel/helper-validator-identifier" "^7.12.11" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.2.0": +"@babel/plugin-transform-modules-umd@^7.13.0", "@babel/plugin-transform-modules-umd@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz#8a3d96a97d199705b9fd021580082af81c06e70b" integrity sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw== @@ -630,21 +866,21 @@ "@babel/helper-module-transforms" "^7.13.0" "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-named-capturing-groups-regex@^7.3.0": +"@babel/plugin-transform-named-capturing-groups-regex@^7.12.13", "@babel/plugin-transform-named-capturing-groups-regex@^7.3.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz#2213725a5f5bbbe364b50c3ba5998c9599c5c9d9" integrity sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.12.13" -"@babel/plugin-transform-new-target@^7.0.0": +"@babel/plugin-transform-new-target@^7.0.0", "@babel/plugin-transform-new-target@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz#e22d8c3af24b150dd528cbd6e685e799bf1c351c" integrity sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-object-super@^7.2.0": +"@babel/plugin-transform-object-super@^7.12.13", "@babel/plugin-transform-object-super@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz#b4416a2d63b8f7be314f3d349bd55a9c1b5171f7" integrity sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ== @@ -659,13 +895,27 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-regenerator@^7.3.4": +"@babel/plugin-transform-property-literals@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz#4e6a9e37864d8f1b3bc0e2dce7bf8857db8b1a81" + integrity sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-regenerator@^7.12.13", "@babel/plugin-transform-regenerator@^7.3.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz#b628bcc9c85260ac1aeb05b45bde25210194a2f5" integrity sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA== dependencies: regenerator-transform "^0.14.2" +"@babel/plugin-transform-reserved-words@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz#7d9988d4f06e0fe697ea1d9803188aa18b472695" + integrity sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-transform-runtime@^7.4.0": version "7.13.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.7.tgz#a856b53681da9193d98dfb03d8192b0a3f4cdd8a" @@ -678,14 +928,14 @@ babel-plugin-polyfill-regenerator "^0.1.2" semver "7.0.0" -"@babel/plugin-transform-shorthand-properties@^7.2.0": +"@babel/plugin-transform-shorthand-properties@^7.12.13", "@babel/plugin-transform-shorthand-properties@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz#db755732b70c539d504c6390d9ce90fe64aff7ad" integrity sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-spread@^7.2.0": +"@babel/plugin-transform-spread@^7.13.0", "@babel/plugin-transform-spread@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz#84887710e273c1815ace7ae459f6f42a5d31d5fd" integrity sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg== @@ -693,28 +943,35 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" -"@babel/plugin-transform-sticky-regex@^7.2.0": +"@babel/plugin-transform-sticky-regex@^7.12.13", "@babel/plugin-transform-sticky-regex@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz#760ffd936face73f860ae646fb86ee82f3d06d1f" integrity sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-template-literals@^7.2.0": +"@babel/plugin-transform-template-literals@^7.13.0", "@babel/plugin-transform-template-literals@^7.2.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz#a36049127977ad94438dee7443598d1cefdf409d" integrity sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw== dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-typeof-symbol@^7.2.0": +"@babel/plugin-transform-typeof-symbol@^7.12.13", "@babel/plugin-transform-typeof-symbol@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz#785dd67a1f2ea579d9c2be722de8c84cb85f5a7f" integrity sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-unicode-regex@^7.2.0": +"@babel/plugin-transform-unicode-escapes@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz#840ced3b816d3b5127dd1d12dcedc5dead1a5e74" + integrity sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-unicode-regex@^7.12.13", "@babel/plugin-transform-unicode-regex@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz#b52521685804e155b1202e83fc188d34bb70f5ac" integrity sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA== @@ -771,6 +1028,103 @@ js-levenshtein "^1.1.3" semver "^5.3.0" +"@babel/preset-env@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.12.tgz#6dff470478290582ac282fb77780eadf32480237" + integrity sha512-JzElc6jk3Ko6zuZgBtjOd01pf9yYDEIH8BcqVuYIuOkzOwDesoa/Nz4gIo4lBG6K861KTV9TvIgmFuT6ytOaAA== + dependencies: + "@babel/compat-data" "^7.13.12" + "@babel/helper-compilation-targets" "^7.13.10" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-validator-option" "^7.12.17" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12" + "@babel/plugin-proposal-async-generator-functions" "^7.13.8" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-dynamic-import" "^7.13.8" + "@babel/plugin-proposal-export-namespace-from" "^7.12.13" + "@babel/plugin-proposal-json-strings" "^7.13.8" + "@babel/plugin-proposal-logical-assignment-operators" "^7.13.8" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-numeric-separator" "^7.12.13" + "@babel/plugin-proposal-object-rest-spread" "^7.13.8" + "@babel/plugin-proposal-optional-catch-binding" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-proposal-private-methods" "^7.13.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.12.13" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.12.13" + "@babel/plugin-transform-arrow-functions" "^7.13.0" + "@babel/plugin-transform-async-to-generator" "^7.13.0" + "@babel/plugin-transform-block-scoped-functions" "^7.12.13" + "@babel/plugin-transform-block-scoping" "^7.12.13" + "@babel/plugin-transform-classes" "^7.13.0" + "@babel/plugin-transform-computed-properties" "^7.13.0" + "@babel/plugin-transform-destructuring" "^7.13.0" + "@babel/plugin-transform-dotall-regex" "^7.12.13" + "@babel/plugin-transform-duplicate-keys" "^7.12.13" + "@babel/plugin-transform-exponentiation-operator" "^7.12.13" + "@babel/plugin-transform-for-of" "^7.13.0" + "@babel/plugin-transform-function-name" "^7.12.13" + "@babel/plugin-transform-literals" "^7.12.13" + "@babel/plugin-transform-member-expression-literals" "^7.12.13" + "@babel/plugin-transform-modules-amd" "^7.13.0" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/plugin-transform-modules-systemjs" "^7.13.8" + "@babel/plugin-transform-modules-umd" "^7.13.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.13" + "@babel/plugin-transform-new-target" "^7.12.13" + "@babel/plugin-transform-object-super" "^7.12.13" + "@babel/plugin-transform-parameters" "^7.13.0" + "@babel/plugin-transform-property-literals" "^7.12.13" + "@babel/plugin-transform-regenerator" "^7.12.13" + "@babel/plugin-transform-reserved-words" "^7.12.13" + "@babel/plugin-transform-shorthand-properties" "^7.12.13" + "@babel/plugin-transform-spread" "^7.13.0" + "@babel/plugin-transform-sticky-regex" "^7.12.13" + "@babel/plugin-transform-template-literals" "^7.13.0" + "@babel/plugin-transform-typeof-symbol" "^7.12.13" + "@babel/plugin-transform-unicode-escapes" "^7.12.13" + "@babel/plugin-transform-unicode-regex" "^7.12.13" + "@babel/preset-modules" "^0.1.4" + "@babel/types" "^7.13.12" + babel-plugin-polyfill-corejs2 "^0.1.4" + babel-plugin-polyfill-corejs3 "^0.1.3" + babel-plugin-polyfill-regenerator "^0.1.2" + core-js-compat "^3.9.0" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" + integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/register@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.13.8.tgz#d9051dc6820cb4e86375cc0e2d55a4862b31184f" + integrity sha512-yCVtABcmvQjRsX2elcZFUV5Q5kDDpHdtXKKku22hNDma60lYuhKmtp1ykZ/okRCPLT2bR5S+cA1kvtBdAFlDTQ== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.19" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + "@babel/runtime-corejs2@^7.2.0": version "7.13.7" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.13.7.tgz#0d1a7770aca474a2874064230afe62e830c059d8" @@ -786,7 +1140,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.12.13", "@babel/template@^7.3.3": +"@babel/template@^7.12.13", "@babel/template@^7.3.3", "@babel/template@^7.4.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== @@ -810,6 +1164,20 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.13.13", "@babel/traverse@^7.4.3": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" + integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.13" + "@babel/types" "^7.13.13" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@7.0.0-beta.35": version "7.0.0-beta.35" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.35.tgz#cf933a9a9a38484ca724b335b88d83726d5ab960" @@ -828,6 +1196,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.4.0", "@babel/types@^7.4.4": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.13.tgz#dcd8b815b38f537a3697ce84c8e3cc62197df96f" + integrity sha512-kt+EpC6qDfIaqlP+DIbIJOclYy/A1YXs9dAf/ljbi+39Bcbc073H6jKVpXEr/EoIh5anGn5xq/yRVzKl+uIc9w== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -898,6 +1275,15 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jest/console@^24.7.1", "@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + "@jest/console@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" @@ -910,6 +1296,40 @@ jest-util "^26.6.2" slash "^3.0.0" +"@jest/core@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" + integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== + dependencies: + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-changed-files "^24.9.0" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-resolve-dependencies "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + jest-watcher "^24.9.0" + micromatch "^3.1.10" + p-each-series "^1.0.0" + realpath-native "^1.1.0" + rimraf "^2.5.4" + slash "^2.0.0" + strip-ansi "^5.0.0" + "@jest/core@^26.6.3": version "26.6.3" resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" @@ -944,6 +1364,16 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/environment@^24.3.0", "@jest/environment@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" + integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== + dependencies: + "@jest/fake-timers" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + "@jest/environment@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" @@ -954,6 +1384,15 @@ "@types/node" "*" jest-mock "^26.6.2" +"@jest/fake-timers@^24.3.0", "@jest/fake-timers@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" + integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== + dependencies: + "@jest/types" "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -975,6 +1414,33 @@ "@jest/types" "^26.6.2" expect "^26.6.2" +"@jest/reporters@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" + integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + istanbul-lib-coverage "^2.0.2" + istanbul-lib-instrument "^3.0.1" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.1" + istanbul-reports "^2.2.6" + jest-haste-map "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + node-notifier "^5.4.2" + slash "^2.0.0" + source-map "^0.6.0" + string-length "^2.0.0" + "@jest/reporters@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" @@ -1007,6 +1473,15 @@ optionalDependencies: node-notifier "^8.0.0" +"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -1016,6 +1491,15 @@ graceful-fs "^4.2.4" source-map "^0.6.0" +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@jest/test-result@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" @@ -1026,6 +1510,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-sequencer@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" + integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== + dependencies: + "@jest/test-result" "^24.9.0" + jest-haste-map "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + "@jest/test-sequencer@^26.6.3": version "26.6.3" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" @@ -1037,6 +1531,28 @@ jest-runner "^26.6.3" jest-runtime "^26.6.3" +"@jest/transform@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" + integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.9.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.9.0" + jest-regex-util "^24.9.0" + jest-util "^24.9.0" + micromatch "^3.1.10" + pirates "^4.0.1" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + "@jest/transform@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" @@ -1058,6 +1574,15 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/types@^24.3.0", "@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -1136,7 +1661,7 @@ string-width "^2.0.0" strip-ansi "^5" -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.0", "@types/babel__core@^7.1.7": version "7.1.14" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== @@ -1208,6 +1733,14 @@ dependencies: "@types/istanbul-lib-coverage" "*" +"@types/istanbul-reports@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2" + integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + "@types/istanbul-reports@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" @@ -1215,6 +1748,13 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest@^24.0.19": + version "24.9.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.9.1.tgz#02baf9573c78f1b9974a5f36778b366aa77bd534" + integrity sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q== + dependencies: + jest-diff "^24.3.0" + "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" @@ -1250,6 +1790,11 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + "@types/stack-utils@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" @@ -1270,6 +1815,13 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== +"@types/yargs@^13.0.0": + version "13.0.11" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.11.tgz#def2f0c93e4bdf2c61d7e34899b17e34be28d3b1" + integrity sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ== + dependencies: + "@types/yargs-parser" "*" + "@types/yargs@^15.0.0": version "15.0.13" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" @@ -1453,6 +2005,27 @@ eslint "^4.19.1" eslint-plugin-vue "^4.7.1" +"@vue/cli-plugin-unit-jest@^4.5.12": + version "4.5.12" + resolved "https://registry.yarnpkg.com/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-4.5.12.tgz#883d43f9a4bb6088a3b8125bd492511d05e1073e" + integrity sha512-hZibVfMDGTANN7QENbE7eEAlk8adTW8fEpuGXA4IV+eDqLDPUPVUOwcw8f9d7Rx3KVHES3GyFQ9yVK/KeI9NGw== + dependencies: + "@babel/core" "^7.11.0" + "@babel/plugin-transform-modules-commonjs" "^7.9.6" + "@types/jest" "^24.0.19" + "@vue/cli-shared-utils" "^4.5.12" + babel-core "^7.0.0-bridge.0" + babel-jest "^24.9.0" + babel-plugin-transform-es2015-modules-commonjs "^6.26.2" + deepmerge "^4.2.2" + jest "^24.9.0" + jest-environment-jsdom-fifteen "^1.0.2" + jest-serializer-vue "^2.0.2" + jest-transform-stub "^2.0.0" + jest-watch-typeahead "^0.4.2" + ts-jest "^24.2.0" + vue-jest "^3.0.5" + "@vue/cli-service@^3.7.0": version "3.12.1" resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-3.12.1.tgz#13220b1c189254e7c003390df329086f9b6e77e6" @@ -1533,6 +2106,24 @@ semver "^6.0.0" string.prototype.padstart "^3.0.0" +"@vue/cli-shared-utils@^4.5.12": + version "4.5.12" + resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-4.5.12.tgz#0e0693d488336d284ffa658ff33b1ea22927d065" + integrity sha512-qnIQPJ4XckMoqYh9fJ0Y91QKMIb4Hiibrm9+k4E15QHpk5RaokuOpf10SsOr2NLPCXSWsHOLo3hduZSwHPGY/Q== + dependencies: + "@hapi/joi" "^15.0.1" + chalk "^2.4.2" + execa "^1.0.0" + launch-editor "^2.2.1" + lru-cache "^5.1.1" + node-ipc "^9.1.1" + open "^6.3.0" + ora "^3.4.0" + read-pkg "^5.1.1" + request "^2.88.2" + semver "^6.1.0" + strip-ansi "^6.0.0" + "@vue/component-compiler-utils@^3.0.0", "@vue/component-compiler-utils@^3.1.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.2.0.tgz#8f85182ceed28e9b3c75313de669f83166d11e5d" @@ -1732,7 +2323,7 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.3, abab@^2.0.5: +abab@^2.0.0, abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== @@ -1750,6 +2341,14 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn-globals@^4.1.0, acorn-globals@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1770,7 +2369,7 @@ acorn-jsx@^5.0.0, acorn-jsx@^5.2.0: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn-walk@^6.1.1: +acorn-walk@^6.0.1, acorn-walk@^6.1.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== @@ -1785,17 +2384,17 @@ acorn@^3.0.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= -acorn@^5.5.0: +acorn@^5.5.0, acorn@^5.5.3: version "5.7.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== -acorn@^6.0.7, acorn@^6.1.1, acorn@^6.4.1: +acorn@^6.0.1, acorn@^6.0.7, acorn@^6.1.1, acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^7.1.1: +acorn@^7.1.0, acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -1887,7 +2486,7 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.1.0: +ansi-regex@^4.0.0, ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -1977,6 +2576,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -2169,11 +2773,24 @@ babel-eslint@^10.0.1, babel-eslint@^10.1.0: eslint-visitor-keys "^1.0.0" resolve "^1.12.0" -babel-helper-vue-jsx-merge-props@^2.0.0: +babel-helper-vue-jsx-merge-props@^2.0.0, babel-helper-vue-jsx-merge-props@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6" integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg== +babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== + dependencies: + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.9.0" + chalk "^2.4.2" + slash "^2.0.0" + babel-jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" @@ -2219,6 +2836,16 @@ babel-plugin-dynamic-import-node@^2.2.0, babel-plugin-dynamic-import-node@^2.3.3 dependencies: object.assign "^4.1.0" +babel-plugin-istanbul@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" + integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" @@ -2230,6 +2857,13 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" +babel-plugin-jest-hoist@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" + integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== + dependencies: + "@types/babel__traverse" "^7.0.6" + babel-plugin-jest-hoist@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" @@ -2240,6 +2874,20 @@ babel-plugin-jest-hoist@^26.6.2: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-jsx-event-modifiers@^2.0.2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/babel-plugin-jsx-event-modifiers/-/babel-plugin-jsx-event-modifiers-2.0.5.tgz#93e6ebb5d7553bb08f9fedbf7a0bee3af09a0472" + integrity sha512-tWGnCk0whZ+nZcj9tYLw4+y08tPJXqaEjIxRJZS6DkUUae72Kz4BsoGpxt/Kow7mmgQJpvFCw8IPLSNh5rkZCg== + +babel-plugin-jsx-v-model@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jsx-v-model/-/babel-plugin-jsx-v-model-2.0.3.tgz#c396416b99cb1af782087315ae1d3e62e070f47d" + integrity sha512-SIx3Y3XxwGEz56Q1atwr5GaZsxJ2IRYmn5dl38LFkaTAvjnbNQxsZHO+ylJPsd+Hmv+ixJBYYFEekPBTHwiGfQ== + dependencies: + babel-plugin-syntax-jsx "^6.18.0" + html-tags "^2.0.0" + svg-tags "^1.0.0" + babel-plugin-module-resolver@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz#ddfa5e301e3b9aa12d852a9979f18b37881ff5a7" @@ -2280,7 +2928,12 @@ babel-plugin-require-context-hook@^1.0.0: resolved "https://registry.yarnpkg.com/babel-plugin-require-context-hook/-/babel-plugin-require-context-hook-1.0.0.tgz#3f0e7cce87c338f53639b948632fd4e73834632d" integrity sha512-EMZD1563QUqLhzrqcThk759RhuNVX/ZJdrtGK6drwzgvnR+ARjWyXIHPbu+tUNaMGtPz/gQeAM2M6VUw2UiUeA== -babel-plugin-transform-es2015-modules-commonjs@^6.26.0: +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= + +babel-plugin-transform-es2015-modules-commonjs@^6.26.0, babel-plugin-transform-es2015-modules-commonjs@^6.26.2: version "6.26.2" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== @@ -2298,6 +2951,13 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" +babel-plugin-transform-vue-jsx@^3.5.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-3.7.0.tgz#d40492e6692a36b594f7e9a1928f43e969740960" + integrity sha512-W39X07/n3oJMQd8tALBO+440NraGSF//Lo1ydd/9Nme3+QiRGFBb1Q39T9iixh0jZPPbfv3so18tNoIgLatymw== + dependencies: + esutils "^2.0.2" + babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -2316,6 +2976,14 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" +babel-preset-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" + integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.9.0" + babel-preset-jest@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" @@ -2324,6 +2992,17 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" +babel-preset-vue@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/babel-preset-vue/-/babel-preset-vue-2.0.2.tgz#cfadf1bd736125397481b5f8525ced0049a0c71f" + integrity sha1-z63xvXNhJTl0gbX4UlztAEmgxx8= + dependencies: + babel-helper-vue-jsx-merge-props "^2.0.2" + babel-plugin-jsx-event-modifiers "^2.0.2" + babel-plugin-jsx-v-model "^2.0.1" + babel-plugin-syntax-jsx "^6.18.0" + babel-plugin-transform-vue-jsx "^3.5.0" + babel-runtime@6.x, babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" @@ -2572,6 +3251,13 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== + dependencies: + resolve "1.1.7" + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -2644,6 +3330,13 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4 escalade "^3.1.1" node-releases "^1.1.70" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -2674,7 +3367,7 @@ buffer-fill@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= -buffer-from@^1.0.0, buffer-from@^1.1.1: +buffer-from@1.x, buffer-from@^1.0.0, buffer-from@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -2862,6 +3555,11 @@ camelcase@^2.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -3268,6 +3966,11 @@ commander@2.17.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== +commander@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + commander@~2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" @@ -3440,11 +4143,24 @@ core-js-compat@^3.8.1: browserslist "^4.16.3" semver "7.0.0" +core-js-compat@^3.9.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.1.tgz#4e572acfe90aff69d76d8c37759d21a5c59bb455" + integrity sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA== + dependencies: + browserslist "^4.16.3" + semver "7.0.0" + core-js@^2.4.0, core-js@^2.5.7, core-js@^2.6.5: version "2.6.12" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== +core-js@^3.2.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz#cec8de593db8eb2a85ffb0dbdeb312cb6e5460ae" + integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== + core-js@^3.6.5: version "3.9.0" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.0.tgz#790b1bb11553a2272b36e2625c7179db345492f8" @@ -3715,17 +4431,24 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@~0.3.6: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^2.3.0: +cssom@^0.4.1, cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssstyle@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== + dependencies: + cssom "0.3.x" + +cssstyle@^2.0.0, cssstyle@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== @@ -4034,6 +4757,15 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-urls@^1.0.0, data-urls@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -4235,6 +4967,11 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -4250,6 +4987,11 @@ diacriticless@1.0.1: resolved "https://registry.yarnpkg.com/diacriticless/-/diacriticless-1.0.1.tgz#e7dda978c2919609bb48aee1efc5de6a337bd4c3" integrity sha1-592peMKRlgm7SK7h78XeajN71MM= +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -4360,6 +5102,13 @@ domelementtype@^2.0.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -4743,7 +5492,7 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@^1.11.1: +escodegen@^1.11.1, escodegen@^1.9.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== @@ -5246,6 +5995,18 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expect@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== + dependencies: + "@jest/types" "^24.9.0" + ansi-styles "^3.2.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" + expect@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" @@ -5424,7 +6185,7 @@ fast-glob@^3.1.1: micromatch "^4.0.2" picomatch "^2.2.1" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -5561,7 +6322,7 @@ find-cache-dir@^1.0.0: make-dir "^1.0.0" pkg-dir "^2.0.0" -find-cache-dir@^2.1.0: +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== @@ -5656,6 +6417,13 @@ follow-redirects@^1.0.0, follow-redirects@^1.10.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147" integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -6017,6 +6785,11 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +harmony-reflect@^1.4.6: + version "1.6.1" + resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9" + integrity sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -6138,6 +6911,13 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + hoopy@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" @@ -6173,6 +6953,13 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -6337,6 +7124,13 @@ icss-utils@^2.1.0: dependencies: postcss "^6.0.1" +identity-obj-proxy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" + integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ= + dependencies: + harmony-reflect "^1.4.6" + ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -6510,7 +7304,7 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -invariant@^2.2.2: +invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -6604,7 +7398,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.2, is-callable@^1.2.3: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.2, is-callable@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== @@ -6921,11 +7715,29 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== +istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" @@ -6936,6 +7748,15 @@ istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -6945,6 +7766,17 @@ istanbul-lib-report@^3.0.0: make-dir "^3.0.0" supports-color "^7.1.0" +istanbul-lib-source-maps@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + istanbul-lib-source-maps@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" @@ -6954,6 +7786,13 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" +istanbul-reports@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931" + integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg== + dependencies: + html-escaper "^2.0.0" + istanbul-reports@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" @@ -6967,6 +7806,15 @@ javascript-stringify@^1.6.0: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM= +jest-changed-files@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" + integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== + dependencies: + "@jest/types" "^24.9.0" + execa "^1.0.0" + throat "^4.0.0" + jest-changed-files@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" @@ -6976,6 +7824,25 @@ jest-changed-files@^26.6.2: execa "^4.0.0" throat "^5.0.0" +jest-cli@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" + integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== + dependencies: + "@jest/core" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + import-local "^2.0.0" + is-ci "^2.0.0" + jest-config "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + prompts "^2.0.1" + realpath-native "^1.1.0" + yargs "^13.3.0" + jest-cli@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" @@ -6995,6 +7862,29 @@ jest-cli@^26.6.3: prompts "^2.0.1" yargs "^15.4.1" +jest-config@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" + integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^24.9.0" + "@jest/types" "^24.9.0" + babel-jest "^24.9.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^24.9.0" + jest-environment-node "^24.9.0" + jest-get-type "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + micromatch "^3.1.10" + pretty-format "^24.9.0" + realpath-native "^1.1.0" + jest-config@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" @@ -7019,6 +7909,16 @@ jest-config@^26.6.3: micromatch "^4.0.2" pretty-format "^26.6.2" +jest-diff@^24.3.0, jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-diff@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" @@ -7029,6 +7929,13 @@ jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-docblock@^24.3.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" + integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== + dependencies: + detect-newline "^2.1.0" + jest-docblock@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" @@ -7036,6 +7943,17 @@ jest-docblock@^26.0.0: dependencies: detect-newline "^3.0.0" +jest-each@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" + integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== + dependencies: + "@jest/types" "^24.9.0" + chalk "^2.0.1" + jest-get-type "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + jest-each@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" @@ -7047,6 +7965,30 @@ jest-each@^26.6.2: jest-util "^26.6.2" pretty-format "^26.6.2" +jest-environment-jsdom-fifteen@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom-fifteen/-/jest-environment-jsdom-fifteen-1.0.2.tgz#49a0af55e0d32737a6114a1575dd714702ad63b0" + integrity sha512-nfrnAfwklE1872LIB31HcjM65cWTh1wzvMSp10IYtPJjLDUbTTvDpajZgIxUnhRmzGvogdHDayCIlerLK0OBBg== + dependencies: + "@jest/environment" "^24.3.0" + "@jest/fake-timers" "^24.3.0" + "@jest/types" "^24.3.0" + jest-mock "^24.0.0" + jest-util "^24.0.0" + jsdom "^15.2.1" + +jest-environment-jsdom@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" + integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + jsdom "^11.5.1" + jest-environment-jsdom@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" @@ -7060,6 +8002,17 @@ jest-environment-jsdom@^26.6.2: jest-util "^26.6.2" jsdom "^16.4.0" +jest-environment-node@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" + integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + jest-environment-node@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" @@ -7072,11 +8025,35 @@ jest-environment-node@^26.6.2: jest-mock "^26.6.2" jest-util "^26.6.2" +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== + jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== +jest-haste-map@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" + integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== + dependencies: + "@jest/types" "^24.9.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.9.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -7098,6 +8075,28 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" +jest-jasmine2@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" + integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.9.0" + is-generator-fn "^2.0.0" + jest-each "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + throat "^4.0.0" + jest-jasmine2@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" @@ -7122,6 +8121,14 @@ jest-jasmine2@^26.6.3: pretty-format "^26.6.2" throat "^5.0.0" +jest-leak-detector@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" + integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== + dependencies: + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-leak-detector@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" @@ -7130,6 +8137,16 @@ jest-leak-detector@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== + dependencies: + chalk "^2.0.1" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-matcher-utils@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" @@ -7140,6 +8157,20 @@ jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -7155,6 +8186,13 @@ jest-message-util@^26.6.2: slash "^3.0.0" stack-utils "^2.0.2" +jest-mock@^24.0.0, jest-mock@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" + integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== + dependencies: + "@jest/types" "^24.9.0" + jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -7163,16 +8201,30 @@ jest-mock@^26.6.2: "@jest/types" "^26.6.2" "@types/node" "*" -jest-pnp-resolver@^1.2.2: +jest-pnp-resolver@^1.2.1, jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== +jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + jest-regex-util@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== +jest-resolve-dependencies@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" + integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== + dependencies: + "@jest/types" "^24.9.0" + jest-regex-util "^24.3.0" + jest-snapshot "^24.9.0" + jest-resolve-dependencies@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" @@ -7182,6 +8234,17 @@ jest-resolve-dependencies@^26.6.3: jest-regex-util "^26.0.0" jest-snapshot "^26.6.2" +jest-resolve@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" + integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== + dependencies: + "@jest/types" "^24.9.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + jest-resolve@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" @@ -7196,6 +8259,31 @@ jest-resolve@^26.6.2: resolve "^1.18.1" slash "^3.0.0" +jest-runner@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" + integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.4.2" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-docblock "^24.3.0" + jest-haste-map "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-leak-detector "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + source-map-support "^0.5.6" + throat "^4.0.0" + jest-runner@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" @@ -7222,6 +8310,35 @@ jest-runner@^26.6.3: source-map-support "^0.5.6" throat "^5.0.0" +jest-runtime@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" + integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/source-map" "^24.3.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + realpath-native "^1.1.0" + slash "^2.0.0" + strip-bom "^3.0.0" + yargs "^13.3.0" + jest-runtime@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" @@ -7255,6 +8372,18 @@ jest-runtime@^26.6.3: strip-bom "^4.0.0" yargs "^15.4.1" +jest-serializer-vue@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz#b238ef286357ec6b480421bd47145050987d59b3" + integrity sha1-sjjvKGNX7GtIBCG9RxRQUJh9WbM= + dependencies: + pretty "2.0.0" + +jest-serializer@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" + integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== + jest-serializer@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" @@ -7263,6 +8392,25 @@ jest-serializer@^26.6.2: "@types/node" "*" graceful-fs "^4.2.4" +jest-snapshot@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" + integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + expect "^24.9.0" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^24.9.0" + semver "^6.2.0" + jest-snapshot@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" @@ -7285,6 +8433,29 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" +jest-transform-stub@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jest-transform-stub/-/jest-transform-stub-2.0.0.tgz#19018b0851f7568972147a5d60074b55f0225a7d" + integrity sha512-lspHaCRx/mBbnm3h4uMMS3R5aZzMwyNpNIJLXj4cEsV0mIUtS4IjYJLSoyjRCtnxb6RIGJ4NL2quZzfIeNhbkg== + +jest-util@^24.0.0, jest-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" + integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== + dependencies: + "@jest/console" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/source-map" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -7297,6 +8468,18 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" +jest-validate@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" + integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== + dependencies: + "@jest/types" "^24.9.0" + camelcase "^5.3.1" + chalk "^2.0.1" + jest-get-type "^24.9.0" + leven "^3.1.0" + pretty-format "^24.9.0" + jest-validate@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -7309,6 +8492,32 @@ jest-validate@^26.6.2: leven "^3.1.0" pretty-format "^26.6.2" +jest-watch-typeahead@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.4.2.tgz#e5be959698a7fa2302229a5082c488c3c8780a4a" + integrity sha512-f7VpLebTdaXs81rg/oj4Vg/ObZy2QtGzAmGLNsqUS5G5KtSN68tFcIsbvNODfNyQxU78g7D8x77o3bgfBTR+2Q== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.1" + jest-regex-util "^24.9.0" + jest-watcher "^24.3.0" + slash "^3.0.0" + string-length "^3.1.0" + strip-ansi "^5.0.0" + +jest-watcher@^24.3.0, jest-watcher@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" + integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== + dependencies: + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + jest-util "^24.9.0" + string-length "^2.0.0" + jest-watcher@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" @@ -7322,6 +8531,14 @@ jest-watcher@^26.6.2: jest-util "^26.6.2" string-length "^4.0.1" +jest-worker@^24.6.0, jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -7331,6 +8548,14 @@ jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" +jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" + integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== + dependencies: + import-local "^2.0.0" + jest-cli "^24.9.0" + jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" @@ -7396,6 +8621,70 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + +jsdom@^15.2.1: + version "15.2.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" + integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== + dependencies: + abab "^2.0.0" + acorn "^7.1.0" + acorn-globals "^4.3.2" + array-equal "^1.0.0" + cssom "^0.4.1" + cssstyle "^2.0.0" + data-urls "^1.1.0" + domexception "^1.0.1" + escodegen "^1.11.1" + html-encoding-sniffer "^1.0.2" + nwsapi "^2.2.0" + parse5 "5.1.0" + pn "^1.1.0" + request "^2.88.0" + request-promise-native "^1.0.7" + saxes "^3.1.9" + symbol-tree "^3.2.2" + tough-cookie "^3.0.1" + w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^7.0.0" + xml-name-validator "^3.0.0" + jsdom@^16.4.0: version "16.5.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.1.tgz#4ced6bbd7b77d67fb980e64d9e3e6fb900f97dd6" @@ -7478,6 +8767,13 @@ json3@^3.3.3: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== +json5@2.x, json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -7490,13 +8786,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -7563,6 +8852,11 @@ launch-editor@^2.2.1: chalk "^2.3.0" shell-quote "^1.6.1" +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -7602,6 +8896,16 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + loader-fs-cache@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz#f08657646d607078be2f0a032f8bd69dd6f277d9" @@ -7711,7 +9015,7 @@ lodash.mapvalues@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw= -lodash.memoize@^4.1.2: +lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= @@ -7804,7 +9108,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^2.0.0: +make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -7819,6 +9123,11 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" +make-error@1.x: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -8072,7 +9381,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -8215,6 +9524,14 @@ node-cache@^4.1.1: clone "2.x" lodash "^4.17.15" +node-environment-flags@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -8291,6 +9608,17 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= +node-notifier@^5.4.2: + version "5.4.5" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.5.tgz#0cbc1a2b0f658493b4025775a13ad938e96091ef" + integrity sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + node-notifier@^8.0.0: version "8.0.2" resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" @@ -8443,7 +9771,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -nwsapi@^2.2.0: +nwsapi@^2.0.7, nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== @@ -8517,7 +9845,7 @@ object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0, object.getownpropertydescriptors@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== @@ -8661,6 +9989,13 @@ osenv@0: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-each-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" + integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= + dependencies: + p-reduce "^1.0.0" + p-each-series@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" @@ -8716,6 +10051,11 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + p-retry@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" @@ -8802,6 +10142,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + parse5-htmlparser2-tree-adapter@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" @@ -8809,6 +10154,16 @@ parse5-htmlparser2-tree-adapter@^6.0.0: dependencies: parse5 "^6.0.1" +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== + parse5@6.0.1, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -8976,7 +10331,7 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pirates@^4.0.1: +pirates@^4.0.0, pirates@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== @@ -9023,6 +10378,11 @@ pluralize@^7.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + pngjs@^3.3.0: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" @@ -9430,6 +10790,16 @@ pretty-error@^2.0.2: lodash "^4.17.20" renderkid "^2.0.4" +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== + dependencies: + "@jest/types" "^24.9.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -9440,7 +10810,7 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty@^2.0.0: +pretty@2.0.0, pretty@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pretty/-/pretty-2.0.0.tgz#adbc7960b7bbfe289a557dc5f737619a220d06a5" integrity sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU= @@ -9686,6 +11056,11 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +react-is@^16.8.4: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^17.0.1: version "17.0.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" @@ -9707,6 +11082,14 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -9734,7 +11117,16 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -read-pkg@^5.0.0, read-pkg@^5.2.0: +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +read-pkg@^5.0.0, read-pkg@^5.1.1, read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== @@ -9782,6 +11174,13 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -9919,7 +11318,7 @@ request-promise-core@1.1.4: dependencies: lodash "^4.17.19" -request-promise-native@^1.0.7, request-promise-native@^1.0.9: +request-promise-native@^1.0.5, request-promise-native@^1.0.7, request-promise-native@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== @@ -10026,7 +11425,12 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.5, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.4.0: +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + +resolve@1.x, resolve@^1.1.5, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.4.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -10209,11 +11613,18 @@ sass-loader@^7.1.0: pify "^4.0.1" semver "^6.3.0" -sax@~1.2.4: +sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +saxes@^3.1.9: + version "3.1.11" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" + integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== + dependencies: + xmlchars "^2.1.1" + saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -10269,7 +11680,7 @@ selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -10279,7 +11690,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -10570,7 +11981,7 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -10700,6 +12111,13 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-utils@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.4.tgz#4b600971dcfc6aed0cbdf2a8268177cc916c87c8" + integrity sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w== + dependencies: + escape-string-regexp "^2.0.0" + stack-utils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" @@ -10801,6 +12219,22 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + +string-length@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" + integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== + dependencies: + astral-regex "^1.0.0" + strip-ansi "^5.2.0" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -11036,7 +12470,7 @@ sweetalert2@^9.5.4: resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-9.17.2.tgz#7f33ce157a64c303d2ca98863c9a3f437e5f1187" integrity sha512-HkpPZVMYsnhFUBLdy/LvkU9snggKP3VAuSVnPhVXjxdg02lWbFx0W8H3m7A+WMWw2diXZS1wIa4m67XkNxdvew== -symbol-tree@^3.2.4: +symbol-tree@^3.2.2, symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== @@ -11109,6 +12543,16 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -11146,6 +12590,11 @@ thread-loader@^2.1.2: loader-utils "^1.1.0" neo-async "^2.6.0" +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -11272,7 +12721,7 @@ toposort@^1.0.0: resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= -tough-cookie@^2.3.3, tough-cookie@~2.5.0: +tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -11280,6 +12729,15 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -11289,6 +12747,13 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + tr46@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" @@ -11313,6 +12778,22 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-jest@^24.2.0: + version "24.3.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.3.0.tgz#b97814e3eab359ea840a1ac112deae68aa440869" + integrity sha512-Hb94C/+QRIgjVZlJyiWwouYUF+siNJHJHknyspaOcZ+OQAIdFG/UrdQVXw/0B8Z3No34xkUXZJpOTy9alOWdVQ== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + mkdirp "0.x" + resolve "1.x" + semver "^5.5" + yargs-parser "10.x" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -11582,6 +13063,17 @@ util.promisify@1.0.0: define-properties "^1.1.2" object.getownpropertydescriptors "^2.0.3" +util.promisify@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" + integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + for-each "^0.3.3" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.1" + util.promisify@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" @@ -11635,6 +13127,13 @@ v8-to-istanbul@^7.0.0: convert-source-map "^1.6.0" source-map "^0.7.3" +v8flags@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" + integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== + dependencies: + homedir-polyfill "^1.0.1" + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -11796,7 +13295,7 @@ vue-i18n@^8.17.0, vue-i18n@^8.22.4: resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.23.0.tgz#4a65681a1dfe716d47e1d00ddbd6e0b88ea36735" integrity sha512-mXgniaumwca8tKdp55fmvqIcW658vQQXq0zEyRHp8sgZ6t+Md+Whhu6CCPg9/erVNlvpKzsGsucGjt2N8GrFCA== -vue-jest@^3.0.7: +vue-jest@^3.0.5, vue-jest@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-3.0.7.tgz#a6d29758a5cb4d750f5d1242212be39be4296a33" integrity sha512-PIOxFM+wsBMry26ZpfBvUQ/DGH2hvp5khDQ1n51g3bN0TwFwTy4J85XVfxTRMukqHji/GnAoGUnlZ5Ao73K62w== @@ -11887,13 +13386,22 @@ vuex@^3.6.0: resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71" integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw== -w3c-hr-time@^1.0.2: +w3c-hr-time@^1.0.1, w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + w3c-xmlserializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" @@ -11940,6 +13448,11 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -12101,18 +13614,36 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -whatwg-encoding@^1.0.5: +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" -whatwg-mimetype@^2.3.0: +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-url@^8.0.0: version "8.4.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837" @@ -12138,7 +13669,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1, which@^1.2.9: +which@1, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -12208,6 +13739,15 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + write-file-atomic@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" @@ -12232,6 +13772,13 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + ws@^6.0.0, ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" @@ -12239,7 +13786,7 @@ ws@^6.0.0, ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.4.4: +ws@^7.0.0, ws@^7.4.4: version "7.4.4" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== @@ -12249,7 +13796,7 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xmlchars@^2.2.0: +xmlchars@^2.1.1, xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== @@ -12284,6 +13831,13 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yargs-parser@10.x: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -12305,7 +13859,7 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20" integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA== -yargs@^13.2.2, yargs@^13.2.4, yargs@^13.3.2: +yargs@^13.2.2, yargs@^13.2.4, yargs@^13.3.0, yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== From 1f002f4ed0b12d4b2bf63efceabe546d0c5b58ea Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 16:10:36 +0200 Subject: [PATCH 18/44] update debug docker to use dependencies container pushed to docker hub --- login_server/Dockerfile | 28 +-- login_server/Dockerfile.debug | 189 +++++++----------- .../Dockerfiles/Dockerfile.dependencies | 46 +++++ .../{Dockefile.protoc => Dockerfile.protoc} | 4 +- 4 files changed, 124 insertions(+), 143 deletions(-) create mode 100644 login_server/Dockerfiles/Dockerfile.dependencies rename login_server/Dockerfiles/{Dockefile.protoc => Dockerfile.protoc} (92%) diff --git a/login_server/Dockerfile b/login_server/Dockerfile index 39d1eff4a..7d51a7ca4 100644 --- a/login_server/Dockerfile +++ b/login_server/Dockerfile @@ -1,18 +1,3 @@ -######################################################################################################### -# Build protoc -######################################################################################################### -FROM gcc:7.5 as protoc_build -RUN git clone --recurse-submodules https://github.com/protocolbuffers/protobuf.git -WORKDIR /protobuf - -RUN git checkout v3.9.1 -RUN ./autogen.sh -RUN ./configure --enable-static=yes -RUN make -j$(grep processor /proc/cpuinfo | wc -l) -RUN make check - -CMD ["./protobuf"] - ######################################################################################################### # Build release ######################################################################################################### @@ -22,11 +7,10 @@ ENV DOCKER_WORKDIR="/code" USER root -COPY --from=protoc_build /protobuf/src/.libs/protoc /usr/bin/ -COPY --from=protoc_build /protobuf/src/.libs/libprotobuf.so.20.0.1 /usr/lib/libprotobuf.so.20 -COPY --from=protoc_build /protobuf/src/.libs/libprotoc.so.20.0.1 /usr/lib/libprotoc.so.20 -COPY --from=protoc_build /protobuf/src/google/protobuf/*.proto /usr/include/google/protobuf/ -COPY --from=protoc_build /protobuf/src/google/protobuf/*.h /usr/include/google/protobuf/ +COPY --from=unicorny/protoc:3.9.1 /usr/bin/protoc /usr/bin/ +COPY --from=unicorny/protoc:3.9.1 /usr/lib/libprotobuf.so.20 /usr/lib/libprotobuf.so.20 +COPY --from=unicorny/protoc:3.9.1 /usr/lib/libprotoc.so.20 /usr/lib/libprotoc.so.20 +COPY --from=unicorny/protoc:3.9.1 /usr/include/google/protobuf/* /usr/include/google/protobuf/ RUN mkdir -p ${DOCKER_WORKDIR} @@ -40,7 +24,7 @@ RUN cd dependencies/iroha-ed25519 && \ mkdir build && \ cd build && \ cmake .. -DEDIMPL=ref10 -DHASH=sha2_sphlib -DRANDOM=bcryptgen -DBUILD=STATIC && \ - make -j$(grep processor /proc/cpuinfo | wc -l) + make -j$(nproc) RUN cd dependencies/mariadb-connector-c && \ mkdir build && \ @@ -56,7 +40,7 @@ RUN mkdir build && \ cd build && \ conan install .. --build=missing && \ cmake .. && \ - make -j$(grep processor /proc/cpuinfo | wc -l) Gradido_LoginServer + make -j$(nproc) Gradido_LoginServer CMD ["./code"] diff --git a/login_server/Dockerfile.debug b/login_server/Dockerfile.debug index 15d78b0dd..6c72821fa 100644 --- a/login_server/Dockerfile.debug +++ b/login_server/Dockerfile.debug @@ -1,119 +1,70 @@ - - -######################################################################################################### -# debug build preparation -######################################################################################################### - -From conanio/gcc7 as debug_preparation - -ENV DOCKER_WORKDIR="/code" - -USER root - -COPY --from=protoc_build /protobuf/src/.libs/protoc /usr/bin/ -COPY --from=protoc_build /protobuf/src/.libs/libprotobuf.so.20.0.1 /usr/lib/libprotobuf.so.20 -COPY --from=protoc_build /protobuf/src/.libs/libprotoc.so.20.0.1 /usr/lib/libprotoc.so.20 -COPY --from=protoc_build /protobuf/src/google/protobuf/*.proto /usr/include/google/protobuf/ -COPY --from=protoc_build /protobuf/src/google/protobuf/*.h /usr/include/google/protobuf/ - -#VOLUME /root/.conan - -RUN mkdir -p ${DOCKER_WORKDIR} -WORKDIR ${DOCKER_WORKDIR} - -COPY ./dependencies ./dependencies -COPY ./conanfile.txt ./conanfile.txt - -RUN ls -la -RUN cd dependencies/iroha-ed25519 && \ - ls -la && \ - mkdir build && \ - cd build && \ - cmake .. -DCMAKE_BUILD_TYPE=Debug -DEDIMPL=ref10 -DHASH=sha2_sphlib -DRANDOM=bcryptgen -DBUILD=STATIC && \ - make -j$(nproc) - -RUN cd dependencies/mariadb-connector-c && \ - mkdir build && \ - cd build && \ - cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_SSL=OFF .. - - -#RUN chmod +x compile_proto.sh -#RUN chmod +x compile_pot.sh -#RUN ls -la -#RUN ./compile_pot.sh -#RUN ./compile_proto.sh -RUN mkdir build && \ - cd build && \ - conan install .. --build=missing -s build_type=Debug - - - -######################################################################################################### -# Build debug -######################################################################################################### -From debug_preparation as debug - -ENV DOCKER_WORKDIR="/code" - -USER root -#VOLUME /root/.conan - -RUN apt-get update && \ - apt-get install -y --no-install-recommends gdb && \ - apt-get autoclean && \ - apt-get autoremove && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -#COPY --from=protoc_build /protobuf/src/.libs/libprotobuf.so.20.0.1 /usr/lib/libprotobuf.so.20 -#COPY --from=protoc_build /protobuf/src/google/protobuf/*.h /usr/include/google/protobuf/ - - -#COPY --from=debug_preparation /code /code -#COPY --from=debug_preparation /home/conan /home/conan -#RUN ls -la /home/conan/.conan -COPY . . -WORKDIR ${DOCKER_WORKDIR} - -#RUN ls -la -#RUN cat build/conanbuildinfo.cmake -RUN chmod +x compile_proto.sh -RUN chmod +x compile_pot.sh -#RUN ls -la -RUN ./compile_pot.sh -RUN ./compile_proto.sh -RUN cd build && \ - cmake -DCMAKE_BUILD_TYPE=Debug .. && \ - make -j$(nproc) - - -######################################################################################################### -# run debug -######################################################################################################### -FROM ubuntu:latest as login_server_debug - -WORKDIR "/usr/bin" - -#RUN apt-get update && \ -# apt-get install -y --no-install-recommends gdb && \ -# apt-get autoclean && \ -# apt-get autoremove && \ -# apt-get clean && \ -# rm -rf /var/lib/apt/lists/* - -VOLUME /var/log/grd_login - -COPY --from=debug /code/build/bin/Gradido_LoginServer /usr/bin/ -COPY --from=debug /code/build/lib/libmariadb.so.3 /usr/lib/ -#COPY start_after_mysql.sh . -RUN chmod +x /usr/bin/Gradido_LoginServer -EXPOSE 1200 -EXPOSE 1201 -#ENTRYPOINT ["/usr/bin/Gradido_LoginServer"] -# Wait on mariadb to started -#CMD ["sleep 5", "/usr/bin/Gradido_LoginServer"] -#RUN chmod +x ./start_after_mysql.sh -#ENTRYPOINT ["/usr/bin/Gradido_LoginServer"] -#CMD gdb -ex=r Gradido_LoginServer -CMD Gradido_LoginServer + + +######################################################################################################### +# Build debug +######################################################################################################### +From gradido/login_dependencies:stage1 as debug + +ENV DOCKER_WORKDIR="/code" + +USER root +#VOLUME /root/.conan + +#RUN apt-get update && \ + # apt-get install -y --no-install-recommends gdb && \ +# apt-get autoclean && \ +# apt-get autoremove && \ + # apt-get clean && \ + # rm -rf /var/lib/apt/lists/* + +#COPY --from=protoc_build /protobuf/src/.libs/libprotobuf.so.20.0.1 /usr/lib/libprotobuf.so.20 +#COPY --from=protoc_build /protobuf/src/google/protobuf/*.h /usr/include/google/protobuf/ + + +#COPY --from=debug_preparation /code /code +#COPY --from=debug_preparation /home/conan /home/conan +#RUN ls -la /home/conan/.conan +COPY . . +WORKDIR ${DOCKER_WORKDIR} + +#RUN ls -la +#RUN cat build/conanbuildinfo.cmake +RUN chmod +x compile_proto.sh +RUN chmod +x compile_pot.sh +#RUN ls -la +RUN ./compile_pot.sh +RUN ./compile_proto.sh +RUN cd build && \ + cmake -DCMAKE_BUILD_TYPE=Debug .. && \ + make -j$(nproc) Gradido_LoginServer + + +######################################################################################################### +# run debug +######################################################################################################### +FROM ubuntu:latest as login_server_debug + +WORKDIR "/usr/bin" + +#RUN apt-get update && \ +# apt-get install -y --no-install-recommends gdb && \ +# apt-get autoclean && \ +# apt-get autoremove && \ +# apt-get clean && \ +# rm -rf /var/lib/apt/lists/* + +VOLUME /var/log/grd_login + +COPY --from=debug /code/build/bin/Gradido_LoginServer /usr/bin/ +COPY --from=debug /code/build/lib/libmariadb.so.3 /usr/lib/ +#COPY start_after_mysql.sh . +RUN chmod +x /usr/bin/Gradido_LoginServer +EXPOSE 1200 +EXPOSE 1201 +#ENTRYPOINT ["/usr/bin/Gradido_LoginServer"] +# Wait on mariadb to started +#CMD ["sleep 5", "/usr/bin/Gradido_LoginServer"] +#RUN chmod +x ./start_after_mysql.sh +#ENTRYPOINT ["/usr/bin/Gradido_LoginServer"] +#CMD gdb -ex=r Gradido_LoginServer +CMD Gradido_LoginServer diff --git a/login_server/Dockerfiles/Dockerfile.dependencies b/login_server/Dockerfiles/Dockerfile.dependencies new file mode 100644 index 000000000..bfdb6b0d0 --- /dev/null +++ b/login_server/Dockerfiles/Dockerfile.dependencies @@ -0,0 +1,46 @@ +######################################################################################################### +# debug build preparation +######################################################################################################### + +From conanio/gcc7 as debug_preparation + +ENV DOCKER_WORKDIR="/code" + +USER root + +COPY --from=unicorny/protoc:3.9.1 /usr/bin/protoc /usr/bin/ +COPY --from=unicorny/protoc:3.9.1 /usr/lib/libprotobuf.so.20 /usr/lib/libprotobuf.so.20 +COPY --from=unicorny/protoc:3.9.1 /usr/lib/libprotoc.so.20 /usr/lib/libprotoc.so.20 +COPY --from=unicorny/protoc:3.9.1 /usr/include/google/protobuf/* /usr/include/google/protobuf/ + +#VOLUME /root/.conan + +RUN mkdir -p ${DOCKER_WORKDIR} +WORKDIR ${DOCKER_WORKDIR} + +COPY ./dependencies ./dependencies +COPY ./conanfile.txt ./conanfile.txt + +RUN ls -la +RUN cd dependencies/iroha-ed25519 && \ + ls -la && \ + mkdir build && \ + cd build && \ + cmake .. -DCMAKE_BUILD_TYPE=Debug -DEDIMPL=ref10 -DHASH=sha2_sphlib -DRANDOM=bcryptgen -DBUILD=STATIC && \ + make -j$(nproc) + +RUN cd dependencies/mariadb-connector-c && \ + mkdir build && \ + cd build && \ + cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_SSL=OFF .. + + +#RUN chmod +x compile_proto.sh +#RUN chmod +x compile_pot.sh +#RUN ls -la +#RUN ./compile_pot.sh +#RUN ./compile_proto.sh +RUN mkdir build && \ + cd build && \ + conan install .. --build=missing -s build_type=Debug + diff --git a/login_server/Dockerfiles/Dockefile.protoc b/login_server/Dockerfiles/Dockerfile.protoc similarity index 92% rename from login_server/Dockerfiles/Dockefile.protoc rename to login_server/Dockerfiles/Dockerfile.protoc index 92cf80c2a..7aef56f62 100644 --- a/login_server/Dockerfiles/Dockefile.protoc +++ b/login_server/Dockerfiles/Dockerfile.protoc @@ -25,6 +25,6 @@ COPY --from=protoc3.9.1_build /protobuf/src/google/protobuf/*.proto /usr/include COPY --from=protoc3.9.1_build /protobuf/src/google/protobuf/*.h /usr/include/google/protobuf/ ## build with: -# docker build . -f Dockefile.protoc -t unicorny/protoc:3.9.1 +# docker build . -f Dockefile.protoc -t gradido/protoc:3.9.1 ## upload (login to docker hub on shell before): -# docker push unicorny/protoc:3.9.1 \ No newline at end of file +# docker push gradido/protoc:3.9.1 \ No newline at end of file From 232d697941910df1af673b72d5881f275585eba8 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 29 Mar 2021 16:35:54 +0200 Subject: [PATCH 19/44] tests for ContentFooter --- .../src/views/Layout/ContentFooter.spec.js | 78 ++++++++++++++++++- frontend/src/views/Layout/ContentFooter.vue | 2 +- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/Layout/ContentFooter.spec.js b/frontend/src/views/Layout/ContentFooter.spec.js index ea985a336..6af55fe16 100644 --- a/frontend/src/views/Layout/ContentFooter.spec.js +++ b/frontend/src/views/Layout/ContentFooter.spec.js @@ -9,7 +9,7 @@ describe('ContentFooter', () => { let mocks = { $i18n: { - locale: () => 'en', + locale: 'en', }, $t: jest.fn((t) => t), } @@ -24,7 +24,81 @@ describe('ContentFooter', () => { }) it('renders the content footer', () => { - expect(wrapper.find('footer.footer')).toBeDefined() + expect(wrapper.find('footer.footer').exists()).toBeTruthy() + }) + + describe('copyright', () => { + it('shows the copyright', () => { + expect(wrapper.find('div.copyright').exists()).toBeTruthy() + }) + + it('renders the copyright year', () => { + expect(wrapper.find('div.copyright').text()).toMatch(/©\s*2[0-9]{3,3}\s+/) + }) + + it('renders a link to Gradido-Akademie', () => { + expect(wrapper.find('div.copyright').find('a').text()).toEqual('Gradido-Akademie') + }) + + it('links to the login page when clicked on copyright', () => { + expect(wrapper.find('div.copyright').find('a').attributes('href')).toEqual('#/Login') + }) + }) + + describe('links to gradido.net', () => { + it('has a link to the gradido.net', () => { + expect(wrapper.findAll('a.nav-link').at(0).text()).toEqual('Gradido') + }) + + it('links to the https://gradido.net/en when locale is en', () => { + expect(wrapper.findAll('a.nav-link').at(0).attributes('href')).toEqual( + 'https://gradido.net/en', + ) + }) + + it('has a link to the legal notice', () => { + expect(wrapper.findAll('a.nav-link').at(1).text()).toEqual('imprint') + }) + + it('links to the https://gradido.net/en/impressum when locale is en', () => { + expect(wrapper.findAll('a.nav-link').at(1).attributes('href')).toEqual( + 'https://gradido.net/en/impressum/', + ) + }) + + it('has a link to the privacy policy', () => { + expect(wrapper.findAll('a.nav-link').at(2).text()).toEqual('privacy_policy') + }) + + it('links to the https://gradido.net/en/datenschutz when locale is en', () => { + expect(wrapper.findAll('a.nav-link').at(2).attributes('href')).toEqual( + 'https://gradido.net/en/datenschutz/', + ) + }) + + describe('links are localized', () => { + beforeEach(() => { + mocks.$i18n.locale = 'de' + }) + + it('links to the https://gradido.net/de when locale is de', () => { + expect(wrapper.findAll('a.nav-link').at(0).attributes('href')).toEqual( + 'https://gradido.net/de', + ) + }) + + it('links to the https://gradido.net/de/impressum when locale is de', () => { + expect(wrapper.findAll('a.nav-link').at(1).attributes('href')).toEqual( + 'https://gradido.net/de/impressum/', + ) + }) + + it('links to the https://gradido.net/de/datenschutz when locale is de', () => { + expect(wrapper.findAll('a.nav-link').at(2).attributes('href')).toEqual( + 'https://gradido.net/de/datenschutz/', + ) + }) + }) }) }) }) diff --git a/frontend/src/views/Layout/ContentFooter.vue b/frontend/src/views/Layout/ContentFooter.vue index d53c3e3ce..5299b2f54 100755 --- a/frontend/src/views/Layout/ContentFooter.vue +++ b/frontend/src/views/Layout/ContentFooter.vue @@ -4,7 +4,7 @@ From 0da527917523530186e6effe63dc001fc99bd3e3 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 17:47:55 +0200 Subject: [PATCH 20/44] build login-server on docker-compose up in a docker volume so it rebuild only neccessary parts if some c++ files have changed --- docker-compose.override.yml | 127 +++++++++++----------- login_server/Dockerfile.debug | 21 ++-- login_server/Dockerfiles/build_and_run.sh | 9 ++ 3 files changed, 82 insertions(+), 75 deletions(-) create mode 100644 login_server/Dockerfiles/build_and_run.sh diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 97cdb3155..f59316396 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -1,63 +1,64 @@ -version: "3.4" - -services: - ######################################################## - # FRONTEND ############################################# - ######################################################## - frontend: - image: gradido/frontend:development - build: - target: development - environment: - - NODE_ENV="development" - # - DEBUG=true - - NUXT_BUILD=/tmp/nuxt # avoid file permission issues when `rm -rf .nuxt/` - volumes: - # This makes sure the docker container has its own node modules. - # Therefore it is possible to have a different node version on the host machine - - frontend_node_modules:/app/node_modules - # bind the local folder to the docker to allow live reload - - ./frontend:/app - - ######################################################### - ## LOGIN SERVER ######################################### - ######################################################### - login-server: - build: - target: login_server_debug - dockerfile: Dockerfile.debug - security_opt: - - seccomp:unconfined - cap_add: - - SYS_PTRACE - volumes: - - ./logs:/var/log/grd_login - - conan:/root/.conan - - ######################################################### - ## NGINX ################################################ - ######################################################### - nginx: - volumes: - - ./logs:/var/log/nginx - - ######################################################### - ## PHPMYADMIN ########################################### - ######################################################### - phpmyadmin: - image: phpmyadmin - container_name: phpmyadmin - environment: - - PMA_ARBITRARY=1 - #restart: always - ports: - - 8074:80 - networks: - - internal-net - - external-net - volumes: - - /sessions - -volumes: - frontend_node_modules: - conan: \ No newline at end of file +version: "3.4" + +services: + ######################################################## + # FRONTEND ############################################# + ######################################################## + frontend: + image: gradido/frontend:development + build: + target: development + environment: + - NODE_ENV="development" + # - DEBUG=true + - NUXT_BUILD=/tmp/nuxt # avoid file permission issues when `rm -rf .nuxt/` + volumes: + # This makes sure the docker container has its own node modules. + # Therefore it is possible to have a different node version on the host machine + - frontend_node_modules:/app/node_modules + # bind the local folder to the docker to allow live reload + - ./frontend:/app + + ######################################################### + ## LOGIN SERVER ######################################### + ######################################################### + login-server: + build: + target: login_server_debug + dockerfile: Dockerfile.debug + security_opt: + - seccomp:unconfined + cap_add: + - SYS_PTRACE + volumes: + - ./logs:/var/log/grd_login + - ./login_server/src:/code/src + - login_build:/code/build_vol + + ######################################################### + ## NGINX ################################################ + ######################################################### + nginx: + volumes: + - ./logs:/var/log/nginx + + ######################################################### + ## PHPMYADMIN ########################################### + ######################################################### + phpmyadmin: + image: phpmyadmin + container_name: phpmyadmin + environment: + - PMA_ARBITRARY=1 + #restart: always + ports: + - 8074:80 + networks: + - internal-net + - external-net + volumes: + - /sessions + +volumes: + frontend_node_modules: + login_build: \ No newline at end of file diff --git a/login_server/Dockerfile.debug b/login_server/Dockerfile.debug index 6c72821fa..4c9945f8c 100644 --- a/login_server/Dockerfile.debug +++ b/login_server/Dockerfile.debug @@ -24,8 +24,8 @@ USER root #COPY --from=debug_preparation /code /code #COPY --from=debug_preparation /home/conan /home/conan #RUN ls -la /home/conan/.conan -COPY . . WORKDIR ${DOCKER_WORKDIR} +COPY . . #RUN ls -la #RUN cat build/conanbuildinfo.cmake @@ -34,18 +34,15 @@ RUN chmod +x compile_pot.sh #RUN ls -la RUN ./compile_pot.sh RUN ./compile_proto.sh -RUN cd build && \ - cmake -DCMAKE_BUILD_TYPE=Debug .. && \ - make -j$(nproc) Gradido_LoginServer + ######################################################################################################### # run debug ######################################################################################################### -FROM ubuntu:latest as login_server_debug - -WORKDIR "/usr/bin" +FROM debug as login_server_debug +ENV DOCKER_WORKDIR="/code" #RUN apt-get update && \ # apt-get install -y --no-install-recommends gdb && \ # apt-get autoclean && \ @@ -54,17 +51,17 @@ WORKDIR "/usr/bin" # rm -rf /var/lib/apt/lists/* VOLUME /var/log/grd_login +VOLUME /code/src -COPY --from=debug /code/build/bin/Gradido_LoginServer /usr/bin/ -COPY --from=debug /code/build/lib/libmariadb.so.3 /usr/lib/ -#COPY start_after_mysql.sh . -RUN chmod +x /usr/bin/Gradido_LoginServer EXPOSE 1200 EXPOSE 1201 +WORKDIR ${DOCKER_WORKDIR} +RUN chmod +x ./Dockerfiles/build_and_run.sh + #ENTRYPOINT ["/usr/bin/Gradido_LoginServer"] # Wait on mariadb to started #CMD ["sleep 5", "/usr/bin/Gradido_LoginServer"] #RUN chmod +x ./start_after_mysql.sh #ENTRYPOINT ["/usr/bin/Gradido_LoginServer"] #CMD gdb -ex=r Gradido_LoginServer -CMD Gradido_LoginServer +CMD ./Dockerfiles/build_and_run.sh diff --git a/login_server/Dockerfiles/build_and_run.sh b/login_server/Dockerfiles/build_and_run.sh new file mode 100644 index 000000000..ee9999532 --- /dev/null +++ b/login_server/Dockerfiles/build_and_run.sh @@ -0,0 +1,9 @@ +#!/bin/bash +cp build/conan* build_vol/ +cd build_vol + +cmake -DCMAKE_BUILD_TYPE=Debug .. +make -j$(nproc) Gradido_LoginServer +echo "building done" +chmod +x ./bin/Gradido_LoginServer +./bin/Gradido_LoginServer From 246b6487ba775bdbe8f6d20c15c64a932a5149f6 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 17:52:53 +0200 Subject: [PATCH 21/44] Update docker-compose.override.yml Co-authored-by: Ulf Gebhardt --- docker-compose.override.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 51fc7b05c..951d744dd 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -78,4 +78,4 @@ services: volumes: frontend_node_modules: - conan: \ No newline at end of file + conan: From 90d8b60cc4c9e205d908804ba555bcaaf7370023 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 17:53:00 +0200 Subject: [PATCH 22/44] Update docker-compose.yml Co-authored-by: Ulf Gebhardt --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 567018dd1..ad1317556 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -133,4 +133,4 @@ networks: internal: true volumes: - db_vol: \ No newline at end of file + db_vol: From 8535730f943569598d9cf2eb593624d056039152 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 19:08:30 +0200 Subject: [PATCH 23/44] make VUE_PATH as env-variable and use "/" as default if no .env exist --- frontend/.env.dist | 3 ++- frontend/vue.config.js | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/frontend/.env.dist b/frontend/.env.dist index bbd42d002..c8323c316 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,3 +1,4 @@ LOGIN_API_URL=http://localhost/login_api/ COMMUNITY_API_STATE_BALANCE_URL=http://localhost/state-balances/ -COMMUNITY_API_TRANSACTION_CREATION_URL=http://localhost/transaction-creations/ \ No newline at end of file +COMMUNITY_API_TRANSACTION_CREATION_URL=http://localhost/transaction-creations/ +VUE_PATH=/vue \ No newline at end of file diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 5b031b7cf..f3a4094cb 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -4,6 +4,12 @@ const dotenv = require('dotenv-webpack') function resolveSrc(_path) { return path.join(__dirname, _path) } + +let vue_path = process.env.VUE_PATH +if(vue_path == undefined) { + vue_path = "" +} + // vue.config.js module.exports = { pluginOptions: { @@ -15,7 +21,7 @@ module.exports = { }, }, lintOnSave: true, - publicPath: '/vue/', + publicPath: vue_path + '/', configureWebpack: { // Set up all the aliases we use in our app. resolve: { @@ -29,5 +35,5 @@ module.exports = { // Enable CSS source maps. sourceMap: process.env.NODE_ENV !== 'production', }, - outputDir: path.resolve(__dirname, './dist/vue'), + outputDir: path.resolve(__dirname, './dist' + vue_path), } From 5e6f43b6fb17cf0ca52ab45a2fb0ee12b87516f4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 30 Mar 2021 02:05:46 +0200 Subject: [PATCH 24/44] new version 0.9.3 --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ frontend/package.json | 2 +- package.json | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7821e6c89..26f8f465f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,39 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [0.9.3](https://github.com/gradido/gradido/compare/0.9.2...0.9.3) + +- feat: Frontend tests for Content Footer [`#102`](https://github.com/gradido/gradido/pull/102) +- add check session state json function and doc [`#96`](https://github.com/gradido/gradido/pull/96) +- activity page stap 2 [`#86`](https://github.com/gradido/gradido/pull/86) +- qrcode scanner fixed, datum in activitäten fixed, sprache not null fixed [`#88`](https://github.com/gradido/gradido/pull/88) +- add additional docker container which is running skeema on startup to… [`#83`](https://github.com/gradido/gradido/pull/83) +- New fix up frontend for presentation [`#85`](https://github.com/gradido/gradido/pull/85) +- feat: Tougher eslint rules [`#77`](https://github.com/gradido/gradido/pull/77) +- feat: improve translations [`#81`](https://github.com/gradido/gradido/pull/81) +- use_correct_send_api [`#80`](https://github.com/gradido/gradido/pull/80) +- fix bug with access rights [`#78`](https://github.com/gradido/gradido/pull/78) +- make response.html better accessible [`#75`](https://github.com/gradido/gradido/pull/75) +- feat: Lokalize Validation Messages [`#67`](https://github.com/gradido/gradido/pull/67) +- feat: Localize links to gradito.net [`#74`](https://github.com/gradido/gradido/pull/74) +- fix-padding [`#73`](https://github.com/gradido/gradido/pull/73) +- Correctly use transactions API in frontend [`#69`](https://github.com/gradido/gradido/pull/69) +- Qr code scanner [`#72`](https://github.com/gradido/gradido/pull/72) +- Login fail message [`#71`](https://github.com/gradido/gradido/pull/71) +- Setup unit tests for frontend [`#55`](https://github.com/gradido/gradido/pull/55) +- Update doc [`#63`](https://github.com/gradido/gradido/pull/63) +- Add Feature in user search old frontend because Support has requested the feature long ago [`#56`](https://github.com/gradido/gradido/pull/56) +- sprache angepasst, for login, pwd, sigin [`#54`](https://github.com/gradido/gradido/pull/54) +- Improve workflows [`#53`](https://github.com/gradido/gradido/pull/53) +- setup eslint with tougher rules [`1f13507`](https://github.com/gradido/gradido/commit/1f13507eacfd93c2248fb841de5f481c9eb1e6bd) +- semicolon rule implemented [`6762a02`](https://github.com/gradido/gradido/commit/6762a028f2a3e4f2713b26bed81029defe686ad7) +- dev meeting, bernd [`a99de7f`](https://github.com/gradido/gradido/commit/a99de7f5d1f7557c0877eae565aa4263d65aaaf3) + #### [0.9.2](https://github.com/gradido/gradido/compare/0.9.1...0.9.2) +> 18 March 2021 + +- v0.9.2 [`#51`](https://github.com/gradido/gradido/pull/51) - Reload after login fixed [`#50`](https://github.com/gradido/gradido/pull/50) - Monorepo login server [`#48`](https://github.com/gradido/gradido/pull/48) - Stage0 [`#3`](https://github.com/gradido/gradido/pull/3) diff --git a/frontend/package.json b/frontend/package.json index 9243a65cc..feac8fde5 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-vue-gradido-wallet", - "version": "0.9.2", + "version": "0.9.3", "private": true, "scripts": { "start": "node server.js", diff --git a/package.json b/package.json index ea12c0a50..be933d261 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gradido", - "version": "0.9.2", + "version": "0.9.3", "description": "Gradido", "main": "index.js", "repository": "git@github.com:gradido/gradido.git", From 0fbf76857f4ba1f02ade0ae1fcf37350540baa7c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 12:32:27 +0200 Subject: [PATCH 25/44] swap " with ' --- frontend/vue.config.js | 2 +- gn | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index f3a4094cb..54709b89f 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -7,7 +7,7 @@ function resolveSrc(_path) { let vue_path = process.env.VUE_PATH if(vue_path == undefined) { - vue_path = "" + vue_path = '' } // vue.config.js diff --git a/gn b/gn index a61871987..5437e2f88 160000 --- a/gn +++ b/gn @@ -1 +1 @@ -Subproject commit a61871987261614102b11ed58791081be1954d3c +Subproject commit 5437e2f882c54efe4f501f7cd0d97f53806d0b74 From 65fccdd4fa040ea1b427ed53be4d0f477f6c34d9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 12:47:02 +0200 Subject: [PATCH 26/44] add space for linting, was that right? --- frontend/vue.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 54709b89f..d81bee9e5 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -6,7 +6,7 @@ function resolveSrc(_path) { } let vue_path = process.env.VUE_PATH -if(vue_path == undefined) { +if( vue_path == undefined ) { vue_path = '' } From 7056a45d30a1ad8e0908ec031e0795450ef0f76b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 13:49:34 +0200 Subject: [PATCH 27/44] markdown --- docu/login_server.api.md | 2 +- gn | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docu/login_server.api.md b/docu/login_server.api.md index e1eb113b5..fadd2de60 100644 --- a/docu/login_server.api.md +++ b/docu/login_server.api.md @@ -216,7 +216,7 @@ return Check if transactions on login-server for user are processed GET http://localhost/login_api/getRunningUserTasks?email=max.musterman%40gmail.de - # OR +**OR** POST http://localhost/login_api/getRunningUserTasks ```json {"email":"max.musterman@gmail.de"} diff --git a/gn b/gn index a61871987..5437e2f88 160000 --- a/gn +++ b/gn @@ -1 +1 @@ -Subproject commit a61871987261614102b11ed58791081be1954d3c +Subproject commit 5437e2f882c54efe4f501f7cd0d97f53806d0b74 From 7822b63c2342b90d68c3e44640ee14b45162a366 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 14:04:08 +0200 Subject: [PATCH 28/44] change protoc to version from gradido --- gn | 2 +- login_server/Dockerfiles/Dockerfile.dependencies | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gn b/gn index a61871987..5437e2f88 160000 --- a/gn +++ b/gn @@ -1 +1 @@ -Subproject commit a61871987261614102b11ed58791081be1954d3c +Subproject commit 5437e2f882c54efe4f501f7cd0d97f53806d0b74 diff --git a/login_server/Dockerfiles/Dockerfile.dependencies b/login_server/Dockerfiles/Dockerfile.dependencies index bfdb6b0d0..434e14899 100644 --- a/login_server/Dockerfiles/Dockerfile.dependencies +++ b/login_server/Dockerfiles/Dockerfile.dependencies @@ -8,10 +8,10 @@ ENV DOCKER_WORKDIR="/code" USER root -COPY --from=unicorny/protoc:3.9.1 /usr/bin/protoc /usr/bin/ -COPY --from=unicorny/protoc:3.9.1 /usr/lib/libprotobuf.so.20 /usr/lib/libprotobuf.so.20 -COPY --from=unicorny/protoc:3.9.1 /usr/lib/libprotoc.so.20 /usr/lib/libprotoc.so.20 -COPY --from=unicorny/protoc:3.9.1 /usr/include/google/protobuf/* /usr/include/google/protobuf/ +COPY --from=gradido/protoc:3.9.1 /usr/bin/protoc /usr/bin/ +COPY --from=gradido/protoc:3.9.1 /usr/lib/libprotobuf.so.20 /usr/lib/libprotobuf.so.20 +COPY --from=gradido/protoc:3.9.1 /usr/lib/libprotoc.so.20 /usr/lib/libprotoc.so.20 +COPY --from=gradido/protoc:3.9.1 /usr/include/google/protobuf/* /usr/include/google/protobuf/ #VOLUME /root/.conan From d18be8bcd2e397d0e62e160aa741fcf24952f9d4 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 30 Mar 2021 15:13:12 +0200 Subject: [PATCH 29/44] =?UTF-8?q?store=20aufger=C3=A4umt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.vue | 7 + frontend/src/routes/routes.js | 13 +- frontend/src/routes/starterRouter.js | 33 ---- frontend/src/store/store.js | 32 ---- frontend/src/views/GoogleMaps.vue | 169 ----------------- frontend/src/views/KontoOverview.vue | 47 ++--- frontend/src/views/KontoOverview/GddSend.vue | 82 ++++---- .../src/views/KontoOverview/GddStatus.vue | 5 +- frontend/src/views/KontoOverview/GddTable.vue | 5 +- frontend/src/views/Landing.vue | 178 ------------------ frontend/src/views/NotFoundPage.vue | 38 +--- frontend/src/views/RegularTables.vue | 115 ----------- 12 files changed, 97 insertions(+), 627 deletions(-) delete mode 100644 frontend/src/routes/starterRouter.js delete mode 100755 frontend/src/views/GoogleMaps.vue delete mode 100644 frontend/src/views/Landing.vue delete mode 100755 frontend/src/views/RegularTables.vue diff --git a/frontend/src/App.vue b/frontend/src/App.vue index e11e03d3d..f9cc4216f 100755 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -32,6 +32,13 @@ export default { components: { ParticlesBg, }, + data() { + return { + session_id: null, + email: '', + language: 'en', + } + }, created() { //console.log('xx', $cookies.get('gdd_lang')) //console.log('%cWillkommen bei Gradido %cgreen text', 'font-weight:bold', 'color: green') diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js index 887c23655..ecb3e7bba 100755 --- a/frontend/src/routes/routes.js +++ b/frontend/src/routes/routes.js @@ -2,7 +2,7 @@ import DashboardLayout from '@/views/Layout/DashboardLayout.vue' import AuthLayoutGDD from '@/views/Layout/AuthLayout_gdd.vue' import AuthLayout from '@/views/Layout/AuthLayout.vue' -// import NotFound from '@/views/NotFoundPage.vue' +import NotFound from '@/views/NotFoundPage.vue' const routes = [ { @@ -31,6 +31,11 @@ const routes = [ name: 'Explorer', component: () => import('../views/Pages/Explorer.vue'), }, + { + path: '/icons', + name: 'Icons', + component: () => import('../views/Icons.vue'), + }, ], }, { @@ -63,10 +68,9 @@ const routes = [ }, ], }, - , { - path: '/', - redirect: 'AdminOverview', + path: '/admin', + redirect: 'admin', component: AuthLayout, children: [ { @@ -79,6 +83,7 @@ const routes = [ }, ], }, + { path: '*', component: NotFound }, ] export default routes diff --git a/frontend/src/routes/starterRouter.js b/frontend/src/routes/starterRouter.js deleted file mode 100644 index 15cf360a9..000000000 --- a/frontend/src/routes/starterRouter.js +++ /dev/null @@ -1,33 +0,0 @@ -import Vue from 'vue' -import Router from 'vue-router' -import DashboardLayout from '../views/Starter/SampleLayout.vue' -import Starter from '../views/Starter/SamplePage.vue' - -Vue.use(Router) - -export default new Router({ - routes: [ - { - path: '/', - name: 'home', - redirect: '/dashboard', - component: DashboardLayout, - children: [ - { - path: 'dashboard', - name: 'dashboard', - components: { default: Starter }, - }, - ], - }, - ], - scrollBehavior: (to, from, savedPosition) => { - if (savedPosition) { - return savedPosition - } - if (to.hash) { - return { selector: to.hash } - } - return { x: 0, y: 0 } - }, -}) diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 4c5b190fa..cc65ad353 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -13,22 +13,11 @@ export const store = new Vuex.Store({ sizeDE: 'normal', sizeGB: 'big', loginfail: false, - row_form: true, - row_check: false, - row_thx: false, user: { name: '', balance: 0, balance_gdt: 0, }, - ajaxCreateData: { - session_id: '', - email: '', - amount: 0, - target_date: '', - memo: '', - auto_sign: true, - }, transactions: [], modals: false, optionAxios: { @@ -142,27 +131,6 @@ export const store = new Vuex.Store({ $cookies.remove('gdd_lang') router.push('/Login') }, - ajaxCreate: async ({ dispatch, state }) => { - //console.log('action: ajaxCreate') - - state.ajaxCreateData.amount = state.ajaxCreateData.amount * 10000 - - const result = await communityAPI.send( - state.session_id, - state.ajaxCreateData.email, - state.ajaxCreateData.amount, - state.ajaxCreateData.memo, - ) - console.log(result) - - if (result.success) { - console.log('send success') - } else { - console.log('send error') - } - return result - - }, ajaxListTransactions: async ({ commit, dispatch, state }) => { // console.log('action: ajaxListTransactions', state.session_id) // const result = await communityAPI.transactions(state.session_id) diff --git a/frontend/src/views/GoogleMaps.vue b/frontend/src/views/GoogleMaps.vue deleted file mode 100755 index 10302622d..000000000 --- a/frontend/src/views/GoogleMaps.vue +++ /dev/null @@ -1,169 +0,0 @@ - - diff --git a/frontend/src/views/KontoOverview.vue b/frontend/src/views/KontoOverview.vue index 91cfc88b6..e546b4f4e 100644 --- a/frontend/src/views/KontoOverview.vue +++ b/frontend/src/views/KontoOverview.vue @@ -2,25 +2,16 @@
    - +
    - +
    - - - - +
    @@ -28,21 +19,35 @@ import GddStatus from './KontoOverview/GddStatus.vue' import GddSend from './KontoOverview/GddSend.vue' import GddTable from './KontoOverview/GddTable.vue' -// import GddAddWork2 from './KontoOverview/GddAddWork2.vue'; -// import GddWorkTable from './KontoOverview/GddWorkTable.vue'; export default { name: 'Overview', + data() { + return { + row_form: true, + row_check: false, + row_thx: false, + transactions: [], + } + }, components: { GddStatus, GddSend, GddTable, - //GddAddWork2, - //GddWorkTable }, created() { this.$store.dispatch('accountBalance', $cookies.get('gdd_session_id')) }, + methods: { + setRows(rows) { + this.row_form = rows.row_form + this.row_check = rows.row_check + this.row_thx = rows.row_thx + }, + setTransactions(transactions) { + this.transactions = transactions + }, + }, } diff --git a/frontend/src/views/KontoOverview/GddSend.vue b/frontend/src/views/KontoOverview/GddSend.vue index 5f8208366..46cd106c9 100644 --- a/frontend/src/views/KontoOverview/GddSend.vue +++ b/frontend/src/views/KontoOverview/GddSend.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/views/RegularTables.vue b/frontend/src/views/RegularTables.vue deleted file mode 100755 index 69a493337..000000000 --- a/frontend/src/views/RegularTables.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - From 1c7a689621693a55dd4bf6dfab0889d16b0dc834 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 30 Mar 2021 15:13:47 +0200 Subject: [PATCH 30/44] fix lint --- frontend/src/views/KontoOverview.vue | 6 +++++- frontend/src/views/KontoOverview/GddSend.vue | 7 +++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/src/views/KontoOverview.vue b/frontend/src/views/KontoOverview.vue index e546b4f4e..c4fd481ac 100644 --- a/frontend/src/views/KontoOverview.vue +++ b/frontend/src/views/KontoOverview.vue @@ -11,7 +11,11 @@ @change-rows="setRows" />
    - +
diff --git a/frontend/src/views/KontoOverview/GddSend.vue b/frontend/src/views/KontoOverview/GddSend.vue index 46cd106c9..17596ba17 100644 --- a/frontend/src/views/KontoOverview/GddSend.vue +++ b/frontend/src/views/KontoOverview/GddSend.vue @@ -226,8 +226,7 @@ export default { send: false, } }, - computed: { - }, + computed: {}, methods: { async onDecode(decodedString) { const arr = JSON.parse(decodedString) @@ -244,7 +243,7 @@ export default { this.$emit('change-rows', { row_form: false, row_check: true, row_thx: false }) }, - async sendTransaction() { + async sendTransaction() { this.ajaxCreateData.amount = this.ajaxCreateData.amount * 10000 const result = await communityAPI.send( @@ -261,7 +260,7 @@ export default { this.$emit('change-rows', { row_form: false, row_check: false, row_thx: true }) } else { // console.log('send error') - alert("error") + alert('error') this.$emit('change-rows', { row_form: true, row_check: false, row_thx: false }) } }, From ce4aa2438c30966571323521e7dbb65ee5db0009 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 16:04:09 +0200 Subject: [PATCH 31/44] fix linting errors --- frontend/vue.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index d81bee9e5..9f3d486c0 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -6,7 +6,7 @@ function resolveSrc(_path) { } let vue_path = process.env.VUE_PATH -if( vue_path == undefined ) { +if (vue_path == undefined) { vue_path = '' } From 62c34430e15c523ebfcfb4cd3c3ef057ccd279bf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 30 Mar 2021 16:48:01 +0200 Subject: [PATCH 32/44] v0.9.4 --- CHANGELOG.md | 10 ++++++++++ frontend/package.json | 2 +- package.json | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26f8f465f..8c943ecd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,18 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [0.9.4](https://github.com/gradido/gradido/compare/0.9.3...0.9.4) + +- Build on run [`#103`](https://github.com/gradido/gradido/pull/103) +- update debug docker to use dependencies container pushed to docker hub [`1f002f4`](https://github.com/gradido/gradido/commit/1f002f4ed0b12d4b2bf63efceabe546d0c5b58ea) +- removed email tasks complete [`8a143be`](https://github.com/gradido/gradido/commit/8a143be8423d7bd894d4f512848895df8b9694b0) +- build login-server on docker-compose up in a docker volume so it rebuild only neccessary parts if some c++ files have changed [`0da5279`](https://github.com/gradido/gradido/commit/0da527917523530186e6effe63dc001fc99bd3e3) + #### [0.9.3](https://github.com/gradido/gradido/compare/0.9.2...0.9.3) +> 30 March 2021 + +- new version 0.9.3 [`#110`](https://github.com/gradido/gradido/pull/110) - feat: Frontend tests for Content Footer [`#102`](https://github.com/gradido/gradido/pull/102) - add check session state json function and doc [`#96`](https://github.com/gradido/gradido/pull/96) - activity page stap 2 [`#86`](https://github.com/gradido/gradido/pull/86) diff --git a/frontend/package.json b/frontend/package.json index feac8fde5..01e42e3ac 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-vue-gradido-wallet", - "version": "0.9.3", + "version": "0.9.4", "private": true, "scripts": { "start": "node server.js", diff --git a/package.json b/package.json index be933d261..593a21754 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gradido", - "version": "0.9.3", + "version": "0.9.4", "description": "Gradido", "main": "index.js", "repository": "git@github.com:gradido/gradido.git", From b64f4748da803dc513be4e28839736ea49787285 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 30 Mar 2021 17:01:27 +0200 Subject: [PATCH 33/44] lineendings config --- configs/community_server/app.php | 816 +++++++++++----------- configs/login_server/grd_login.properties | 100 +-- configs/node_server/gradido.conf | 40 +- 3 files changed, 478 insertions(+), 478 deletions(-) diff --git a/configs/community_server/app.php b/configs/community_server/app.php index 84bc93228..c52ae8047 100644 --- a/configs/community_server/app.php +++ b/configs/community_server/app.php @@ -1,408 +1,408 @@ - filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN), - - /** - * Configure basic information about the application. - * - * - namespace - The namespace to find app classes under. - * - defaultLocale - The default locale for translation, formatting currencies and numbers, date and time. - * - encoding - The encoding used for HTML + database connections. - * - base - The base directory the app resides in. If false this - * will be auto detected. - * - dir - Name of app directory. - * - webroot - The webroot directory. - * - wwwRoot - The file path to webroot. - * - baseUrl - To configure CakePHP to *not* use mod_rewrite and to - * use CakePHP pretty URLs, remove these .htaccess - * files: - * /.htaccess - * /webroot/.htaccess - * And uncomment the baseUrl key below. - * - fullBaseUrl - A base URL to use for absolute links. When set to false (default) - * CakePHP generates required value based on `HTTP_HOST` environment variable. - * However, you can define it manually to optimize performance or if you - * are concerned about people manipulating the `Host` header. - * - imageBaseUrl - Web path to the public images directory under webroot. - * - cssBaseUrl - Web path to the public css directory under webroot. - * - jsBaseUrl - Web path to the public js directory under webroot. - * - paths - Configure paths for non class based resources. Supports the - * `plugins`, `templates`, `locales` subkeys, which allow the definition of - * paths for plugins, view templates and locale files respectively. - */ - 'App' => [ - 'namespace' => 'App', - 'encoding' => env('APP_ENCODING', 'UTF-8'), - 'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'), - 'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'UTC'), - 'base' => false, - 'dir' => 'src', - 'webroot' => 'webroot', - 'wwwRoot' => WWW_ROOT, - //'baseUrl' => env('SCRIPT_NAME'), - 'fullBaseUrl' => false, - 'imageBaseUrl' => 'img/', - 'cssBaseUrl' => 'css/', - 'jsBaseUrl' => 'js/', - 'paths' => [ - 'plugins' => [ROOT . DS . 'plugins' . DS], - 'templates' => [APP . 'Template' . DS], - 'locales' => [APP . 'Locale' . DS], - ], - ], - - /** - * Security and encryption configuration - * - * - salt - A random string used in security hashing methods. - * The salt value is also used as the encryption key. - * You should treat it as extremely sensitive data. - */ - 'Security' => [ - 'salt' => env('SECURITY_SALT', '7ddf685a27d997ef36e51bdd626e7fc6b50a3abfb2971e8e59974d421116a150'), - ], - - /** - * Apply timestamps with the last modified time to static assets (js, css, images). - * Will append a querystring parameter containing the time the file was modified. - * This is useful for busting browser caches. - * - * Set to true to apply timestamps when debug is true. Set to 'force' to always - * enable timestamping regardless of debug value. - */ - 'Asset' => [ - //'timestamp' => true, - // 'cacheTime' => '+1 year' - ], - - /** - * Configure the cache adapters. - */ - 'Cache' => [ - 'default' => [ - 'className' => FileEngine::class, - 'path' => CACHE, - 'url' => env('CACHE_DEFAULT_URL', null), - ], - - /** - * Configure the cache used for general framework caching. - * Translation cache files are stored with this configuration. - * Duration will be set to '+2 minutes' in bootstrap.php when debug = true - * If you set 'className' => 'Null' core cache will be disabled. - */ - '_cake_core_' => [ - 'className' => FileEngine::class, - 'prefix' => 'myapp_cake_core_', - 'path' => CACHE . 'persistent/', - 'serialize' => true, - 'duration' => '+1 years', - 'url' => env('CACHE_CAKECORE_URL', null), - ], - - /** - * Configure the cache for model and datasource caches. This cache - * configuration is used to store schema descriptions, and table listings - * in connections. - * Duration will be set to '+2 minutes' in bootstrap.php when debug = true - */ - '_cake_model_' => [ - 'className' => FileEngine::class, - 'prefix' => 'myapp_cake_model_', - 'path' => CACHE . 'models/', - 'serialize' => true, - 'duration' => '+1 years', - 'url' => env('CACHE_CAKEMODEL_URL', null), - ], - - /** - * Configure the cache for routes. The cached routes collection is built the - * first time the routes are processed via `config/routes.php`. - * Duration will be set to '+2 seconds' in bootstrap.php when debug = true - */ - '_cake_routes_' => [ - 'className' => FileEngine::class, - 'prefix' => 'myapp_cake_routes_', - 'path' => CACHE, - 'serialize' => true, - 'duration' => '+1 years', - 'url' => env('CACHE_CAKEROUTES_URL', null), - ], - ], - - /** - * Configure the Error and Exception handlers used by your application. - * - * By default errors are displayed using Debugger, when debug is true and logged - * by Cake\Log\Log when debug is false. - * - * In CLI environments exceptions will be printed to stderr with a backtrace. - * In web environments an HTML page will be displayed for the exception. - * With debug true, framework errors like Missing Controller will be displayed. - * When debug is false, framework errors will be coerced into generic HTTP errors. - * - * Options: - * - * - `errorLevel` - int - The level of errors you are interested in capturing. - * - `trace` - boolean - Whether or not backtraces should be included in - * logged errors/exceptions. - * - `log` - boolean - Whether or not you want exceptions logged. - * - `exceptionRenderer` - string - The class responsible for rendering - * uncaught exceptions. If you choose a custom class you should place - * the file for that class in src/Error. This class needs to implement a - * render method. - * - `skipLog` - array - List of exceptions to skip for logging. Exceptions that - * extend one of the listed exceptions will also be skipped for logging. - * E.g.: - * `'skipLog' => ['Cake\Http\Exception\NotFoundException', 'Cake\Http\Exception\UnauthorizedException']` - * - `extraFatalErrorMemory` - int - The number of megabytes to increase - * the memory limit by when a fatal error is encountered. This allows - * breathing room to complete logging or error handling. - */ - 'Error' => [ - 'errorLevel' => E_ALL, - 'exceptionRenderer' => ExceptionRenderer::class, - 'skipLog' => [], - 'log' => true, - 'trace' => true, - ], - - /** - * Email configuration. - * - * By defining transports separately from delivery profiles you can easily - * re-use transport configuration across multiple profiles. - * - * You can specify multiple configurations for production, development and - * testing. - * - * Each transport needs a `className`. Valid options are as follows: - * - * Mail - Send using PHP mail function - * Smtp - Send using SMTP - * Debug - Do not send the email, just return the result - * - * You can add custom transports (or override existing transports) by adding the - * appropriate file to src/Mailer/Transport. Transports should be named - * 'YourTransport.php', where 'Your' is the name of the transport. - */ - 'EmailTransport' => [ - 'default' => [ - 'className' => MailTransport::class, - /* - * The following keys are used in SMTP transports: - */ - 'host' => 'localhost', - 'port' => 25, - 'timeout' => 30, - 'username' => null, - 'password' => null, - 'client' => null, - 'tls' => null, - 'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null), - ], - ], - - /** - * Email delivery profiles - * - * Delivery profiles allow you to predefine various properties about email - * messages from your application and give the settings a name. This saves - * duplication across your application and makes maintenance and development - * easier. Each profile accepts a number of keys. See `Cake\Mailer\Email` - * for more information. - */ - 'Email' => [ - 'default' => [ - 'transport' => 'default', - 'from' => 'you@localhost', - //'charset' => 'utf-8', - //'headerCharset' => 'utf-8', - ], - ], - - /** - * Connection information used by the ORM to connect - * to your application's datastores. - * - * ### Notes - * - Drivers include Mysql Postgres Sqlite Sqlserver - * See vendor\cakephp\cakephp\src\Database\Driver for complete list - * - Do not use periods in database name - it may lead to error. - * See https://github.com/cakephp/cakephp/issues/6471 for details. - * - 'encoding' is recommended to be set to full UTF-8 4-Byte support. - * E.g set it to 'utf8mb4' in MariaDB and MySQL and 'utf8' for any - * other RDBMS. - */ - 'Datasources' => [ - 'default' => [ - 'className' => Connection::class, - 'driver' => Mysql::class, - 'persistent' => false, - 'host' => 'mariadb', - /* - * CakePHP will use the default DB port based on the driver selected - * MySQL on MAMP uses port 8889, MAMP users will want to uncomment - * the following line and set the port accordingly - */ - //'port' => 'non_standard_port_number', - 'username' => 'root', - 'password' => '', - 'database' => 'gradido_community', - /* - * You do not need to set this flag to use full utf-8 encoding (internal default since CakePHP 3.6). - */ - //'encoding' => 'utf8mb4', - 'timezone' => 'UTC', - 'flags' => [], - 'cacheMetadata' => true, - 'log' => false, - - /** - * Set identifier quoting to true if you are using reserved words or - * special characters in your table or column names. Enabling this - * setting will result in queries built using the Query Builder having - * identifiers quoted when creating SQL. It should be noted that this - * decreases performance because each query needs to be traversed and - * manipulated before being executed. - */ - 'quoteIdentifiers' => false, - - /** - * During development, if using MySQL < 5.6, uncommenting the - * following line could boost the speed at which schema metadata is - * fetched from the database. It can also be set directly with the - * mysql configuration directive 'innodb_stats_on_metadata = 0' - * which is the recommended value in production environments - */ - //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], - - 'url' => env('DATABASE_URL', null), - ], - - /** - * The test connection is used during the test suite. - */ - 'test' => [ - 'className' => Connection::class, - 'driver' => Mysql::class, - 'persistent' => false, - 'host' => 'localhost', - //'port' => 'non_standard_port_number', - 'username' => 'my_app', - 'password' => 'secret', - 'database' => 'test_myapp', - //'encoding' => 'utf8mb4', - 'timezone' => 'UTC', - 'cacheMetadata' => true, - 'quoteIdentifiers' => false, - 'log' => false, - //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], - 'url' => env('DATABASE_TEST_URL', null), - ], - ], - - /** - * Configures logging options - */ - 'Log' => [ - 'debug' => [ - 'className' => FileLog::class, - 'path' => LOGS, - 'file' => 'debug', - 'url' => env('LOG_DEBUG_URL', null), - 'scopes' => false, - 'levels' => ['notice', 'info', 'debug'], - ], - 'error' => [ - 'className' => FileLog::class, - 'path' => LOGS, - 'file' => 'error', - 'url' => env('LOG_ERROR_URL', null), - 'scopes' => false, - 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], - ], - // To enable this dedicated query log, you need set your datasource's log flag to true - 'queries' => [ - 'className' => FileLog::class, - 'path' => LOGS, - 'file' => 'queries', - 'url' => env('LOG_QUERIES_URL', null), - 'scopes' => ['queriesLog'], - ], - ], - - /** - * Session configuration. - * - * Contains an array of settings to use for session configuration. The - * `defaults` key is used to define a default preset to use for sessions, any - * settings declared here will override the settings of the default config. - * - * ## Options - * - * - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'. Avoid using `.` in cookie names, - * as PHP will drop sessions from cookies with `.` in the name. - * - `cookiePath` - The url path for which session cookie is set. Maps to the - * `session.cookie_path` php.ini config. Defaults to base path of app. - * - `timeout` - The time in minutes the session should be valid for. - * Pass 0 to disable checking timeout. - * Please note that php.ini's session.gc_maxlifetime must be equal to or greater - * than the largest Session['timeout'] in all served websites for it to have the - * desired effect. - * - `defaults` - The default configuration set to use as a basis for your session. - * There are four built-in options: php, cake, cache, database. - * - `handler` - Can be used to enable a custom session handler. Expects an - * array with at least the `engine` key, being the name of the Session engine - * class to use for managing the session. CakePHP bundles the `CacheSession` - * and `DatabaseSession` engines. - * - `ini` - An associative array of additional ini values to set. - * - * The built-in `defaults` options are: - * - * - 'php' - Uses settings defined in your php.ini. - * - 'cake' - Saves session files in CakePHP's /tmp directory. - * - 'database' - Uses CakePHP's database sessions. - * - 'cache' - Use the Cache class to save sessions. - * - * To define a custom session handler, save it at src/Network/Session/.php. - * Make sure the class implements PHP's `SessionHandlerInterface` and set - * Session.handler to - * - * To use database sessions, load the SQL file located at config/schema/sessions.sql - */ - 'Session' => [ - 'defaults' => 'php', - ], - // Gradido specific configuration - // Login Server ip and port - 'LoginServer' => [ - 'host' => 'http://login-server', - 'port' => 1201 - ], - 'API' => [ - 'allowedCaller' => ['login-server'] - ], - 'ServerAdminEmail' => 'info@gradido.net', - 'noReplyEmail' => 'no-reply@gradido.net', - 'disableEmail' => true, - - 'GroupNode' => false -]; + filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN), + + /** + * Configure basic information about the application. + * + * - namespace - The namespace to find app classes under. + * - defaultLocale - The default locale for translation, formatting currencies and numbers, date and time. + * - encoding - The encoding used for HTML + database connections. + * - base - The base directory the app resides in. If false this + * will be auto detected. + * - dir - Name of app directory. + * - webroot - The webroot directory. + * - wwwRoot - The file path to webroot. + * - baseUrl - To configure CakePHP to *not* use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * /.htaccess + * /webroot/.htaccess + * And uncomment the baseUrl key below. + * - fullBaseUrl - A base URL to use for absolute links. When set to false (default) + * CakePHP generates required value based on `HTTP_HOST` environment variable. + * However, you can define it manually to optimize performance or if you + * are concerned about people manipulating the `Host` header. + * - imageBaseUrl - Web path to the public images directory under webroot. + * - cssBaseUrl - Web path to the public css directory under webroot. + * - jsBaseUrl - Web path to the public js directory under webroot. + * - paths - Configure paths for non class based resources. Supports the + * `plugins`, `templates`, `locales` subkeys, which allow the definition of + * paths for plugins, view templates and locale files respectively. + */ + 'App' => [ + 'namespace' => 'App', + 'encoding' => env('APP_ENCODING', 'UTF-8'), + 'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'), + 'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'UTC'), + 'base' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + //'baseUrl' => env('SCRIPT_NAME'), + 'fullBaseUrl' => false, + 'imageBaseUrl' => 'img/', + 'cssBaseUrl' => 'css/', + 'jsBaseUrl' => 'js/', + 'paths' => [ + 'plugins' => [ROOT . DS . 'plugins' . DS], + 'templates' => [APP . 'Template' . DS], + 'locales' => [APP . 'Locale' . DS], + ], + ], + + /** + * Security and encryption configuration + * + * - salt - A random string used in security hashing methods. + * The salt value is also used as the encryption key. + * You should treat it as extremely sensitive data. + */ + 'Security' => [ + 'salt' => env('SECURITY_SALT', '7ddf685a27d997ef36e51bdd626e7fc6b50a3abfb2971e8e59974d421116a150'), + ], + + /** + * Apply timestamps with the last modified time to static assets (js, css, images). + * Will append a querystring parameter containing the time the file was modified. + * This is useful for busting browser caches. + * + * Set to true to apply timestamps when debug is true. Set to 'force' to always + * enable timestamping regardless of debug value. + */ + 'Asset' => [ + //'timestamp' => true, + // 'cacheTime' => '+1 year' + ], + + /** + * Configure the cache adapters. + */ + 'Cache' => [ + 'default' => [ + 'className' => FileEngine::class, + 'path' => CACHE, + 'url' => env('CACHE_DEFAULT_URL', null), + ], + + /** + * Configure the cache used for general framework caching. + * Translation cache files are stored with this configuration. + * Duration will be set to '+2 minutes' in bootstrap.php when debug = true + * If you set 'className' => 'Null' core cache will be disabled. + */ + '_cake_core_' => [ + 'className' => FileEngine::class, + 'prefix' => 'myapp_cake_core_', + 'path' => CACHE . 'persistent/', + 'serialize' => true, + 'duration' => '+1 years', + 'url' => env('CACHE_CAKECORE_URL', null), + ], + + /** + * Configure the cache for model and datasource caches. This cache + * configuration is used to store schema descriptions, and table listings + * in connections. + * Duration will be set to '+2 minutes' in bootstrap.php when debug = true + */ + '_cake_model_' => [ + 'className' => FileEngine::class, + 'prefix' => 'myapp_cake_model_', + 'path' => CACHE . 'models/', + 'serialize' => true, + 'duration' => '+1 years', + 'url' => env('CACHE_CAKEMODEL_URL', null), + ], + + /** + * Configure the cache for routes. The cached routes collection is built the + * first time the routes are processed via `config/routes.php`. + * Duration will be set to '+2 seconds' in bootstrap.php when debug = true + */ + '_cake_routes_' => [ + 'className' => FileEngine::class, + 'prefix' => 'myapp_cake_routes_', + 'path' => CACHE, + 'serialize' => true, + 'duration' => '+1 years', + 'url' => env('CACHE_CAKEROUTES_URL', null), + ], + ], + + /** + * Configure the Error and Exception handlers used by your application. + * + * By default errors are displayed using Debugger, when debug is true and logged + * by Cake\Log\Log when debug is false. + * + * In CLI environments exceptions will be printed to stderr with a backtrace. + * In web environments an HTML page will be displayed for the exception. + * With debug true, framework errors like Missing Controller will be displayed. + * When debug is false, framework errors will be coerced into generic HTTP errors. + * + * Options: + * + * - `errorLevel` - int - The level of errors you are interested in capturing. + * - `trace` - boolean - Whether or not backtraces should be included in + * logged errors/exceptions. + * - `log` - boolean - Whether or not you want exceptions logged. + * - `exceptionRenderer` - string - The class responsible for rendering + * uncaught exceptions. If you choose a custom class you should place + * the file for that class in src/Error. This class needs to implement a + * render method. + * - `skipLog` - array - List of exceptions to skip for logging. Exceptions that + * extend one of the listed exceptions will also be skipped for logging. + * E.g.: + * `'skipLog' => ['Cake\Http\Exception\NotFoundException', 'Cake\Http\Exception\UnauthorizedException']` + * - `extraFatalErrorMemory` - int - The number of megabytes to increase + * the memory limit by when a fatal error is encountered. This allows + * breathing room to complete logging or error handling. + */ + 'Error' => [ + 'errorLevel' => E_ALL, + 'exceptionRenderer' => ExceptionRenderer::class, + 'skipLog' => [], + 'log' => true, + 'trace' => true, + ], + + /** + * Email configuration. + * + * By defining transports separately from delivery profiles you can easily + * re-use transport configuration across multiple profiles. + * + * You can specify multiple configurations for production, development and + * testing. + * + * Each transport needs a `className`. Valid options are as follows: + * + * Mail - Send using PHP mail function + * Smtp - Send using SMTP + * Debug - Do not send the email, just return the result + * + * You can add custom transports (or override existing transports) by adding the + * appropriate file to src/Mailer/Transport. Transports should be named + * 'YourTransport.php', where 'Your' is the name of the transport. + */ + 'EmailTransport' => [ + 'default' => [ + 'className' => MailTransport::class, + /* + * The following keys are used in SMTP transports: + */ + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => null, + 'password' => null, + 'client' => null, + 'tls' => null, + 'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null), + ], + ], + + /** + * Email delivery profiles + * + * Delivery profiles allow you to predefine various properties about email + * messages from your application and give the settings a name. This saves + * duplication across your application and makes maintenance and development + * easier. Each profile accepts a number of keys. See `Cake\Mailer\Email` + * for more information. + */ + 'Email' => [ + 'default' => [ + 'transport' => 'default', + 'from' => 'you@localhost', + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', + ], + ], + + /** + * Connection information used by the ORM to connect + * to your application's datastores. + * + * ### Notes + * - Drivers include Mysql Postgres Sqlite Sqlserver + * See vendor\cakephp\cakephp\src\Database\Driver for complete list + * - Do not use periods in database name - it may lead to error. + * See https://github.com/cakephp/cakephp/issues/6471 for details. + * - 'encoding' is recommended to be set to full UTF-8 4-Byte support. + * E.g set it to 'utf8mb4' in MariaDB and MySQL and 'utf8' for any + * other RDBMS. + */ + 'Datasources' => [ + 'default' => [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'persistent' => false, + 'host' => 'mariadb', + /* + * CakePHP will use the default DB port based on the driver selected + * MySQL on MAMP uses port 8889, MAMP users will want to uncomment + * the following line and set the port accordingly + */ + //'port' => 'non_standard_port_number', + 'username' => 'root', + 'password' => '', + 'database' => 'gradido_community', + /* + * You do not need to set this flag to use full utf-8 encoding (internal default since CakePHP 3.6). + */ + //'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'flags' => [], + 'cacheMetadata' => true, + 'log' => false, + + /** + * Set identifier quoting to true if you are using reserved words or + * special characters in your table or column names. Enabling this + * setting will result in queries built using the Query Builder having + * identifiers quoted when creating SQL. It should be noted that this + * decreases performance because each query needs to be traversed and + * manipulated before being executed. + */ + 'quoteIdentifiers' => false, + + /** + * During development, if using MySQL < 5.6, uncommenting the + * following line could boost the speed at which schema metadata is + * fetched from the database. It can also be set directly with the + * mysql configuration directive 'innodb_stats_on_metadata = 0' + * which is the recommended value in production environments + */ + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + + 'url' => env('DATABASE_URL', null), + ], + + /** + * The test connection is used during the test suite. + */ + 'test' => [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'persistent' => false, + 'host' => 'localhost', + //'port' => 'non_standard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'test_myapp', + //'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'quoteIdentifiers' => false, + 'log' => false, + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + 'url' => env('DATABASE_TEST_URL', null), + ], + ], + + /** + * Configures logging options + */ + 'Log' => [ + 'debug' => [ + 'className' => FileLog::class, + 'path' => LOGS, + 'file' => 'debug', + 'url' => env('LOG_DEBUG_URL', null), + 'scopes' => false, + 'levels' => ['notice', 'info', 'debug'], + ], + 'error' => [ + 'className' => FileLog::class, + 'path' => LOGS, + 'file' => 'error', + 'url' => env('LOG_ERROR_URL', null), + 'scopes' => false, + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + ], + // To enable this dedicated query log, you need set your datasource's log flag to true + 'queries' => [ + 'className' => FileLog::class, + 'path' => LOGS, + 'file' => 'queries', + 'url' => env('LOG_QUERIES_URL', null), + 'scopes' => ['queriesLog'], + ], + ], + + /** + * Session configuration. + * + * Contains an array of settings to use for session configuration. The + * `defaults` key is used to define a default preset to use for sessions, any + * settings declared here will override the settings of the default config. + * + * ## Options + * + * - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'. Avoid using `.` in cookie names, + * as PHP will drop sessions from cookies with `.` in the name. + * - `cookiePath` - The url path for which session cookie is set. Maps to the + * `session.cookie_path` php.ini config. Defaults to base path of app. + * - `timeout` - The time in minutes the session should be valid for. + * Pass 0 to disable checking timeout. + * Please note that php.ini's session.gc_maxlifetime must be equal to or greater + * than the largest Session['timeout'] in all served websites for it to have the + * desired effect. + * - `defaults` - The default configuration set to use as a basis for your session. + * There are four built-in options: php, cake, cache, database. + * - `handler` - Can be used to enable a custom session handler. Expects an + * array with at least the `engine` key, being the name of the Session engine + * class to use for managing the session. CakePHP bundles the `CacheSession` + * and `DatabaseSession` engines. + * - `ini` - An associative array of additional ini values to set. + * + * The built-in `defaults` options are: + * + * - 'php' - Uses settings defined in your php.ini. + * - 'cake' - Saves session files in CakePHP's /tmp directory. + * - 'database' - Uses CakePHP's database sessions. + * - 'cache' - Use the Cache class to save sessions. + * + * To define a custom session handler, save it at src/Network/Session/.php. + * Make sure the class implements PHP's `SessionHandlerInterface` and set + * Session.handler to + * + * To use database sessions, load the SQL file located at config/schema/sessions.sql + */ + 'Session' => [ + 'defaults' => 'php', + ], + // Gradido specific configuration + // Login Server ip and port + 'LoginServer' => [ + 'host' => 'http://login-server', + 'port' => 1201 + ], + 'API' => [ + 'allowedCaller' => ['login-server'] + ], + 'ServerAdminEmail' => 'info@gradido.net', + 'noReplyEmail' => 'no-reply@gradido.net', + 'disableEmail' => true, + + 'GroupNode' => false +]; diff --git a/configs/login_server/grd_login.properties b/configs/login_server/grd_login.properties index eccde7fb8..853dfe6af 100644 --- a/configs/login_server/grd_login.properties +++ b/configs/login_server/grd_login.properties @@ -1,51 +1,51 @@ -HTTPServer.port = 1200 -JSONServer.port = 1201 -Gradido.group_id = 1 - -crypto.server_admin_public = f909a866baec97c5460b8d7a93b72d3d4d20cc45d9f15d78bd83944eb9286b7f -crypto.server_key = a51ef8ac7ef1abf162fb7a65261acd7a -# TODO auto-generate in docker build step -crypto.app_secret = 21ffbbc616fe - -# Server admin Passphrase -# nerve execute merit pool talk hockey basic win cargo spin disagree ethics swear price purchase say clutch decrease slow half forest reform cheese able -# - -phpServer.url = http://localhost/ -phpServer.host = nginx - -loginServer.path = http://localhost/account -loginServer.default_locale = de -loginServer.db.host = mariadb -loginServer.db.name = gradido_login -loginServer.db.user = root -loginServer.db.password = -loginServer.db.port = 3306 - -email.disable = true -#email.username = -#email.sender = -#email.admin_receiver = -#email.password = -#email.smtp.url = -#email.smtp.port = - -# binary is default, for debugging also json is possible -#hedera.consensus.message_format = json -# TESTNET or MAINNET, TESTNET is default -hedera.nettype = TESTNET - -# server setup types: test, staging or production -ServerSetupType=test - - -# Session timeout in minutes -# -session.timeout = 15 - -# Disabling security features for faster develop and testing -unsecure.allow_passwort_via_json_request = 1 -unsecure.allow_auto_sign_transactions = 1 -unsecure.allow_cors_all = 1 -# default disable, passwords must contain a number, a lower character, a high character, special character, and be at least 8 characters long +HTTPServer.port = 1200 +JSONServer.port = 1201 +Gradido.group_id = 1 + +crypto.server_admin_public = f909a866baec97c5460b8d7a93b72d3d4d20cc45d9f15d78bd83944eb9286b7f +crypto.server_key = a51ef8ac7ef1abf162fb7a65261acd7a +# TODO auto-generate in docker build step +crypto.app_secret = 21ffbbc616fe + +# Server admin Passphrase +# nerve execute merit pool talk hockey basic win cargo spin disagree ethics swear price purchase say clutch decrease slow half forest reform cheese able +# + +phpServer.url = http://localhost/ +phpServer.host = nginx + +loginServer.path = http://localhost/account +loginServer.default_locale = de +loginServer.db.host = mariadb +loginServer.db.name = gradido_login +loginServer.db.user = root +loginServer.db.password = +loginServer.db.port = 3306 + +email.disable = true +#email.username = +#email.sender = +#email.admin_receiver = +#email.password = +#email.smtp.url = +#email.smtp.port = + +# binary is default, for debugging also json is possible +#hedera.consensus.message_format = json +# TESTNET or MAINNET, TESTNET is default +hedera.nettype = TESTNET + +# server setup types: test, staging or production +ServerSetupType=test + + +# Session timeout in minutes +# +session.timeout = 15 + +# Disabling security features for faster develop and testing +unsecure.allow_passwort_via_json_request = 1 +unsecure.allow_auto_sign_transactions = 1 +unsecure.allow_cors_all = 1 +# default disable, passwords must contain a number, a lower character, a high character, special character, and be at least 8 characters long unsecure.allow_all_passwords = 1 \ No newline at end of file diff --git a/configs/node_server/gradido.conf b/configs/node_server/gradido.conf index 3edf0897c..c16adf5d5 100644 --- a/configs/node_server/gradido.conf +++ b/configs/node_server/gradido.conf @@ -1,20 +1,20 @@ -worker_count = 2 -io_worker_count = 1 -data_root_folder = /opt/instance/.gradido -hedera_mirror_endpoint = hcs.testnet.mirrornode.hedera.com:5600 -sibling_node_file = /opt/instance/.gradido/sibling_nodes.txt -#group_requests_endpoint = 0.0.0.0:13701 -#record_requests_endpoint = 0.0.0.0:13702 -#manage_network_requests_endpoint = 0.0.0.0:13703 -grpc_endpoint = 0.0.0.0:13701 -json_rpc_port = 13702 - - -# larger value, larger batch, less concurrency -blockchain_append_batch_size = 1000 -#blochchain_init_batch_size = 1000 -#block_record_outbound_batch_size = 100 -general_batch_size = 1000 -group_register_topic_id = 0.0.79574 - -topic_reset_allowed = 1 +worker_count = 2 +io_worker_count = 1 +data_root_folder = /opt/instance/.gradido +hedera_mirror_endpoint = hcs.testnet.mirrornode.hedera.com:5600 +sibling_node_file = /opt/instance/.gradido/sibling_nodes.txt +#group_requests_endpoint = 0.0.0.0:13701 +#record_requests_endpoint = 0.0.0.0:13702 +#manage_network_requests_endpoint = 0.0.0.0:13703 +grpc_endpoint = 0.0.0.0:13701 +json_rpc_port = 13702 + + +# larger value, larger batch, less concurrency +blockchain_append_batch_size = 1000 +#blochchain_init_batch_size = 1000 +#block_record_outbound_batch_size = 100 +general_batch_size = 1000 +group_register_topic_id = 0.0.79574 + +topic_reset_allowed = 1 From 8d530caf9af2424e69d475fabf092855f1c1a4c6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 30 Mar 2021 17:02:39 +0200 Subject: [PATCH 34/44] lineendings nginx --- nginx/Dockerfile | 20 +++--- nginx/fastcgi.conf | 48 ++++++------- nginx/mime.types | 176 ++++++++++++++++++++++----------------------- nginx/nginx.conf | 152 +++++++++++++++++++-------------------- 4 files changed, 198 insertions(+), 198 deletions(-) diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 763ce0ed6..879d3b2c8 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,10 +1,10 @@ -FROM nginx:latest - -WORKDIR /var/www/cakephp - -COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf -COPY ./nginx/fastcgi.conf /etc/nginx/ -COPY ./nginx/mime.types /etc/nginx/ - -COPY ./community_server/webroot webroot - +FROM nginx:latest + +WORKDIR /var/www/cakephp + +COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf +COPY ./nginx/fastcgi.conf /etc/nginx/ +COPY ./nginx/mime.types /etc/nginx/ + +COPY ./community_server/webroot webroot + diff --git a/nginx/fastcgi.conf b/nginx/fastcgi.conf index c2976fe91..238f7869f 100644 --- a/nginx/fastcgi.conf +++ b/nginx/fastcgi.conf @@ -1,25 +1,25 @@ -fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; -fastcgi_param QUERY_STRING $query_string; -fastcgi_param REQUEST_METHOD $request_method; -fastcgi_param CONTENT_TYPE $content_type; -fastcgi_param CONTENT_LENGTH $content_length; - -fastcgi_param SCRIPT_NAME $fastcgi_script_name; -fastcgi_param REQUEST_URI $request_uri; -fastcgi_param DOCUMENT_URI $document_uri; -fastcgi_param DOCUMENT_ROOT $document_root; -fastcgi_param SERVER_PROTOCOL $server_protocol; -fastcgi_param REQUEST_SCHEME $scheme; -fastcgi_param HTTPS $https if_not_empty; - -fastcgi_param GATEWAY_INTERFACE CGI/1.1; -fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; - -fastcgi_param REMOTE_ADDR $remote_addr; -fastcgi_param REMOTE_PORT $remote_port; -fastcgi_param SERVER_ADDR $server_addr; -fastcgi_param SERVER_PORT $server_port; -fastcgi_param SERVER_NAME $server_name; - -# PHP only, required if PHP was built with --enable-force-cgi-redirect +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param REQUEST_SCHEME $scheme; +fastcgi_param HTTPS $https if_not_empty; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +# PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200; \ No newline at end of file diff --git a/nginx/mime.types b/nginx/mime.types index 84c644fc7..cd3d700ea 100644 --- a/nginx/mime.types +++ b/nginx/mime.types @@ -1,88 +1,88 @@ -types { - text/html html htm shtml; - text/css css; - text/xml xml; - image/gif gif; - image/jpeg jpeg jpg; - application/javascript js; - application/atom+xml atom; - application/rss+xml rss; - - text/mathml mml; - text/plain txt; - text/vnd.sun.j2me.app-descriptor jad; - text/vnd.wap.wml wml; - text/x-component htc; - - image/png png; - image/tiff tif tiff; - image/vnd.wap.wbmp wbmp; - image/x-icon ico; - image/x-jng jng; - image/x-ms-bmp bmp; - image/svg+xml svg svgz; - image/webp webp; - - application/font-woff woff; - application/java-archive jar war ear; - application/json json; - application/mac-binhex40 hqx; - application/msword doc; - application/pdf pdf; - application/postscript ps eps ai; - application/rtf rtf; - application/vnd.apple.mpegurl m3u8; - application/vnd.ms-excel xls; - application/vnd.ms-fontobject eot; - application/vnd.ms-powerpoint ppt; - application/vnd.wap.wmlc wmlc; - application/vnd.google-earth.kml+xml kml; - application/vnd.google-earth.kmz kmz; - application/x-7z-compressed 7z; - application/x-cocoa cco; - application/x-java-archive-diff jardiff; - application/x-java-jnlp-file jnlp; - application/x-makeself run; - application/x-perl pl pm; - application/x-pilot prc pdb; - application/x-rar-compressed rar; - application/x-redhat-package-manager rpm; - application/x-sea sea; - application/x-shockwave-flash swf; - application/x-stuffit sit; - application/x-tcl tcl tk; - application/x-x509-ca-cert der pem crt; - application/x-xpinstall xpi; - application/xhtml+xml xhtml; - application/xspf+xml xspf; - application/zip zip; - - application/octet-stream bin exe dll; - application/octet-stream deb; - application/octet-stream dmg; - application/octet-stream iso img; - application/octet-stream msi msp msm; - - application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; - application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; - - audio/midi mid midi kar; - audio/mpeg mp3; - audio/ogg ogg; - audio/x-m4a m4a; - audio/x-realaudio ra; - - video/3gpp 3gpp 3gp; - video/mp2t ts; - video/mp4 mp4; - video/mpeg mpeg mpg; - video/quicktime mov; - video/webm webm; - video/x-flv flv; - video/x-m4v m4v; - video/x-mng mng; - video/x-ms-asf asx asf; - video/x-ms-wmv wmv; - video/x-msvideo avi; -} +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + image/svg+xml svg svgz; + image/webp webp; + + application/font-woff woff; + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.wap.wmlc wmlc; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; + application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 064395336..2bb7a879e 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -1,77 +1,77 @@ - -server { - - listen 80 ; - listen [::]:80; - server_name 0.0.0.0; - - #include /etc/nginx/common/protect.conf; - #include /etc/nginx/common/protect_add_header.conf; - #include /etc/nginx/common/ssl.conf; - - - root /var/www/cakephp/webroot; - index index.php; - - location ~* \.(png|jpg|ico|webp)$ { - expires 30d; - } - - location ~* \.(js|css) { - # expires 1d; - expires 1d; - } - - location ~ \.php$ { - fastcgi_pass community-server:9000; - fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - # fastcgi_param PHP_VALUE "error_log=/var/www/myapp/logs/php_errors.log"; - fastcgi_buffers 16 16k; - fastcgi_buffer_size 32k; - include fastcgi_params; - - } - - location ~ /\.ht { - deny all; - } - - location /account { - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $host; - rewrite /account/(.*) /$1 break; - - #proxy_next_upstream error timeout invalid_header http_502 non_idempotent; - proxy_pass http://login-server:1200; - proxy_redirect off; - - - } - - location /login_api { - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $host; - rewrite /login_api/(.*) /$1 break; - - proxy_pass http://login-server:1201; - proxy_redirect off; - } - - location / { - try_files $uri $uri/ /index.php?$args; - } - -# access_log /var/log/nginx/access.log main; - + +server { + + listen 80 ; + listen [::]:80; + server_name 0.0.0.0; + + #include /etc/nginx/common/protect.conf; + #include /etc/nginx/common/protect_add_header.conf; + #include /etc/nginx/common/ssl.conf; + + + root /var/www/cakephp/webroot; + index index.php; + + location ~* \.(png|jpg|ico|webp)$ { + expires 30d; + } + + location ~* \.(js|css) { + # expires 1d; + expires 1d; + } + + location ~ \.php$ { + fastcgi_pass community-server:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + # fastcgi_param PHP_VALUE "error_log=/var/www/myapp/logs/php_errors.log"; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + include fastcgi_params; + + } + + location ~ /\.ht { + deny all; + } + + location /account { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $host; + rewrite /account/(.*) /$1 break; + + #proxy_next_upstream error timeout invalid_header http_502 non_idempotent; + proxy_pass http://login-server:1200; + proxy_redirect off; + + + } + + location /login_api { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $host; + rewrite /login_api/(.*) /$1 break; + + proxy_pass http://login-server:1201; + proxy_redirect off; + } + + location / { + try_files $uri $uri/ /index.php?$args; + } + +# access_log /var/log/nginx/access.log main; + } \ No newline at end of file From 45557a55c08bc8d014af2fab8848af122a18b47a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 30 Mar 2021 17:03:21 +0200 Subject: [PATCH 35/44] lineendings misc --- docker-compose.yml | 276 +++++++++++++++++------------------ docu/community-server.api.md | 262 ++++++++++++++++----------------- mariadb/Dockerfile | 40 ++--- php-fpm/Dockerfile | 16 +- 4 files changed, 297 insertions(+), 297 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b0c30ca66..ad291eac8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,138 +1,138 @@ -# This file defines the production settings. It is overwritten by docker-compose.override.yml, -# which defines the development settings. The override.yml is loaded by default. Therefore it -# is required to explicitly define if you want an production build: -# > docker-compose -f docker-compose.yml up - -version: "3.4" - -services: - ######################################################## - # FRONTEND ############################################# - ######################################################## - frontend: - image: gradido/frontend:latest - build: - context: ./frontend - target: production - networks: - - external-net - depends_on: - - nginx - ports: - - 8080:8080 - environment: - # Envs used in Dockerfile - # - DOCKER_WORKDIR="/app" - # - PORT="8080" - - BUILD_DATE - - BUILD_VERSION - - BUILD_COMMIT - - NODE_ENV="production" - # Application only envs - #- HOST=0.0.0.0 # This is nuxt specific, alternative value is HOST=webapp - #env_file: - # - ./frontend/.env - - ######################################################### - ## MARIADB ############################################## - ######################################################### - mariadb: - build: - context: . - dockerfile: ./mariadb/Dockerfile - target: mariadb_server - container_name: mariadb - environment: - - MARIADB_ALLOW_EMPTY_PASSWORD=1 - - MARIADB_USER=root - networks: - - internal-net - ports: - - 3306:3306 - volumes: - - db_vol:/var/lib/mysql - - ######################################################### - ## LOGIN SERVER ######################################### - ######################################################### - login-server: - build: - context: ./login_server/ - target: login_server - container_name: login-server - depends_on: - - mariadb - networks: - - internal-net - ports: - - 1200:1200 - - 1201:1201 - volumes: - - ./configs/login_server:/etc/grd_login - - ######################################################### - ## NGINX ################################################ - ######################################################### - ## nginx, connect login-server and community-server together (and php-fpm to community-server) - nginx: - build: - context: . - dockerfile: ./nginx/Dockerfile - container_name: nginx - networks: - - external-net - - internal-net - depends_on: - - community-server - - login-server - ports: - - 80:80 - - ######################################################### - ## COMMUNITY SERVER (cakephp with php-fpm) ############## - ######################################################### - community-server: - build: - context: . - dockerfile: ./community_server/Dockerfile - container_name: community-server - environment: - - DB_PASSWORD='' - - DB_USER='root' - - DB_DATABASE='gradido_community' - depends_on: - - mariadb - networks: - - internal-net - volumes: - - ./community_server/config/php-fpm/php-ini-overrides.ini:/etc/php/7.4/fpm/conf.d/99-overrides.ini - - ######################################################### - ## GRADIDO NODE v1 ###################################### - ######################################################### - # gradido-node: - # build: - # context: . - # dockerfile: ./gn/docker/deprecated-hedera-node/Dockerfile - # volumes: - # - ${GN_INSTANCE_FOLDER}:/opt/instance - # container_name: ${GN_CONTAINER_NAME} - - ######################################################### - ## GRADIDO NODE test ################################### - ######################################################### - # gradido-node-test: - # build: - # context: . - # dockerfile: ./gn/docker/deprecated-hedera-node/Dockerfile - # container_name: gn-test - # working_dir: /opt/gn/build - # command: ["./unit_tests"] - -networks: - external-net: - internal-net: - internal: true - -volumes: - db_vol: +# This file defines the production settings. It is overwritten by docker-compose.override.yml, +# which defines the development settings. The override.yml is loaded by default. Therefore it +# is required to explicitly define if you want an production build: +# > docker-compose -f docker-compose.yml up + +version: "3.4" + +services: + ######################################################## + # FRONTEND ############################################# + ######################################################## + frontend: + image: gradido/frontend:latest + build: + context: ./frontend + target: production + networks: + - external-net + depends_on: + - nginx + ports: + - 8080:8080 + environment: + # Envs used in Dockerfile + # - DOCKER_WORKDIR="/app" + # - PORT="8080" + - BUILD_DATE + - BUILD_VERSION + - BUILD_COMMIT + - NODE_ENV="production" + # Application only envs + #- HOST=0.0.0.0 # This is nuxt specific, alternative value is HOST=webapp + #env_file: + # - ./frontend/.env + + ######################################################### + ## MARIADB ############################################## + ######################################################### + mariadb: + build: + context: . + dockerfile: ./mariadb/Dockerfile + target: mariadb_server + container_name: mariadb + environment: + - MARIADB_ALLOW_EMPTY_PASSWORD=1 + - MARIADB_USER=root + networks: + - internal-net + ports: + - 3306:3306 + volumes: + - db_vol:/var/lib/mysql + + ######################################################### + ## LOGIN SERVER ######################################### + ######################################################### + login-server: + build: + context: ./login_server/ + target: login_server + container_name: login-server + depends_on: + - mariadb + networks: + - internal-net + ports: + - 1200:1200 + - 1201:1201 + volumes: + - ./configs/login_server:/etc/grd_login + + ######################################################### + ## NGINX ################################################ + ######################################################### + ## nginx, connect login-server and community-server together (and php-fpm to community-server) + nginx: + build: + context: . + dockerfile: ./nginx/Dockerfile + container_name: nginx + networks: + - external-net + - internal-net + depends_on: + - community-server + - login-server + ports: + - 80:80 + + ######################################################### + ## COMMUNITY SERVER (cakephp with php-fpm) ############## + ######################################################### + community-server: + build: + context: . + dockerfile: ./community_server/Dockerfile + container_name: community-server + environment: + - DB_PASSWORD='' + - DB_USER='root' + - DB_DATABASE='gradido_community' + depends_on: + - mariadb + networks: + - internal-net + volumes: + - ./community_server/config/php-fpm/php-ini-overrides.ini:/etc/php/7.4/fpm/conf.d/99-overrides.ini + + ######################################################### + ## GRADIDO NODE v1 ###################################### + ######################################################### + # gradido-node: + # build: + # context: . + # dockerfile: ./gn/docker/deprecated-hedera-node/Dockerfile + # volumes: + # - ${GN_INSTANCE_FOLDER}:/opt/instance + # container_name: ${GN_CONTAINER_NAME} + + ######################################################### + ## GRADIDO NODE test ################################### + ######################################################### + # gradido-node-test: + # build: + # context: . + # dockerfile: ./gn/docker/deprecated-hedera-node/Dockerfile + # container_name: gn-test + # working_dir: /opt/gn/build + # command: ["./unit_tests"] + +networks: + external-net: + internal-net: + internal: true + +volumes: + db_vol: diff --git a/docu/community-server.api.md b/docu/community-server.api.md index d45911c6f..bcd5da61d 100644 --- a/docu/community-server.api.md +++ b/docu/community-server.api.md @@ -1,131 +1,131 @@ -# community server api - -In this examples I assume that you use gradido with or docker-compose build on your local maschine - -## Konto Overview -return current account balance - -GET http://localhost/state-balances/ajaxGetBalance/-127182 - -If session is valid, return: -```json -{"state":"success","balance":174500} -``` -- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 174500 = 17,45 GDD - -## List Transactions -List all transactions from logged in user, currently without paging -Ajax: -GET http://localhost/state-balances/ajaxListTransactions/-127182/ -or -GET http://localhost/state-balances/ajaxListTransactions/-127182/DESC -to get transaction in descending order - -Antwort: -Wenn alles okay: -```json -{"state":"success", "transactions": - [ - { - "name": "Max Mustermann", - "email": "Maxim Mustermann", - "type": "send", - "transaction_id": 2, - "date": "2021-02-19T13:25:36+00:00", - "balance": 1920000, - "memo": "a piece of cake :)", - "pubkey": "038a6f93270dc57b91d76bf110ad3863fcb7d1b08e7692e793fcdb4467e5b6a7" - } - ], - "transactionExecutingCount": 0, - "count": 1, - "gdtSum": 0, - "timeUsed": 0.04562687873840332 -} -``` - -- name: name of other involved party or empty if unknown (if other party don't belong to group) - - if type is send, name is name of receiver - - if type is receive, name is name of sender - - if type is creation currently I use a static string ("Gradido Akademie) -- email: optional, only if type is send or receive and other user is known -- pubkey: optional, only if type is send or receive and other user isn't known -- type: type of transaction - - creation: user has get gradidos created - - send: user has send another user gradidos - - receiver: user has received gradidos from another user -- transaction_id: id of transaction in db, in stage2 also the hedera sequence number of transaction -- date: date of ordering transaction (booking date) -- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 1920000 = 192,00 GDD -- memo: Details about transaction -- pubkey: optional, if other party isn't known, hexadecimal representation of 32 Byte public key of user [0-9a-f] - -- transactionExecutingCount: how many transaction for this user currently pending and waiting for signing -- count: sum of finished transactions user is involved -- gdtSum: sum of gdt of user in cent with 2 places (Nachkommastellen) -- timeUsed: time used for getting data from db in seconds, only for analyse backend performance - -## Creation Transaction -Make a creation transaction -With new Option set in Login-Server: -```ini -unsecure.allow_auto_sign_transactions = 1 -``` -transactions can be auto-signed directly with handing in transaction. -Normally a forwarding to login-server check transactions side is neccessary to minimize security risks. - -POST http://localhost/transaction-creations/ajaxCreate -```json -{ - "session_id" : -127182, - "email": "max.musterman@gmail.de", - "amount": 10000000, - "target_date":"2021-02-19T13:25:36+00:00", - "memo":"AGE", - "auto_sign": true -} -``` -return if everything is ok: -```json -{"state":"success", "timeUsed": 0.0122} -``` -- timeUsed: time used for getting data from db in seconds, only for analyse backend performance - -## Send Coins Transaction -Make a simple GDD Transaction, send Coins from one user to other. -With new Option set in Login-Server: -```ini -unsecure.allow_auto_sign_transactions = 1 -``` -transactions can be auto-signed directly with handing in transaction. -Normally a forwarding to login-server check transactions side is neccessary to minimize security risks. - -POST http://localhost/transaction-send-coins/ajaxCreate -```json -{ - "session_id" : -127182, - "amount": 2000000, - "email": "max.musterman@gmail.de", - "memo":"Thank you :)", - "auto_sign": true -} -``` -- amout: amount to transfer, 2000000 = 200,00 GDD -- email: receiver email address, must be differ from user email -- memo: Details about transaction -- auto_sign: set to true to directly sign transaction if unsecure.allow_auto_sign_transactions = 1 is set - -return if everything is ok: -```json -{"state":"success", "timeUsed": 0.0122} -``` -- timeUsed: time used for getting data from db in seconds, only for analyse backend performance - -Than the transaction was created on community server, send to login-server, signed (if unsecure.allow_auto_sign_transactions = 1 and auto_sign = true) -and send back to community server and put into db. -After you get this answear you see the new transaction if you list transactions or call for the balance. - -Without auto-sign the transaction is pending on login-server and waits for the user to review it at -http://localhost/account/checkTransactions - - +# community server api + +In this examples I assume that you use gradido with or docker-compose build on your local maschine + +## Konto Overview +return current account balance + +GET http://localhost/state-balances/ajaxGetBalance/-127182 + +If session is valid, return: +```json +{"state":"success","balance":174500} +``` +- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 174500 = 17,45 GDD + +## List Transactions +List all transactions from logged in user, currently without paging +Ajax: +GET http://localhost/state-balances/ajaxListTransactions/-127182/ +or +GET http://localhost/state-balances/ajaxListTransactions/-127182/DESC +to get transaction in descending order + +Antwort: +Wenn alles okay: +```json +{"state":"success", "transactions": + [ + { + "name": "Max Mustermann", + "email": "Maxim Mustermann", + "type": "send", + "transaction_id": 2, + "date": "2021-02-19T13:25:36+00:00", + "balance": 1920000, + "memo": "a piece of cake :)", + "pubkey": "038a6f93270dc57b91d76bf110ad3863fcb7d1b08e7692e793fcdb4467e5b6a7" + } + ], + "transactionExecutingCount": 0, + "count": 1, + "gdtSum": 0, + "timeUsed": 0.04562687873840332 +} +``` + +- name: name of other involved party or empty if unknown (if other party don't belong to group) + - if type is send, name is name of receiver + - if type is receive, name is name of sender + - if type is creation currently I use a static string ("Gradido Akademie) +- email: optional, only if type is send or receive and other user is known +- pubkey: optional, only if type is send or receive and other user isn't known +- type: type of transaction + - creation: user has get gradidos created + - send: user has send another user gradidos + - receiver: user has received gradidos from another user +- transaction_id: id of transaction in db, in stage2 also the hedera sequence number of transaction +- date: date of ordering transaction (booking date) +- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 1920000 = 192,00 GDD +- memo: Details about transaction +- pubkey: optional, if other party isn't known, hexadecimal representation of 32 Byte public key of user [0-9a-f] + +- transactionExecutingCount: how many transaction for this user currently pending and waiting for signing +- count: sum of finished transactions user is involved +- gdtSum: sum of gdt of user in cent with 2 places (Nachkommastellen) +- timeUsed: time used for getting data from db in seconds, only for analyse backend performance + +## Creation Transaction +Make a creation transaction +With new Option set in Login-Server: +```ini +unsecure.allow_auto_sign_transactions = 1 +``` +transactions can be auto-signed directly with handing in transaction. +Normally a forwarding to login-server check transactions side is neccessary to minimize security risks. + +POST http://localhost/transaction-creations/ajaxCreate +```json +{ + "session_id" : -127182, + "email": "max.musterman@gmail.de", + "amount": 10000000, + "target_date":"2021-02-19T13:25:36+00:00", + "memo":"AGE", + "auto_sign": true +} +``` +return if everything is ok: +```json +{"state":"success", "timeUsed": 0.0122} +``` +- timeUsed: time used for getting data from db in seconds, only for analyse backend performance + +## Send Coins Transaction +Make a simple GDD Transaction, send Coins from one user to other. +With new Option set in Login-Server: +```ini +unsecure.allow_auto_sign_transactions = 1 +``` +transactions can be auto-signed directly with handing in transaction. +Normally a forwarding to login-server check transactions side is neccessary to minimize security risks. + +POST http://localhost/transaction-send-coins/ajaxCreate +```json +{ + "session_id" : -127182, + "amount": 2000000, + "email": "max.musterman@gmail.de", + "memo":"Thank you :)", + "auto_sign": true +} +``` +- amout: amount to transfer, 2000000 = 200,00 GDD +- email: receiver email address, must be differ from user email +- memo: Details about transaction +- auto_sign: set to true to directly sign transaction if unsecure.allow_auto_sign_transactions = 1 is set + +return if everything is ok: +```json +{"state":"success", "timeUsed": 0.0122} +``` +- timeUsed: time used for getting data from db in seconds, only for analyse backend performance + +Than the transaction was created on community server, send to login-server, signed (if unsecure.allow_auto_sign_transactions = 1 and auto_sign = true) +and send back to community server and put into db. +After you get this answear you see the new transaction if you list transactions or call for the balance. + +Without auto-sign the transaction is pending on login-server and waits for the user to review it at +http://localhost/account/checkTransactions + + diff --git a/mariadb/Dockerfile b/mariadb/Dockerfile index 526ebf250..d88257a8e 100644 --- a/mariadb/Dockerfile +++ b/mariadb/Dockerfile @@ -1,20 +1,20 @@ -######################################################################################################### -# mariadb server -######################################################################################################### -From mariadb/server:10.5 as mariadb_server - -ENV DOCKER_WORKDIR="/docker-entrypoint-initdb.d" - -RUN mkdir -p ${DOCKER_WORKDIR} -WORKDIR ${DOCKER_WORKDIR} - -# create databases -COPY ./mariadb/setup_dbs.sql a_setup_dbs.sql -# login server db -COPY ./login_server/skeema/ . -RUN cd ./gradido_login/ && for f in *.sql; do cp -- "$f" "../b_$f"; sed -i '1i use gradido_login;' "../b_$f"; done -# community server db -COPY ./community_server/skeema/ . -RUN cd ./gradido_community/ && for f in *.sql; do cp -- "$f" "../d_$f"; sed -i '1i use gradido_community;' "../d_$f"; done -RUN cd ./gradido_community/insert && for f in *.sql; do cp -- "$f" "../../e_$f"; sed -i '1i use gradido_community;' "../../e_$f"; done - +######################################################################################################### +# mariadb server +######################################################################################################### +From mariadb/server:10.5 as mariadb_server + +ENV DOCKER_WORKDIR="/docker-entrypoint-initdb.d" + +RUN mkdir -p ${DOCKER_WORKDIR} +WORKDIR ${DOCKER_WORKDIR} + +# create databases +COPY ./mariadb/setup_dbs.sql a_setup_dbs.sql +# login server db +COPY ./login_server/skeema/ . +RUN cd ./gradido_login/ && for f in *.sql; do cp -- "$f" "../b_$f"; sed -i '1i use gradido_login;' "../b_$f"; done +# community server db +COPY ./community_server/skeema/ . +RUN cd ./gradido_community/ && for f in *.sql; do cp -- "$f" "../d_$f"; sed -i '1i use gradido_community;' "../d_$f"; done +RUN cd ./gradido_community/insert && for f in *.sql; do cp -- "$f" "../../e_$f"; sed -i '1i use gradido_community;' "../../e_$f"; done + diff --git a/php-fpm/Dockerfile b/php-fpm/Dockerfile index 87a9a1456..d648fb564 100644 --- a/php-fpm/Dockerfile +++ b/php-fpm/Dockerfile @@ -1,9 +1,9 @@ -From phpdockerio/php74-fpm as php-fpm - -# Install selected extensions and other stuff -RUN apt-get update \ - && apt-get -y --no-install-recommends install curl php7.4-curl php7.4-fpm php7.4-mbstring php7.4-intl php7.4-xml php7.4-pdo php7.4-mysql\ - && apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* - - +From phpdockerio/php74-fpm as php-fpm + +# Install selected extensions and other stuff +RUN apt-get update \ + && apt-get -y --no-install-recommends install curl php7.4-curl php7.4-fpm php7.4-mbstring php7.4-intl php7.4-xml php7.4-pdo php7.4-mysql\ + && apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + + WORKDIR "/var/www/cakephp" \ No newline at end of file From d4d9a221644a0f0a7015014e6a16ce90acbbc602 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 30 Mar 2021 17:05:16 +0200 Subject: [PATCH 36/44] lineendings community server --- community_server/Dockerfile | 34 +- community_server/config/nginx/fastcgi.conf | 48 +- community_server/config/nginx/mime.types | 176 +- community_server/config/nginx/nginx.conf | 170 +- .../gradido_community/community_profiles.sql | 16 +- .../JsonRpcRequestClientComponent.php | 142 +- .../src/Model/Messages/Gradido/Key.php | 12 +- .../Model/Messages/Gradido/SenderAmount.php | 8 +- .../Model/Messages/Gradido/SignatureMap.php | 8 +- .../Model/Messages/Gradido/SignaturePair.php | 12 +- .../Messages/Gradido/StateCreateGroup.php | 2 +- .../Gradido/StateGroupChangeParent.php | 2 +- .../src/Model/Messages/Gradido/Timestamp.php | 16 +- .../Messages/Gradido/TimestampSeconds.php | 8 +- .../Messages/Gradido/TransactionCreation.php | 28 +- .../src/Model/Transactions/Transaction.php | 536 ++-- .../Model/Transactions/TransactionBase.php | 242 +- .../src/Template/StateBalances/overview.ctp | 296 +-- .../Template/StateUsers/list_ident_hashes.ctp | 58 +- .../Template/TransactionCreations/create.ctp | 62 +- ...nchronize_with_state_user_transactions.ctp | 92 +- .../TestCase/Controller/AppControllerTest.php | 92 +- .../webroot/css/materialdesignicons.min.css | 4 +- .../webroot/js/ensurePassphrase.js | 2318 ++++++++--------- 24 files changed, 2191 insertions(+), 2191 deletions(-) diff --git a/community_server/Dockerfile b/community_server/Dockerfile index cb4f67d27..8129d581d 100644 --- a/community_server/Dockerfile +++ b/community_server/Dockerfile @@ -1,17 +1,17 @@ -FROM phpdockerio/php74-fpm - -# install php fpm -RUN apt-get update \ - && apt-get -y --no-install-recommends install curl unzip php7.4-curl php7.4-fpm php7.4-mbstring php7.4-intl php7.4-xml php7.4-pdo php7.4-mysql \ - && apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* - -WORKDIR /var/www/cakephp -RUN mkdir logs && mkdir tmp && chmod 777 logs && chmod 777 tmp -COPY ./community_server/ . -COPY ./configs/community_server/app.php ./config/ - -RUN composer update -RUN composer dump-autoload - - - +FROM phpdockerio/php74-fpm + +# install php fpm +RUN apt-get update \ + && apt-get -y --no-install-recommends install curl unzip php7.4-curl php7.4-fpm php7.4-mbstring php7.4-intl php7.4-xml php7.4-pdo php7.4-mysql \ + && apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + +WORKDIR /var/www/cakephp +RUN mkdir logs && mkdir tmp && chmod 777 logs && chmod 777 tmp +COPY ./community_server/ . +COPY ./configs/community_server/app.php ./config/ + +RUN composer update +RUN composer dump-autoload + + + diff --git a/community_server/config/nginx/fastcgi.conf b/community_server/config/nginx/fastcgi.conf index c2976fe91..238f7869f 100644 --- a/community_server/config/nginx/fastcgi.conf +++ b/community_server/config/nginx/fastcgi.conf @@ -1,25 +1,25 @@ -fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; -fastcgi_param QUERY_STRING $query_string; -fastcgi_param REQUEST_METHOD $request_method; -fastcgi_param CONTENT_TYPE $content_type; -fastcgi_param CONTENT_LENGTH $content_length; - -fastcgi_param SCRIPT_NAME $fastcgi_script_name; -fastcgi_param REQUEST_URI $request_uri; -fastcgi_param DOCUMENT_URI $document_uri; -fastcgi_param DOCUMENT_ROOT $document_root; -fastcgi_param SERVER_PROTOCOL $server_protocol; -fastcgi_param REQUEST_SCHEME $scheme; -fastcgi_param HTTPS $https if_not_empty; - -fastcgi_param GATEWAY_INTERFACE CGI/1.1; -fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; - -fastcgi_param REMOTE_ADDR $remote_addr; -fastcgi_param REMOTE_PORT $remote_port; -fastcgi_param SERVER_ADDR $server_addr; -fastcgi_param SERVER_PORT $server_port; -fastcgi_param SERVER_NAME $server_name; - -# PHP only, required if PHP was built with --enable-force-cgi-redirect +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param REQUEST_SCHEME $scheme; +fastcgi_param HTTPS $https if_not_empty; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +# PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200; \ No newline at end of file diff --git a/community_server/config/nginx/mime.types b/community_server/config/nginx/mime.types index 84c644fc7..cd3d700ea 100644 --- a/community_server/config/nginx/mime.types +++ b/community_server/config/nginx/mime.types @@ -1,88 +1,88 @@ -types { - text/html html htm shtml; - text/css css; - text/xml xml; - image/gif gif; - image/jpeg jpeg jpg; - application/javascript js; - application/atom+xml atom; - application/rss+xml rss; - - text/mathml mml; - text/plain txt; - text/vnd.sun.j2me.app-descriptor jad; - text/vnd.wap.wml wml; - text/x-component htc; - - image/png png; - image/tiff tif tiff; - image/vnd.wap.wbmp wbmp; - image/x-icon ico; - image/x-jng jng; - image/x-ms-bmp bmp; - image/svg+xml svg svgz; - image/webp webp; - - application/font-woff woff; - application/java-archive jar war ear; - application/json json; - application/mac-binhex40 hqx; - application/msword doc; - application/pdf pdf; - application/postscript ps eps ai; - application/rtf rtf; - application/vnd.apple.mpegurl m3u8; - application/vnd.ms-excel xls; - application/vnd.ms-fontobject eot; - application/vnd.ms-powerpoint ppt; - application/vnd.wap.wmlc wmlc; - application/vnd.google-earth.kml+xml kml; - application/vnd.google-earth.kmz kmz; - application/x-7z-compressed 7z; - application/x-cocoa cco; - application/x-java-archive-diff jardiff; - application/x-java-jnlp-file jnlp; - application/x-makeself run; - application/x-perl pl pm; - application/x-pilot prc pdb; - application/x-rar-compressed rar; - application/x-redhat-package-manager rpm; - application/x-sea sea; - application/x-shockwave-flash swf; - application/x-stuffit sit; - application/x-tcl tcl tk; - application/x-x509-ca-cert der pem crt; - application/x-xpinstall xpi; - application/xhtml+xml xhtml; - application/xspf+xml xspf; - application/zip zip; - - application/octet-stream bin exe dll; - application/octet-stream deb; - application/octet-stream dmg; - application/octet-stream iso img; - application/octet-stream msi msp msm; - - application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; - application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; - - audio/midi mid midi kar; - audio/mpeg mp3; - audio/ogg ogg; - audio/x-m4a m4a; - audio/x-realaudio ra; - - video/3gpp 3gpp 3gp; - video/mp2t ts; - video/mp4 mp4; - video/mpeg mpeg mpg; - video/quicktime mov; - video/webm webm; - video/x-flv flv; - video/x-m4v m4v; - video/x-mng mng; - video/x-ms-asf asx asf; - video/x-ms-wmv wmv; - video/x-msvideo avi; -} +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + image/svg+xml svg svgz; + image/webp webp; + + application/font-woff woff; + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.wap.wmlc wmlc; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; + application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/community_server/config/nginx/nginx.conf b/community_server/config/nginx/nginx.conf index 5aa5c3095..197c96167 100644 --- a/community_server/config/nginx/nginx.conf +++ b/community_server/config/nginx/nginx.conf @@ -1,86 +1,86 @@ - -server { - - listen 80 ; - listen [::]:80; - server_name 0.0.0.0; - - #include /etc/nginx/common/protect.conf; - #include /etc/nginx/common/protect_add_header.conf; - #include /etc/nginx/common/ssl.conf; - - - root /usr/share/nginx/html/webroot; - index index.php; - - location ~* \.(png|jpg|ico|webp)\$ { - expires 30d; - } - - location ~* \.(js|css) { - # expires 1d; - expires 1d; - } - - location ~ \.php\$ { - # regex to split $uri to $fastcgi_script_name and $fastcgi_path - fastcgi_split_path_info ^(.+\.php)(/.+)$; - - # Check that the PHP script exists before passing it - try_files $fastcgi_script_name =404; - - # Bypass the fact that try_files resets $fastcgi_path_info - # see: http://trac.nginx.org/nginx/ticket/321 - set $path_info $fastcgi_path_info; - fastcgi_param PATH_INFO $path_info; - - fastcgi_index index.php; - include fastcgi.conf; - - #fastcgi_pass unix:/run/php/php7.3-fpm.sock; - fastcgi_pass 127.0.0.1:9000; - - } - - location ~ /\.ht { - deny all; - } - - location /account { - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_cache_bypass \$http_upgrade; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$remote_addr; - proxy_set_header Host \$host; - rewrite /account/(.*) /\$1 break; - - #proxy_next_upstream error timeout invalid_header http_502 non_idempotent; - proxy_pass http://login-server:1200; - proxy_redirect off; - - - } - - location /login_api { - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_cache_bypass \$http_upgrade; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$remote_addr; - proxy_set_header Host \$host; - rewrite /login_api/(.*) /\$1 break; - - proxy_pass http://login-server:1201; - proxy_redirect off; - } - - location / { - try_files \$uri \$uri/ /index.php?\$args; - } - -# access_log /var/log/nginx/access.log main; - + +server { + + listen 80 ; + listen [::]:80; + server_name 0.0.0.0; + + #include /etc/nginx/common/protect.conf; + #include /etc/nginx/common/protect_add_header.conf; + #include /etc/nginx/common/ssl.conf; + + + root /usr/share/nginx/html/webroot; + index index.php; + + location ~* \.(png|jpg|ico|webp)\$ { + expires 30d; + } + + location ~* \.(js|css) { + # expires 1d; + expires 1d; + } + + location ~ \.php\$ { + # regex to split $uri to $fastcgi_script_name and $fastcgi_path + fastcgi_split_path_info ^(.+\.php)(/.+)$; + + # Check that the PHP script exists before passing it + try_files $fastcgi_script_name =404; + + # Bypass the fact that try_files resets $fastcgi_path_info + # see: http://trac.nginx.org/nginx/ticket/321 + set $path_info $fastcgi_path_info; + fastcgi_param PATH_INFO $path_info; + + fastcgi_index index.php; + include fastcgi.conf; + + #fastcgi_pass unix:/run/php/php7.3-fpm.sock; + fastcgi_pass 127.0.0.1:9000; + + } + + location ~ /\.ht { + deny all; + } + + location /account { + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_cache_bypass \$http_upgrade; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$remote_addr; + proxy_set_header Host \$host; + rewrite /account/(.*) /\$1 break; + + #proxy_next_upstream error timeout invalid_header http_502 non_idempotent; + proxy_pass http://login-server:1200; + proxy_redirect off; + + + } + + location /login_api { + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_cache_bypass \$http_upgrade; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$remote_addr; + proxy_set_header Host \$host; + rewrite /login_api/(.*) /\$1 break; + + proxy_pass http://login-server:1201; + proxy_redirect off; + } + + location / { + try_files \$uri \$uri/ /index.php?\$args; + } + +# access_log /var/log/nginx/access.log main; + } \ No newline at end of file diff --git a/community_server/skeema/gradido_community/community_profiles.sql b/community_server/skeema/gradido_community/community_profiles.sql index ea857bebf..22926b126 100644 --- a/community_server/skeema/gradido_community/community_profiles.sql +++ b/community_server/skeema/gradido_community/community_profiles.sql @@ -1,8 +1,8 @@ -CREATE TABLE `community_profiles` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `state_user_id` int(10) unsigned NOT NULL, - `profile_img` longblob, - `profile_desc` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `state_user_id` (`state_user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE `community_profiles` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `state_user_id` int(10) unsigned NOT NULL, + `profile_img` longblob, + `profile_desc` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `state_user_id` (`state_user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php b/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php index d5ca72808..5ccf6d898 100644 --- a/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php +++ b/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php @@ -1,71 +1,71 @@ -rpcClient = new JsonRpcClient(); - } - - // @param id: if id = 0 call rand for it - public function request($method, $params = [], $id = 0) - { - - if(0 == $id) { - $id = random_int(1, 12000); - } - $this->rpcClient->query($id, $method, $params); - - $message = $this->rpcClient->encode(); - return $this->sendRequest($message); - // message: {"jsonrpc":"2.0","method":"add","params":[1,2],"id":1} - } - - public function sendRequest($message) { - $http = new Client(); - - $response = $http->post($this->getGradidoNodeUrl(), $message, ['type' => 'json']); - $responseStatus = $response->getStatusCode(); - if($responseStatus != 200) { - return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response status code isn\'t 200', 'details' => $responseStatus]; - } - //$responseType = $response->getType(); - //if($responseType != 'application/json') { -// return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response isn\'t json', 'details' => $responseType]; -// } - $json = $response->getJson(); - if($json == null) { - //$responseType = $response->getType(); - return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response isn\'t valid json']; - } - return $json; - //return ['state' => 'success', 'data' => $json]; - } - - static public function getGradidoNodeUrl() - { - $gradidoNode = Configure::read('GradidoNode'); - return $gradidoNode['host'] . ':' . $gradidoNode['port']; - } - - -} - - +rpcClient = new JsonRpcClient(); + } + + // @param id: if id = 0 call rand for it + public function request($method, $params = [], $id = 0) + { + + if(0 == $id) { + $id = random_int(1, 12000); + } + $this->rpcClient->query($id, $method, $params); + + $message = $this->rpcClient->encode(); + return $this->sendRequest($message); + // message: {"jsonrpc":"2.0","method":"add","params":[1,2],"id":1} + } + + public function sendRequest($message) { + $http = new Client(); + + $response = $http->post($this->getGradidoNodeUrl(), $message, ['type' => 'json']); + $responseStatus = $response->getStatusCode(); + if($responseStatus != 200) { + return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response status code isn\'t 200', 'details' => $responseStatus]; + } + //$responseType = $response->getType(); + //if($responseType != 'application/json') { +// return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response isn\'t json', 'details' => $responseType]; +// } + $json = $response->getJson(); + if($json == null) { + //$responseType = $response->getType(); + return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response isn\'t valid json']; + } + return $json; + //return ['state' => 'success', 'data' => $json]; + } + + static public function getGradidoNodeUrl() + { + $gradidoNode = Configure::read('GradidoNode'); + return $gradidoNode['host'] . ':' . $gradidoNode['port']; + } + + +} + + diff --git a/community_server/src/Model/Messages/Gradido/Key.php b/community_server/src/Model/Messages/Gradido/Key.php index ace440729..b6190304c 100644 --- a/community_server/src/Model/Messages/Gradido/Key.php +++ b/community_server/src/Model/Messages/Gradido/Key.php @@ -22,9 +22,9 @@ class Key extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @type string $ed25519 - * ed25519 signature (libsodium default) + * ed25519 signature (libsodium default) * @type string $ed25519_ref10 - * ed25519 ref10 signature + * ed25519 ref10 signature * } */ public function __construct($data = NULL) { @@ -33,7 +33,7 @@ class Key extends \Google\Protobuf\Internal\Message } /** - * ed25519 signature (libsodium default) + * ed25519 signature (libsodium default) * * Generated from protobuf field bytes ed25519 = 2; * @return string @@ -44,7 +44,7 @@ class Key extends \Google\Protobuf\Internal\Message } /** - * ed25519 signature (libsodium default) + * ed25519 signature (libsodium default) * * Generated from protobuf field bytes ed25519 = 2; * @param string $var @@ -59,7 +59,7 @@ class Key extends \Google\Protobuf\Internal\Message } /** - * ed25519 ref10 signature + * ed25519 ref10 signature * * Generated from protobuf field bytes ed25519_ref10 = 3; * @return string @@ -70,7 +70,7 @@ class Key extends \Google\Protobuf\Internal\Message } /** - * ed25519 ref10 signature + * ed25519 ref10 signature * * Generated from protobuf field bytes ed25519_ref10 = 3; * @param string $var diff --git a/community_server/src/Model/Messages/Gradido/SenderAmount.php b/community_server/src/Model/Messages/Gradido/SenderAmount.php index 9b423947e..52a41f71f 100644 --- a/community_server/src/Model/Messages/Gradido/SenderAmount.php +++ b/community_server/src/Model/Messages/Gradido/SenderAmount.php @@ -22,7 +22,7 @@ class SenderAmount extends \Google\Protobuf\Internal\Message */ private $amount = 0; /** - * sender balance after transaction, including perishability + * sender balance after transaction, including perishability * * Generated from protobuf field sint64 senderFinalBalance = 3; */ @@ -37,7 +37,7 @@ class SenderAmount extends \Google\Protobuf\Internal\Message * @type string $ed25519_sender_pubkey * @type int|string $amount * @type int|string $senderFinalBalance - * sender balance after transaction, including perishability + * sender balance after transaction, including perishability * } */ public function __construct($data = NULL) { @@ -90,7 +90,7 @@ class SenderAmount extends \Google\Protobuf\Internal\Message } /** - * sender balance after transaction, including perishability + * sender balance after transaction, including perishability * * Generated from protobuf field sint64 senderFinalBalance = 3; * @return int|string @@ -101,7 +101,7 @@ class SenderAmount extends \Google\Protobuf\Internal\Message } /** - * sender balance after transaction, including perishability + * sender balance after transaction, including perishability * * Generated from protobuf field sint64 senderFinalBalance = 3; * @param int|string $var diff --git a/community_server/src/Model/Messages/Gradido/SignatureMap.php b/community_server/src/Model/Messages/Gradido/SignatureMap.php index ea6f16150..228282747 100644 --- a/community_server/src/Model/Messages/Gradido/SignatureMap.php +++ b/community_server/src/Model/Messages/Gradido/SignatureMap.php @@ -14,7 +14,7 @@ use Google\Protobuf\Internal\GPBUtil; class SignatureMap extends \Google\Protobuf\Internal\Message { /** - * Each signature pair corresponds to a unique Key required to sign the transaction. + * Each signature pair corresponds to a unique Key required to sign the transaction. * * Generated from protobuf field repeated .model.messages.gradido.SignaturePair sigPair = 1; */ @@ -27,7 +27,7 @@ class SignatureMap extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @type \Model\Messages\Gradido\SignaturePair[]|\Google\Protobuf\Internal\RepeatedField $sigPair - * Each signature pair corresponds to a unique Key required to sign the transaction. + * Each signature pair corresponds to a unique Key required to sign the transaction. * } */ public function __construct($data = NULL) { @@ -36,7 +36,7 @@ class SignatureMap extends \Google\Protobuf\Internal\Message } /** - * Each signature pair corresponds to a unique Key required to sign the transaction. + * Each signature pair corresponds to a unique Key required to sign the transaction. * * Generated from protobuf field repeated .model.messages.gradido.SignaturePair sigPair = 1; * @return \Google\Protobuf\Internal\RepeatedField @@ -47,7 +47,7 @@ class SignatureMap extends \Google\Protobuf\Internal\Message } /** - * Each signature pair corresponds to a unique Key required to sign the transaction. + * Each signature pair corresponds to a unique Key required to sign the transaction. * * Generated from protobuf field repeated .model.messages.gradido.SignaturePair sigPair = 1; * @param \Model\Messages\Gradido\SignaturePair[]|\Google\Protobuf\Internal\RepeatedField $var diff --git a/community_server/src/Model/Messages/Gradido/SignaturePair.php b/community_server/src/Model/Messages/Gradido/SignaturePair.php index 4575a7d38..203eb5677 100644 --- a/community_server/src/Model/Messages/Gradido/SignaturePair.php +++ b/community_server/src/Model/Messages/Gradido/SignaturePair.php @@ -27,9 +27,9 @@ class SignaturePair extends \Google\Protobuf\Internal\Message * * @type string $pubKey * @type string $ed25519 - * ed25519 signature (libsodium default) + * ed25519 signature (libsodium default) * @type string $ed25519_ref10 - * ed25519 ref10 signature + * ed25519 ref10 signature * } */ public function __construct($data = NULL) { @@ -60,7 +60,7 @@ class SignaturePair extends \Google\Protobuf\Internal\Message } /** - * ed25519 signature (libsodium default) + * ed25519 signature (libsodium default) * * Generated from protobuf field bytes ed25519 = 2; * @return string @@ -71,7 +71,7 @@ class SignaturePair extends \Google\Protobuf\Internal\Message } /** - * ed25519 signature (libsodium default) + * ed25519 signature (libsodium default) * * Generated from protobuf field bytes ed25519 = 2; * @param string $var @@ -86,7 +86,7 @@ class SignaturePair extends \Google\Protobuf\Internal\Message } /** - * ed25519 ref10 signature + * ed25519 ref10 signature * * Generated from protobuf field bytes ed25519_ref10 = 3; * @return string @@ -97,7 +97,7 @@ class SignaturePair extends \Google\Protobuf\Internal\Message } /** - * ed25519 ref10 signature + * ed25519 ref10 signature * * Generated from protobuf field bytes ed25519_ref10 = 3; * @param string $var diff --git a/community_server/src/Model/Messages/Gradido/StateCreateGroup.php b/community_server/src/Model/Messages/Gradido/StateCreateGroup.php index dad246745..4273ef93d 100644 --- a/community_server/src/Model/Messages/Gradido/StateCreateGroup.php +++ b/community_server/src/Model/Messages/Gradido/StateCreateGroup.php @@ -9,7 +9,7 @@ use Google\Protobuf\Internal\RepeatedField; use Google\Protobuf\Internal\GPBUtil; /** - * need signature from this group and from parent (if it isn't zero) + * need signature from this group and from parent (if it isn't zero) * * Generated from protobuf message model.messages.gradido.StateCreateGroup */ diff --git a/community_server/src/Model/Messages/Gradido/StateGroupChangeParent.php b/community_server/src/Model/Messages/Gradido/StateGroupChangeParent.php index 9cd15175a..c5371d76c 100644 --- a/community_server/src/Model/Messages/Gradido/StateGroupChangeParent.php +++ b/community_server/src/Model/Messages/Gradido/StateGroupChangeParent.php @@ -9,7 +9,7 @@ use Google\Protobuf\Internal\RepeatedField; use Google\Protobuf\Internal\GPBUtil; /** - * need signature from this group and from both parents (if it isn't zero) + * need signature from this group and from both parents (if it isn't zero) * * Generated from protobuf message model.messages.gradido.StateGroupChangeParent */ diff --git a/community_server/src/Model/Messages/Gradido/Timestamp.php b/community_server/src/Model/Messages/Gradido/Timestamp.php index 7b2316720..19721729b 100644 --- a/community_server/src/Model/Messages/Gradido/Timestamp.php +++ b/community_server/src/Model/Messages/Gradido/Timestamp.php @@ -16,13 +16,13 @@ use Google\Protobuf\Internal\GPBUtil; class Timestamp extends \Google\Protobuf\Internal\Message { /** - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * * Generated from protobuf field int64 seconds = 1; */ private $seconds = 0; /** - * Number of nanoseconds since the start of the last second + * Number of nanoseconds since the start of the last second * * Generated from protobuf field int32 nanos = 2; */ @@ -35,9 +35,9 @@ class Timestamp extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @type int|string $seconds - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * @type int $nanos - * Number of nanoseconds since the start of the last second + * Number of nanoseconds since the start of the last second * } */ public function __construct($data = NULL) { @@ -46,7 +46,7 @@ class Timestamp extends \Google\Protobuf\Internal\Message } /** - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * * Generated from protobuf field int64 seconds = 1; * @return int|string @@ -57,7 +57,7 @@ class Timestamp extends \Google\Protobuf\Internal\Message } /** - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * * Generated from protobuf field int64 seconds = 1; * @param int|string $var @@ -72,7 +72,7 @@ class Timestamp extends \Google\Protobuf\Internal\Message } /** - * Number of nanoseconds since the start of the last second + * Number of nanoseconds since the start of the last second * * Generated from protobuf field int32 nanos = 2; * @return int @@ -83,7 +83,7 @@ class Timestamp extends \Google\Protobuf\Internal\Message } /** - * Number of nanoseconds since the start of the last second + * Number of nanoseconds since the start of the last second * * Generated from protobuf field int32 nanos = 2; * @param int $var diff --git a/community_server/src/Model/Messages/Gradido/TimestampSeconds.php b/community_server/src/Model/Messages/Gradido/TimestampSeconds.php index 2646a06c9..2a588a312 100644 --- a/community_server/src/Model/Messages/Gradido/TimestampSeconds.php +++ b/community_server/src/Model/Messages/Gradido/TimestampSeconds.php @@ -16,7 +16,7 @@ use Google\Protobuf\Internal\GPBUtil; class TimestampSeconds extends \Google\Protobuf\Internal\Message { /** - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * * Generated from protobuf field int64 seconds = 1; */ @@ -29,7 +29,7 @@ class TimestampSeconds extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @type int|string $seconds - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * } */ public function __construct($data = NULL) { @@ -38,7 +38,7 @@ class TimestampSeconds extends \Google\Protobuf\Internal\Message } /** - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * * Generated from protobuf field int64 seconds = 1; * @return int|string @@ -49,7 +49,7 @@ class TimestampSeconds extends \Google\Protobuf\Internal\Message } /** - * Number of complete seconds since the start of the epoch + * Number of complete seconds since the start of the epoch * * Generated from protobuf field int64 seconds = 1; * @param int|string $var diff --git a/community_server/src/Model/Messages/Gradido/TransactionCreation.php b/community_server/src/Model/Messages/Gradido/TransactionCreation.php index 1c8f33dba..ae5f86e07 100644 --- a/community_server/src/Model/Messages/Gradido/TransactionCreation.php +++ b/community_server/src/Model/Messages/Gradido/TransactionCreation.php @@ -9,27 +9,27 @@ use Google\Protobuf\Internal\RepeatedField; use Google\Protobuf\Internal\GPBUtil; /** - * need signature from group admin or - * percent of group users another than the receiver + * need signature from group admin or + * percent of group users another than the receiver * * Generated from protobuf message model.messages.gradido.TransactionCreation */ class TransactionCreation extends \Google\Protobuf\Internal\Message { /** - * 40 Byte + * 40 Byte * * Generated from protobuf field .model.messages.gradido.ReceiverAmount receiverAmount = 1; */ private $receiverAmount = null; /** - * 4 Byte + * 4 Byte * * Generated from protobuf field sint32 ident_hash = 2; */ private $ident_hash = 0; /** - * 8 Byte + * 8 Byte * * Generated from protobuf field .model.messages.gradido.TimestampSeconds target_date = 3; */ @@ -42,11 +42,11 @@ class TransactionCreation extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @type \Model\Messages\Gradido\ReceiverAmount $receiverAmount - * 40 Byte + * 40 Byte * @type int $ident_hash - * 4 Byte + * 4 Byte * @type \Model\Messages\Gradido\TimestampSeconds $target_date - * 8 Byte + * 8 Byte * } */ public function __construct($data = NULL) { @@ -55,7 +55,7 @@ class TransactionCreation extends \Google\Protobuf\Internal\Message } /** - * 40 Byte + * 40 Byte * * Generated from protobuf field .model.messages.gradido.ReceiverAmount receiverAmount = 1; * @return \Model\Messages\Gradido\ReceiverAmount @@ -66,7 +66,7 @@ class TransactionCreation extends \Google\Protobuf\Internal\Message } /** - * 40 Byte + * 40 Byte * * Generated from protobuf field .model.messages.gradido.ReceiverAmount receiverAmount = 1; * @param \Model\Messages\Gradido\ReceiverAmount $var @@ -81,7 +81,7 @@ class TransactionCreation extends \Google\Protobuf\Internal\Message } /** - * 4 Byte + * 4 Byte * * Generated from protobuf field sint32 ident_hash = 2; * @return int @@ -92,7 +92,7 @@ class TransactionCreation extends \Google\Protobuf\Internal\Message } /** - * 4 Byte + * 4 Byte * * Generated from protobuf field sint32 ident_hash = 2; * @param int $var @@ -107,7 +107,7 @@ class TransactionCreation extends \Google\Protobuf\Internal\Message } /** - * 8 Byte + * 8 Byte * * Generated from protobuf field .model.messages.gradido.TimestampSeconds target_date = 3; * @return \Model\Messages\Gradido\TimestampSeconds @@ -118,7 +118,7 @@ class TransactionCreation extends \Google\Protobuf\Internal\Message } /** - * 8 Byte + * 8 Byte * * Generated from protobuf field .model.messages.gradido.TimestampSeconds target_date = 3; * @param \Model\Messages\Gradido\TimestampSeconds $var diff --git a/community_server/src/Model/Transactions/Transaction.php b/community_server/src/Model/Transactions/Transaction.php index 76353595a..99bd5fae7 100644 --- a/community_server/src/Model/Transactions/Transaction.php +++ b/community_server/src/Model/Transactions/Transaction.php @@ -1,269 +1,269 @@ -mProtoTransaction = $base64Data; - $this->mTransactionBody = new TransactionBody($this->mProtoTransaction->getBodyBytes()); - return; - } - - try { - $transactionBin = sodium_base642bin($base64Data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING); - } catch(\SodiumException $e) { - //$this->addError('Transaction', $e->getMessage());// . ' ' . $base64Data); - //return; - $transactionBin = base64_decode($base64Data, true); - if($transactionBin == false) { - $this->addError('Transaction', $e->getMessage());// . ' ' . $base64Data); - return; - } - } - //*/} - - if($transactionBin == false) { - //$this->addError('base64 decode failed'); - $this->addError('Transaction', 'base64 decode error: ' . $base64Data); - } else { - //var_dump($transactionBin); - $this->mProtoTransaction = new \Model\Messages\Gradido\Transaction(); - try { - $this->mProtoTransaction->mergeFromString($transactionBin); - //var_dump($this->mProtoTransaction); - // cannot catch Exception with cakePHP, I don't know why - } catch(\Google\Protobuf\Internal\GPBDecodeException $e) { - //var_dump($e); - $this->addError('Transaction', $e->getMessage()); - return; - }//*/ - - //echo 'serialize to json:
'; - //echo $this->mProtoTransaction->serializeToJsonString(); - //echo "body bytes:
"; - //var_dump($this->mProtoTransaction->getBodyBytes()); - //echo "
end body bytes
"; - $this->mTransactionBody = new TransactionBody($this->mProtoTransaction->getBodyBytes()); - } - } - - static public function build(\Model\Messages\Gradido\TransactionBody $transactionBody, $senderKeyPair) - { - $protoTransaction = new \Model\Messages\Gradido\Transaction(); - - $recevied = new \Model\Messages\Gradido\TimestampSeconds(); - $recevied->setSeconds(time()); - $protoTransaction->setReceived($recevied); - - $bodyBytes = $transactionBody->serializeToString(); - - $sigMap = SignatureMap::build($bodyBytes, [$senderKeyPair]); - $protoTransaction->setSigMap($sigMap->getProto()); - - $protoTransaction->setBodyBytes($bodyBytes); - - return $protoTransaction; - - } - - public function getTransactionBody() { - return $this->mTransactionBody; - } - - public function getFirstPublic() { - $sigPairs = $this->mProtoTransaction->getSigMap()->getSigPair(); - return $sigPairs[0]->getPubKey(); - } - - public function getId() { - return $this->mProtoTransaction->getId(); - } - - public function validate() { - $sigMap = $this->mProtoTransaction->getSigMap(); - if(!$sigMap) { - $this->addError('Transaction', 'signature map is zero'); - return false; - } - //var_dump($sigMap); - //die(); - $sigPairs = $sigMap->getSigPair(); - $bodyBytes = $this->mProtoTransaction->getBodyBytes(); - - - if(!$sigPairs || count($sigPairs) < 1) { - $this->addError('Transaction::validate', 'no signature found'); - return false; - } - - // check signature(s) - foreach($sigPairs as $sigPair) { - //echo 'sig Pair: '; var_dump($sigPair); echo "
"; - $pubkey = $sigPair->getPubKey(); - $signature = $sigPair->getEd25519(); - //echo "verify bodybytes:
" . bin2hex($bodyBytes) . '
'; - if (!\Sodium\crypto_sign_verify_detached($signature, $bodyBytes, $pubkey)) { - $this->addError('Transaction::validate', 'signature for key ' . bin2hex($pubkey) . ' isn\'t valid ' ); - return false; - } - } - - if(!$this->mTransactionBody->validate($sigPairs)) { - $this->addErrors($this->mTransactionBody->getErrors()); - return false; - } - - return true; - } - - public function save() - { - $connection = ConnectionManager::get('default'); - $connection->begin(); - //id transaction_id signature pubkey - - if (!$this->mTransactionBody->save($this->getFirstPublic(), $this->mProtoTransaction->getSigMap())) { - $this->addErrors($this->mTransactionBody->getErrors()); - $connection->rollback(); - return false; - } - - // save transaction signatures - $transactionsSignaturesTable = TableRegistry::getTableLocator()->get('transaction_signatures'); - $transactionId = $this->mTransactionBody->getTransactionID(); - //signature pubkey - - $sigPairs = $this->mProtoTransaction->getSigMap()->getSigPair(); - //echo "sigPairs: "; var_dump($sigPairs); - $signatureEntitys = []; - foreach($sigPairs as $sigPair) { - $signatureEntity = $transactionsSignaturesTable->newEntity(); - $signatureEntity->transaction_id = $transactionId; - $signatureEntity->signature = $sigPair->getEd25519(); - $signatureEntity->pubkey = $sigPair->getPubKey(); - array_push($signatureEntitys, $signatureEntity); - } - //debug($signatureEntitys); - if(!$transactionsSignaturesTable->saveMany($signatureEntitys)) { - foreach($signatureEntitys as $entity) { - $errors = $entity->getErrors(); - if(!$errors && count($errors) > 0) { - $pubkeyHex = bin2hex($entity->pubkey); - $this->addError('Transaction::save', 'error saving signature for pubkey: ' . $pubkeyHex . ', with errors: ' . json_encode($errors) ); - } - } - $connection->rollback(); - return false; - } - - $connection->commit(); - - $this->mTransactionBody->getSpecificTransaction()->sendNotificationEmail($this->mTransactionBody->getMemo()); - - return true; - } - - static public function fromTable($id) - { - $transactionsTable = TableRegistry::getTableLocator()->get('transactions'); - $transactionEntry = $transactionsTable - ->find('all') - ->where(['id' => $id]) - ->contain([ - 'TransactionCreations', - 'TransactionSendCoins', - 'TransactionSignatures']) - ->first(); - //var_dump($transactionEntry->toArray()); - $protoTransaction = new \Model\Messages\Gradido\Transaction(); - - - - $protoTransaction->setId($transactionEntry->id); - - - $recevied = new \Model\Messages\Gradido\TimestampSeconds(); - $recevied->setSeconds($transactionEntry->received->getTimestamp()); - $protoTransaction->setReceived($recevied); - - - $sigMap = SignatureMap::fromEntity($transactionEntry->transaction_signatures); - $protoTransaction->setSigMap($sigMap->getProto()); - - //echo "sig map: check
"; - $protoTransaction->setTxHash(stream_get_contents($transactionEntry->tx_hash)); - - $body = TransactionBody::fromEntity($transactionEntry->memo, $transactionEntry); - if(is_array($body)) { - return ['state' => 'error', 'msg' => 'error creating body transaction', 'details' => $body]; - } - - // validate signatures - $sigPairs = $sigMap->getProto()->getSigPair(); - - if(!$sigPairs || count($sigPairs) < 1) { - return ['state' => 'error', 'msg' => 'error no signatures found']; - } - - //echo "verify bodybytes:
" . bin2hex($bodyBytes) . '
'; - $created = new \Model\Messages\Gradido\TimestampSeconds(); - $created->setSeconds($recevied->getSeconds()); - $body->setCreated($created); - $bodyBytes = $body->serializeToString(); - $createTrys = 0; - $createRight = false; - // check signature(s) and - // try to get created field of TransactionBody right, because it wasn't saved - foreach($sigPairs as $sigPair) { - //echo 'sig Pair: '; var_dump($sigPair); echo "
"; - $pubkey = $sigPair->getPubKey(); - $signature = $sigPair->getEd25519(); - if(!$createRight) { - while($createTrys < 500) { - if(\Sodium\crypto_sign_verify_detached($signature, $bodyBytes, $pubkey)) { - $createRight = true; - break; - } else { - $createTrys++; - $created->setSeconds($created->getSeconds() - 1); - //$body->setCreated($created); - $bodyBytes = $body->serializeToString(); - } - } - } - - if (!\Sodium\crypto_sign_verify_detached($signature, $bodyBytes, $pubkey)) { - return ['state' => 'error', 'msg' => 'signature for key ' . bin2hex($pubkey) . ' isn\'t valid ']; - } - } - - $protoTransaction->setBodyBytes($bodyBytes); - - - - return $protoTransaction; - } - +mProtoTransaction = $base64Data; + $this->mTransactionBody = new TransactionBody($this->mProtoTransaction->getBodyBytes()); + return; + } + + try { + $transactionBin = sodium_base642bin($base64Data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING); + } catch(\SodiumException $e) { + //$this->addError('Transaction', $e->getMessage());// . ' ' . $base64Data); + //return; + $transactionBin = base64_decode($base64Data, true); + if($transactionBin == false) { + $this->addError('Transaction', $e->getMessage());// . ' ' . $base64Data); + return; + } + } + //*/} + + if($transactionBin == false) { + //$this->addError('base64 decode failed'); + $this->addError('Transaction', 'base64 decode error: ' . $base64Data); + } else { + //var_dump($transactionBin); + $this->mProtoTransaction = new \Model\Messages\Gradido\Transaction(); + try { + $this->mProtoTransaction->mergeFromString($transactionBin); + //var_dump($this->mProtoTransaction); + // cannot catch Exception with cakePHP, I don't know why + } catch(\Google\Protobuf\Internal\GPBDecodeException $e) { + //var_dump($e); + $this->addError('Transaction', $e->getMessage()); + return; + }//*/ + + //echo 'serialize to json:
'; + //echo $this->mProtoTransaction->serializeToJsonString(); + //echo "body bytes:
"; + //var_dump($this->mProtoTransaction->getBodyBytes()); + //echo "
end body bytes
"; + $this->mTransactionBody = new TransactionBody($this->mProtoTransaction->getBodyBytes()); + } + } + + static public function build(\Model\Messages\Gradido\TransactionBody $transactionBody, $senderKeyPair) + { + $protoTransaction = new \Model\Messages\Gradido\Transaction(); + + $recevied = new \Model\Messages\Gradido\TimestampSeconds(); + $recevied->setSeconds(time()); + $protoTransaction->setReceived($recevied); + + $bodyBytes = $transactionBody->serializeToString(); + + $sigMap = SignatureMap::build($bodyBytes, [$senderKeyPair]); + $protoTransaction->setSigMap($sigMap->getProto()); + + $protoTransaction->setBodyBytes($bodyBytes); + + return $protoTransaction; + + } + + public function getTransactionBody() { + return $this->mTransactionBody; + } + + public function getFirstPublic() { + $sigPairs = $this->mProtoTransaction->getSigMap()->getSigPair(); + return $sigPairs[0]->getPubKey(); + } + + public function getId() { + return $this->mProtoTransaction->getId(); + } + + public function validate() { + $sigMap = $this->mProtoTransaction->getSigMap(); + if(!$sigMap) { + $this->addError('Transaction', 'signature map is zero'); + return false; + } + //var_dump($sigMap); + //die(); + $sigPairs = $sigMap->getSigPair(); + $bodyBytes = $this->mProtoTransaction->getBodyBytes(); + + + if(!$sigPairs || count($sigPairs) < 1) { + $this->addError('Transaction::validate', 'no signature found'); + return false; + } + + // check signature(s) + foreach($sigPairs as $sigPair) { + //echo 'sig Pair: '; var_dump($sigPair); echo "
"; + $pubkey = $sigPair->getPubKey(); + $signature = $sigPair->getEd25519(); + //echo "verify bodybytes:
" . bin2hex($bodyBytes) . '
'; + if (!\Sodium\crypto_sign_verify_detached($signature, $bodyBytes, $pubkey)) { + $this->addError('Transaction::validate', 'signature for key ' . bin2hex($pubkey) . ' isn\'t valid ' ); + return false; + } + } + + if(!$this->mTransactionBody->validate($sigPairs)) { + $this->addErrors($this->mTransactionBody->getErrors()); + return false; + } + + return true; + } + + public function save() + { + $connection = ConnectionManager::get('default'); + $connection->begin(); + //id transaction_id signature pubkey + + if (!$this->mTransactionBody->save($this->getFirstPublic(), $this->mProtoTransaction->getSigMap())) { + $this->addErrors($this->mTransactionBody->getErrors()); + $connection->rollback(); + return false; + } + + // save transaction signatures + $transactionsSignaturesTable = TableRegistry::getTableLocator()->get('transaction_signatures'); + $transactionId = $this->mTransactionBody->getTransactionID(); + //signature pubkey + + $sigPairs = $this->mProtoTransaction->getSigMap()->getSigPair(); + //echo "sigPairs: "; var_dump($sigPairs); + $signatureEntitys = []; + foreach($sigPairs as $sigPair) { + $signatureEntity = $transactionsSignaturesTable->newEntity(); + $signatureEntity->transaction_id = $transactionId; + $signatureEntity->signature = $sigPair->getEd25519(); + $signatureEntity->pubkey = $sigPair->getPubKey(); + array_push($signatureEntitys, $signatureEntity); + } + //debug($signatureEntitys); + if(!$transactionsSignaturesTable->saveMany($signatureEntitys)) { + foreach($signatureEntitys as $entity) { + $errors = $entity->getErrors(); + if(!$errors && count($errors) > 0) { + $pubkeyHex = bin2hex($entity->pubkey); + $this->addError('Transaction::save', 'error saving signature for pubkey: ' . $pubkeyHex . ', with errors: ' . json_encode($errors) ); + } + } + $connection->rollback(); + return false; + } + + $connection->commit(); + + $this->mTransactionBody->getSpecificTransaction()->sendNotificationEmail($this->mTransactionBody->getMemo()); + + return true; + } + + static public function fromTable($id) + { + $transactionsTable = TableRegistry::getTableLocator()->get('transactions'); + $transactionEntry = $transactionsTable + ->find('all') + ->where(['id' => $id]) + ->contain([ + 'TransactionCreations', + 'TransactionSendCoins', + 'TransactionSignatures']) + ->first(); + //var_dump($transactionEntry->toArray()); + $protoTransaction = new \Model\Messages\Gradido\Transaction(); + + + + $protoTransaction->setId($transactionEntry->id); + + + $recevied = new \Model\Messages\Gradido\TimestampSeconds(); + $recevied->setSeconds($transactionEntry->received->getTimestamp()); + $protoTransaction->setReceived($recevied); + + + $sigMap = SignatureMap::fromEntity($transactionEntry->transaction_signatures); + $protoTransaction->setSigMap($sigMap->getProto()); + + //echo "sig map: check
"; + $protoTransaction->setTxHash(stream_get_contents($transactionEntry->tx_hash)); + + $body = TransactionBody::fromEntity($transactionEntry->memo, $transactionEntry); + if(is_array($body)) { + return ['state' => 'error', 'msg' => 'error creating body transaction', 'details' => $body]; + } + + // validate signatures + $sigPairs = $sigMap->getProto()->getSigPair(); + + if(!$sigPairs || count($sigPairs) < 1) { + return ['state' => 'error', 'msg' => 'error no signatures found']; + } + + //echo "verify bodybytes:
" . bin2hex($bodyBytes) . '
'; + $created = new \Model\Messages\Gradido\TimestampSeconds(); + $created->setSeconds($recevied->getSeconds()); + $body->setCreated($created); + $bodyBytes = $body->serializeToString(); + $createTrys = 0; + $createRight = false; + // check signature(s) and + // try to get created field of TransactionBody right, because it wasn't saved + foreach($sigPairs as $sigPair) { + //echo 'sig Pair: '; var_dump($sigPair); echo "
"; + $pubkey = $sigPair->getPubKey(); + $signature = $sigPair->getEd25519(); + if(!$createRight) { + while($createTrys < 500) { + if(\Sodium\crypto_sign_verify_detached($signature, $bodyBytes, $pubkey)) { + $createRight = true; + break; + } else { + $createTrys++; + $created->setSeconds($created->getSeconds() - 1); + //$body->setCreated($created); + $bodyBytes = $body->serializeToString(); + } + } + } + + if (!\Sodium\crypto_sign_verify_detached($signature, $bodyBytes, $pubkey)) { + return ['state' => 'error', 'msg' => 'signature for key ' . bin2hex($pubkey) . ' isn\'t valid ']; + } + } + + $protoTransaction->setBodyBytes($bodyBytes); + + + + return $protoTransaction; + } + } \ No newline at end of file diff --git a/community_server/src/Model/Transactions/TransactionBase.php b/community_server/src/Model/Transactions/TransactionBase.php index 80ce8ef5d..31a26f2ee 100644 --- a/community_server/src/Model/Transactions/TransactionBase.php +++ b/community_server/src/Model/Transactions/TransactionBase.php @@ -1,122 +1,122 @@ -errors; - } - - public function addError($functionName, $errorName) { - array_push($this->errors, [$functionName => $errorName]); - } - - public function addErrors($errors) { - $this->errors = array_merge($this->errors, $errors); - } - - public function hasErrors() { - return count($this->errors) > 0; - } - - public static function getTable($tableName) { - if(!isset(self::$tables[$tableName])) { - self::$tables[$tableName] = TableRegistry::getTableLocator()->get($tableName); - } - return self::$tables[$tableName]; - } - - - protected function getStateUserId($publicKey) { - - $stateUsersTable = self::getTable('state_users'); - $stateUser = $stateUsersTable->find('all')->select(['id'])->where(['public_key' => $publicKey])->first(); - if($stateUser) { - return $stateUser->id; - } - // create new entry - $stateUserEntity = $stateUsersTable->newEntity(); - $stateUserEntity->public_key = $publicKey; - if($stateUsersTable->save($stateUserEntity)) { - return $stateUserEntity->id; - } else { - $this->addError('TransactionBase::getStateUserId', 'error saving new state user with error: ' . json_encode($stateUserEntity->getErrors())); - } - - return NULL; - } - - protected function getStateUser($id) { - $stateUsersTable = self::getTable('state_users'); - $stateUser = $stateUsersTable->get($id); - if($stateUser) { - return $stateUser; - } - - return NULL; - } - - - protected function updateStateBalance($stateUserId, $addAmountCent, $recordDate) { - $finalBalance = 0; - $stateBalancesTable = self::getTable('stateBalances'); - $stateBalanceQuery = $stateBalancesTable - ->find('all') - ->select(['amount', 'id']) - ->contain(false) - ->where(['state_user_id' => $stateUserId]);//->first(); - //debug($stateBalanceQuery); - - if($stateBalanceQuery->count() > 0) { - $stateBalanceEntry = $stateBalanceQuery->first(); - $stateBalanceEntry->amount = $stateBalanceEntry->partDecay($recordDate) + $addAmountCent; - $stateBalanceEntry->amount += $addAmountCent; - } else { - $stateBalanceEntry = $stateBalancesTable->newEntity(); - $stateBalanceEntry->state_user_id = $stateUserId; - $stateBalanceEntry->amount = $addAmountCent; - } - $stateBalanceEntry->record_date = $recordDate; - $finalBalance = $stateBalanceEntry->amount; - //echo "\ntry to save: "; var_dump($stateBalanceEntry); echo "\n"; - if(!$stateBalancesTable->save($stateBalanceEntry)) { - $errors = $stateBalanceEntry->getErrors(); - $this->addError('TransactionBase::updateStateBalance', 'error saving state balance with: ' . json_encode($errors)); - return false; - } - return $finalBalance; - } - - protected function addStateUserTransaction($stateUserId, $transactionId, $transactionTypeId, $balance) { - $stateUserTransactionTable = self::getTable('state_user_transactions'); - $stateUserTransactions = $stateUserTransactionTable - ->find('all') - ->where(['state_user_id' => $stateUserId]) - ->order(['transaction_id DESC']); - - if($stateUserTransactions->count() > 0) { - $stateBalanceTable = self::getTable('state_balances'); - $balance_entity = $stateBalanceTable->newEntity(); - $balance_entity->amount = $stateUserTransactions->first()->balance; - $balance_entity->record_date = $stateUserTransactions->first()->balance_date; - $balance = $balance_entity->decay + $balance; - } - $entity = $stateUserTransactionTable->newEntity(); - $entity->state_user_id = $stateUserId; - $entity->transaction_id = $transactionId; - $entity->transaction_type_id = $transactionTypeId; - $entity->balance = $balance; - - if(!$stateUserTransactionTable->save($entity)) { - $errors = $entity->getErrors(); - $this->addError('TransactionBase::addStateUserTransaction', 'error saving state user balance with: ' . json_encode($errors)); - return false; - } - return true; - } +errors; + } + + public function addError($functionName, $errorName) { + array_push($this->errors, [$functionName => $errorName]); + } + + public function addErrors($errors) { + $this->errors = array_merge($this->errors, $errors); + } + + public function hasErrors() { + return count($this->errors) > 0; + } + + public static function getTable($tableName) { + if(!isset(self::$tables[$tableName])) { + self::$tables[$tableName] = TableRegistry::getTableLocator()->get($tableName); + } + return self::$tables[$tableName]; + } + + + protected function getStateUserId($publicKey) { + + $stateUsersTable = self::getTable('state_users'); + $stateUser = $stateUsersTable->find('all')->select(['id'])->where(['public_key' => $publicKey])->first(); + if($stateUser) { + return $stateUser->id; + } + // create new entry + $stateUserEntity = $stateUsersTable->newEntity(); + $stateUserEntity->public_key = $publicKey; + if($stateUsersTable->save($stateUserEntity)) { + return $stateUserEntity->id; + } else { + $this->addError('TransactionBase::getStateUserId', 'error saving new state user with error: ' . json_encode($stateUserEntity->getErrors())); + } + + return NULL; + } + + protected function getStateUser($id) { + $stateUsersTable = self::getTable('state_users'); + $stateUser = $stateUsersTable->get($id); + if($stateUser) { + return $stateUser; + } + + return NULL; + } + + + protected function updateStateBalance($stateUserId, $addAmountCent, $recordDate) { + $finalBalance = 0; + $stateBalancesTable = self::getTable('stateBalances'); + $stateBalanceQuery = $stateBalancesTable + ->find('all') + ->select(['amount', 'id']) + ->contain(false) + ->where(['state_user_id' => $stateUserId]);//->first(); + //debug($stateBalanceQuery); + + if($stateBalanceQuery->count() > 0) { + $stateBalanceEntry = $stateBalanceQuery->first(); + $stateBalanceEntry->amount = $stateBalanceEntry->partDecay($recordDate) + $addAmountCent; + $stateBalanceEntry->amount += $addAmountCent; + } else { + $stateBalanceEntry = $stateBalancesTable->newEntity(); + $stateBalanceEntry->state_user_id = $stateUserId; + $stateBalanceEntry->amount = $addAmountCent; + } + $stateBalanceEntry->record_date = $recordDate; + $finalBalance = $stateBalanceEntry->amount; + //echo "\ntry to save: "; var_dump($stateBalanceEntry); echo "\n"; + if(!$stateBalancesTable->save($stateBalanceEntry)) { + $errors = $stateBalanceEntry->getErrors(); + $this->addError('TransactionBase::updateStateBalance', 'error saving state balance with: ' . json_encode($errors)); + return false; + } + return $finalBalance; + } + + protected function addStateUserTransaction($stateUserId, $transactionId, $transactionTypeId, $balance) { + $stateUserTransactionTable = self::getTable('state_user_transactions'); + $stateUserTransactions = $stateUserTransactionTable + ->find('all') + ->where(['state_user_id' => $stateUserId]) + ->order(['transaction_id DESC']); + + if($stateUserTransactions->count() > 0) { + $stateBalanceTable = self::getTable('state_balances'); + $balance_entity = $stateBalanceTable->newEntity(); + $balance_entity->amount = $stateUserTransactions->first()->balance; + $balance_entity->record_date = $stateUserTransactions->first()->balance_date; + $balance = $balance_entity->decay + $balance; + } + $entity = $stateUserTransactionTable->newEntity(); + $entity->state_user_id = $stateUserId; + $entity->transaction_id = $transactionId; + $entity->transaction_type_id = $transactionTypeId; + $entity->balance = $balance; + + if(!$stateUserTransactionTable->save($entity)) { + $errors = $entity->getErrors(); + $this->addError('TransactionBase::addStateUserTransaction', 'error saving state user balance with: ' . json_encode($errors)); + return false; + } + return true; + } } \ No newline at end of file diff --git a/community_server/src/Template/StateBalances/overview.ctp b/community_server/src/Template/StateBalances/overview.ctp index e55d0aa7b..82208fa84 100644 --- a/community_server/src/Template/StateBalances/overview.ctp +++ b/community_server/src/Template/StateBalances/overview.ctp @@ -1,149 +1,149 @@ -assign('title', __('Kontoübersicht')); - -$header = '

' . __('Aktueller Kontostand: ') . '

' . - '

' . $this->element('printGradido', ['number' => $balance]) . '

'; -if($gdtSum > 0) { - $header .= '

'.$this->Html->link( - $this->element('printGDT', ['number' => $gdtSum]), - ['action' => 'overview_gdt'], - ['escape' => false] - ).'

'; -} -$this->assign('header', $header); -//var_dump($transactions); -?> - - 0) : ?> -
- -
- -
-

Überweisungen

-
-
-
-
-
-
-
-
- 30) { - $memoShort = substr($memoShort, 0, 30) . '...'; - } - $cellColorClass = 'success-color'; - if($send) { - $balance = -$balance; - $cellColorClass = 'alert-color'; - } else if($transaction['type'] == 'creation') { - $cellColorClass = 'orange-color'; - } - ?> -
-
- Html->image('50x50.png', ['class' => 'profile-img', 'alt' => 'profile image']) ?> -
- - - - - - - - -
- - redeem - - - - arrow_back - - - arrow_forward - - -
-
-
-
- 30): ?> - - - - -
-
nice() ?>
-
element('printGradido', ['number' => $balance]) ?>
-
- -
-
- -
-
- - - 0) : ?> - +assign('title', __('Kontoübersicht')); + +$header = '

' . __('Aktueller Kontostand: ') . '

' . + '

' . $this->element('printGradido', ['number' => $balance]) . '

'; +if($gdtSum > 0) { + $header .= '

'.$this->Html->link( + $this->element('printGDT', ['number' => $gdtSum]), + ['action' => 'overview_gdt'], + ['escape' => false] + ).'

'; +} +$this->assign('header', $header); +//var_dump($transactions); +?> + + 0) : ?> +
+ +
+ +
+

Überweisungen

+
+
+
+
+
+
+
+
+ 30) { + $memoShort = substr($memoShort, 0, 30) . '...'; + } + $cellColorClass = 'success-color'; + if($send) { + $balance = -$balance; + $cellColorClass = 'alert-color'; + } else if($transaction['type'] == 'creation') { + $cellColorClass = 'orange-color'; + } + ?> +
+
+ Html->image('50x50.png', ['class' => 'profile-img', 'alt' => 'profile image']) ?> +
+ + + + + + + + +
+ + redeem + + + + arrow_back + + + arrow_forward + + +
+
+
+
+ 30): ?> + + + + +
+
nice() ?>
+
element('printGradido', ['number' => $balance]) ?>
+
+ +
+
+ +
+
+ + + 0) : ?> + \ No newline at end of file diff --git a/community_server/src/Template/StateUsers/list_ident_hashes.ctp b/community_server/src/Template/StateUsers/list_ident_hashes.ctp index 7f50910b1..777a32c99 100644 --- a/community_server/src/Template/StateUsers/list_ident_hashes.ctp +++ b/community_server/src/Template/StateUsers/list_ident_hashes.ctp @@ -1,29 +1,29 @@ - -
- - - - - - - - - - - - - - - - - -
first namelast nameemailidentHashPublic key hex -
first_name ?>last_name ?>email ?>identHash ?>public_key)) ?>
-
+ +
+ + + + + + + + + + + + + + + + + +
first namelast nameemailidentHashPublic key hex +
first_name ?>last_name ?>email ?>identHash ?>public_key)) ?>
+
diff --git a/community_server/src/Template/TransactionCreations/create.ctp b/community_server/src/Template/TransactionCreations/create.ctp index a7e7c4afc..d5c9ee2c5 100644 --- a/community_server/src/Template/TransactionCreations/create.ctp +++ b/community_server/src/Template/TransactionCreations/create.ctp @@ -1,31 +1,31 @@ - $receiver) { - //var_dump($receiver); - array_push($address_options, [ - 'text' => $receiver['name'], - 'value' => $i+1, - 'title' => $receiver['key'] - ]); -} -$this->assign('title', __('Schöpfungstransaktion')); -?> -
- - Form->create($creationForm) ?> -
- Form->control('memo'); ?> - Form->control('amount'); ?> - Form->control('receiver', ['options' => $address_options]); ?> - -
- Form->button(__('Transaktion(en) abschließen'), ['name' => 'next', 'class' => 'grd-form-bn grd-form-bn-succeed grd_clickable grd-width-200']) ?> - Form->button(__('Weitere Transaktion erstellen'), ['name' => 'add', 'class' => 'grd-form-bn grd_clickable grd-width-200']) ?> - Form->end() ?> -
+ $receiver) { + //var_dump($receiver); + array_push($address_options, [ + 'text' => $receiver['name'], + 'value' => $i+1, + 'title' => $receiver['key'] + ]); +} +$this->assign('title', __('Schöpfungstransaktion')); +?> +
+ + Form->create($creationForm) ?> +
+ Form->control('memo'); ?> + Form->control('amount'); ?> + Form->control('receiver', ['options' => $address_options]); ?> + +
+ Form->button(__('Transaktion(en) abschließen'), ['name' => 'next', 'class' => 'grd-form-bn grd-form-bn-succeed grd_clickable grd-width-200']) ?> + Form->button(__('Weitere Transaktion erstellen'), ['name' => 'add', 'class' => 'grd-form-bn grd_clickable grd-width-200']) ?> + Form->end() ?> +
diff --git a/community_server/src/Template/Transactions/synchronize_with_state_user_transactions.ctp b/community_server/src/Template/Transactions/synchronize_with_state_user_transactions.ctp index a4b48cb14..8ac1ecb2d 100644 --- a/community_server/src/Template/Transactions/synchronize_with_state_user_transactions.ctp +++ b/community_server/src/Template/Transactions/synchronize_with_state_user_transactions.ctp @@ -1,46 +1,46 @@ - -
-

Synchronize state_user_transactions with transactions

-

transactions count:

-

state_user_transaction count:

-

Missing count:

-

First 10 Missing ids:

-

$id) { - if($i > 10) break; - if($i > 0) echo ', '; - echo $id['id']; -} ?>

- - -

Synchronize errors:

-
    - $result) : - if(false != $result) { - $succeed++; - continue; - } - ?> -
  • Error saving entity: with error: getErrors()) ?>
  • - - -
  • Succeed:
  • -
- - - Form->create() ?> - Form->button(__('Synchronize')) ?> - Form->end() ?> -
+ +
+

Synchronize state_user_transactions with transactions

+

transactions count:

+

state_user_transaction count:

+

Missing count:

+

First 10 Missing ids:

+

$id) { + if($i > 10) break; + if($i > 0) echo ', '; + echo $id['id']; +} ?>

+ + +

Synchronize errors:

+
    + $result) : + if(false != $result) { + $succeed++; + continue; + } + ?> +
  • Error saving entity: with error: getErrors()) ?>
  • + + +
  • Succeed:
  • +
+ + + Form->create() ?> + Form->button(__('Synchronize')) ?> + Form->end() ?> +
diff --git a/community_server/tests/TestCase/Controller/AppControllerTest.php b/community_server/tests/TestCase/Controller/AppControllerTest.php index 79294cbe0..502ed46da 100644 --- a/community_server/tests/TestCase/Controller/AppControllerTest.php +++ b/community_server/tests/TestCase/Controller/AppControllerTest.php @@ -1,46 +1,46 @@ -session(['StateUser.id' => 1]); - $this->get('/'); - $this->assertSession(1200, 'StateUser.balance'); - //$this->markTestIncomplete('Not implemented yet.'); - } - - -} +session(['StateUser.id' => 1]); + $this->get('/'); + $this->assertSession(1200, 'StateUser.balance'); + //$this->markTestIncomplete('Not implemented yet.'); + } + + +} diff --git a/community_server/webroot/css/materialdesignicons.min.css b/community_server/webroot/css/materialdesignicons.min.css index 07688156b..bbdcbc816 100644 --- a/community_server/webroot/css/materialdesignicons.min.css +++ b/community_server/webroot/css/materialdesignicons.min.css @@ -1,2 +1,2 @@ -/* MaterialDesignIcons.com */@font-face{font-family:"Material Design Icons";src:url("../fonts/materialdesignicons-webfont.eot?v=3.5.95");src:url("../fonts/materialdesignicons-webfont.eot?#iefix&v=3.5.95") format("embedded-opentype"),url("../fonts/materialdesignicons-webfont.woff2?v=3.5.95") format("woff2"),url("../fonts/materialdesignicons-webfont.woff?v=3.5.95") format("woff"),url("../fonts/materialdesignicons-webfont.ttf?v=3.5.95") format("truetype"),url("../fonts/materialdesignicons-webfont.svg?v=3.5.95#materialdesigniconsregular") format("svg");font-weight:normal;font-style:normal}.mdi:before,.mdi-set{display:inline-block;font:normal normal normal 24px/1 "Material Design Icons";font-size:inherit;text-rendering:auto;line-height:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.mdi-access-point:before{content:"\F002"}.mdi-access-point-network:before{content:"\F003"}.mdi-access-point-network-off:before{content:"\FBBD"}.mdi-account:before{content:"\F004"}.mdi-account-alert:before{content:"\F005"}.mdi-account-alert-outline:before{content:"\FB2C"}.mdi-account-arrow-left:before{content:"\FB2D"}.mdi-account-arrow-left-outline:before{content:"\FB2E"}.mdi-account-arrow-right:before{content:"\FB2F"}.mdi-account-arrow-right-outline:before{content:"\FB30"}.mdi-account-badge:before{content:"\FD83"}.mdi-account-badge-alert:before{content:"\FD84"}.mdi-account-badge-alert-outline:before{content:"\FD85"}.mdi-account-badge-outline:before{content:"\FD86"}.mdi-account-box:before{content:"\F006"}.mdi-account-box-multiple:before{content:"\F933"}.mdi-account-box-outline:before{content:"\F007"}.mdi-account-card-details:before{content:"\F5D2"}.mdi-account-card-details-outline:before{content:"\FD87"}.mdi-account-check:before{content:"\F008"}.mdi-account-check-outline:before{content:"\FBBE"}.mdi-account-child:before{content:"\FA88"}.mdi-account-child-circle:before{content:"\FA89"}.mdi-account-circle:before{content:"\F009"}.mdi-account-circle-outline:before{content:"\FB31"}.mdi-account-clock:before{content:"\FB32"}.mdi-account-clock-outline:before{content:"\FB33"}.mdi-account-convert:before{content:"\F00A"}.mdi-account-details:before{content:"\F631"}.mdi-account-edit:before{content:"\F6BB"}.mdi-account-group:before{content:"\F848"}.mdi-account-group-outline:before{content:"\FB34"}.mdi-account-heart:before{content:"\F898"}.mdi-account-heart-outline:before{content:"\FBBF"}.mdi-account-key:before{content:"\F00B"}.mdi-account-key-outline:before{content:"\FBC0"}.mdi-account-minus:before{content:"\F00D"}.mdi-account-minus-outline:before{content:"\FAEB"}.mdi-account-multiple:before{content:"\F00E"}.mdi-account-multiple-check:before{content:"\F8C4"}.mdi-account-multiple-minus:before{content:"\F5D3"}.mdi-account-multiple-minus-outline:before{content:"\FBC1"}.mdi-account-multiple-outline:before{content:"\F00F"}.mdi-account-multiple-plus:before{content:"\F010"}.mdi-account-multiple-plus-outline:before{content:"\F7FF"}.mdi-account-network:before{content:"\F011"}.mdi-account-network-outline:before{content:"\FBC2"}.mdi-account-off:before{content:"\F012"}.mdi-account-off-outline:before{content:"\FBC3"}.mdi-account-outline:before{content:"\F013"}.mdi-account-plus:before{content:"\F014"}.mdi-account-plus-outline:before{content:"\F800"}.mdi-account-question:before{content:"\FB35"}.mdi-account-question-outline:before{content:"\FB36"}.mdi-account-remove:before{content:"\F015"}.mdi-account-remove-outline:before{content:"\FAEC"}.mdi-account-search:before{content:"\F016"}.mdi-account-search-outline:before{content:"\F934"}.mdi-account-settings:before{content:"\F630"}.mdi-account-star:before{content:"\F017"}.mdi-account-star-outline:before{content:"\FBC4"}.mdi-account-supervisor:before{content:"\FA8A"}.mdi-account-supervisor-circle:before{content:"\FA8B"}.mdi-account-switch:before{content:"\F019"}.mdi-account-tie:before{content:"\FCBF"}.mdi-accusoft:before{content:"\F849"}.mdi-adchoices:before{content:"\FD1E"}.mdi-adjust:before{content:"\F01A"}.mdi-adobe:before{content:"\F935"}.mdi-air-conditioner:before{content:"\F01B"}.mdi-air-filter:before{content:"\FD1F"}.mdi-air-horn:before{content:"\FD88"}.mdi-air-purifier:before{content:"\FD20"}.mdi-airbag:before{content:"\FBC5"}.mdi-airballoon:before{content:"\F01C"}.mdi-airplane:before{content:"\F01D"}.mdi-airplane-landing:before{content:"\F5D4"}.mdi-airplane-off:before{content:"\F01E"}.mdi-airplane-takeoff:before{content:"\F5D5"}.mdi-airplay:before{content:"\F01F"}.mdi-airport:before{content:"\F84A"}.mdi-alarm:before{content:"\F020"}.mdi-alarm-bell:before{content:"\F78D"}.mdi-alarm-check:before{content:"\F021"}.mdi-alarm-light:before{content:"\F78E"}.mdi-alarm-light-outline:before{content:"\FBC6"}.mdi-alarm-multiple:before{content:"\F022"}.mdi-alarm-off:before{content:"\F023"}.mdi-alarm-plus:before{content:"\F024"}.mdi-alarm-snooze:before{content:"\F68D"}.mdi-album:before{content:"\F025"}.mdi-alert:before{content:"\F026"}.mdi-alert-box:before{content:"\F027"}.mdi-alert-box-outline:before{content:"\FCC0"}.mdi-alert-circle:before{content:"\F028"}.mdi-alert-circle-outline:before{content:"\F5D6"}.mdi-alert-decagram:before{content:"\F6BC"}.mdi-alert-decagram-outline:before{content:"\FCC1"}.mdi-alert-octagon:before{content:"\F029"}.mdi-alert-octagon-outline:before{content:"\FCC2"}.mdi-alert-octagram:before{content:"\F766"}.mdi-alert-octagram-outline:before{content:"\FCC3"}.mdi-alert-outline:before{content:"\F02A"}.mdi-alien:before{content:"\F899"}.mdi-all-inclusive:before{content:"\F6BD"}.mdi-alpha:before{content:"\F02B"}.mdi-alpha-a:before{content:"\41"}.mdi-alpha-a-box:before{content:"\FAED"}.mdi-alpha-a-box-outline:before{content:"\FBC7"}.mdi-alpha-a-circle:before{content:"\FBC8"}.mdi-alpha-a-circle-outline:before{content:"\FBC9"}.mdi-alpha-b:before{content:"\42"}.mdi-alpha-b-box:before{content:"\FAEE"}.mdi-alpha-b-box-outline:before{content:"\FBCA"}.mdi-alpha-b-circle:before{content:"\FBCB"}.mdi-alpha-b-circle-outline:before{content:"\FBCC"}.mdi-alpha-c:before{content:"\43"}.mdi-alpha-c-box:before{content:"\FAEF"}.mdi-alpha-c-box-outline:before{content:"\FBCD"}.mdi-alpha-c-circle:before{content:"\FBCE"}.mdi-alpha-c-circle-outline:before{content:"\FBCF"}.mdi-alpha-d:before{content:"\44"}.mdi-alpha-d-box:before{content:"\FAF0"}.mdi-alpha-d-box-outline:before{content:"\FBD0"}.mdi-alpha-d-circle:before{content:"\FBD1"}.mdi-alpha-d-circle-outline:before{content:"\FBD2"}.mdi-alpha-e:before{content:"\45"}.mdi-alpha-e-box:before{content:"\FAF1"}.mdi-alpha-e-box-outline:before{content:"\FBD3"}.mdi-alpha-e-circle:before{content:"\FBD4"}.mdi-alpha-e-circle-outline:before{content:"\FBD5"}.mdi-alpha-f:before{content:"\46"}.mdi-alpha-f-box:before{content:"\FAF2"}.mdi-alpha-f-box-outline:before{content:"\FBD6"}.mdi-alpha-f-circle:before{content:"\FBD7"}.mdi-alpha-f-circle-outline:before{content:"\FBD8"}.mdi-alpha-g:before{content:"\47"}.mdi-alpha-g-box:before{content:"\FAF3"}.mdi-alpha-g-box-outline:before{content:"\FBD9"}.mdi-alpha-g-circle:before{content:"\FBDA"}.mdi-alpha-g-circle-outline:before{content:"\FBDB"}.mdi-alpha-h:before{content:"\48"}.mdi-alpha-h-box:before{content:"\FAF4"}.mdi-alpha-h-box-outline:before{content:"\FBDC"}.mdi-alpha-h-circle:before{content:"\FBDD"}.mdi-alpha-h-circle-outline:before{content:"\FBDE"}.mdi-alpha-i:before{content:"\49"}.mdi-alpha-i-box:before{content:"\FAF5"}.mdi-alpha-i-box-outline:before{content:"\FBDF"}.mdi-alpha-i-circle:before{content:"\FBE0"}.mdi-alpha-i-circle-outline:before{content:"\FBE1"}.mdi-alpha-j:before{content:"\4A"}.mdi-alpha-j-box:before{content:"\FAF6"}.mdi-alpha-j-box-outline:before{content:"\FBE2"}.mdi-alpha-j-circle:before{content:"\FBE3"}.mdi-alpha-j-circle-outline:before{content:"\FBE4"}.mdi-alpha-k:before{content:"\4B"}.mdi-alpha-k-box:before{content:"\FAF7"}.mdi-alpha-k-box-outline:before{content:"\FBE5"}.mdi-alpha-k-circle:before{content:"\FBE6"}.mdi-alpha-k-circle-outline:before{content:"\FBE7"}.mdi-alpha-l:before{content:"\4C"}.mdi-alpha-l-box:before{content:"\FAF8"}.mdi-alpha-l-box-outline:before{content:"\FBE8"}.mdi-alpha-l-circle:before{content:"\FBE9"}.mdi-alpha-l-circle-outline:before{content:"\FBEA"}.mdi-alpha-m:before{content:"\4D"}.mdi-alpha-m-box:before{content:"\FAF9"}.mdi-alpha-m-box-outline:before{content:"\FBEB"}.mdi-alpha-m-circle:before{content:"\FBEC"}.mdi-alpha-m-circle-outline:before{content:"\FBED"}.mdi-alpha-n:before{content:"\4E"}.mdi-alpha-n-box:before{content:"\FAFA"}.mdi-alpha-n-box-outline:before{content:"\FBEE"}.mdi-alpha-n-circle:before{content:"\FBEF"}.mdi-alpha-n-circle-outline:before{content:"\FBF0"}.mdi-alpha-o:before{content:"\4F"}.mdi-alpha-o-box:before{content:"\FAFB"}.mdi-alpha-o-box-outline:before{content:"\FBF1"}.mdi-alpha-o-circle:before{content:"\FBF2"}.mdi-alpha-o-circle-outline:before{content:"\FBF3"}.mdi-alpha-p:before{content:"\50"}.mdi-alpha-p-box:before{content:"\FAFC"}.mdi-alpha-p-box-outline:before{content:"\FBF4"}.mdi-alpha-p-circle:before{content:"\FBF5"}.mdi-alpha-p-circle-outline:before{content:"\FBF6"}.mdi-alpha-q:before{content:"\51"}.mdi-alpha-q-box:before{content:"\FAFD"}.mdi-alpha-q-box-outline:before{content:"\FBF7"}.mdi-alpha-q-circle:before{content:"\FBF8"}.mdi-alpha-q-circle-outline:before{content:"\FBF9"}.mdi-alpha-r:before{content:"\52"}.mdi-alpha-r-box:before{content:"\FAFE"}.mdi-alpha-r-box-outline:before{content:"\FBFA"}.mdi-alpha-r-circle:before{content:"\FBFB"}.mdi-alpha-r-circle-outline:before{content:"\FBFC"}.mdi-alpha-s:before{content:"\53"}.mdi-alpha-s-box:before{content:"\FAFF"}.mdi-alpha-s-box-outline:before{content:"\FBFD"}.mdi-alpha-s-circle:before{content:"\FBFE"}.mdi-alpha-s-circle-outline:before{content:"\FBFF"}.mdi-alpha-t:before{content:"\54"}.mdi-alpha-t-box:before{content:"\FB00"}.mdi-alpha-t-box-outline:before{content:"\FC00"}.mdi-alpha-t-circle:before{content:"\FC01"}.mdi-alpha-t-circle-outline:before{content:"\FC02"}.mdi-alpha-u:before{content:"\55"}.mdi-alpha-u-box:before{content:"\FB01"}.mdi-alpha-u-box-outline:before{content:"\FC03"}.mdi-alpha-u-circle:before{content:"\FC04"}.mdi-alpha-u-circle-outline:before{content:"\FC05"}.mdi-alpha-v:before{content:"\56"}.mdi-alpha-v-box:before{content:"\FB02"}.mdi-alpha-v-box-outline:before{content:"\FC06"}.mdi-alpha-v-circle:before{content:"\FC07"}.mdi-alpha-v-circle-outline:before{content:"\FC08"}.mdi-alpha-w:before{content:"\57"}.mdi-alpha-w-box:before{content:"\FB03"}.mdi-alpha-w-box-outline:before{content:"\FC09"}.mdi-alpha-w-circle:before{content:"\FC0A"}.mdi-alpha-w-circle-outline:before{content:"\FC0B"}.mdi-alpha-x:before{content:"\58"}.mdi-alpha-x-box:before{content:"\FB04"}.mdi-alpha-x-box-outline:before{content:"\FC0C"}.mdi-alpha-x-circle:before{content:"\FC0D"}.mdi-alpha-x-circle-outline:before{content:"\FC0E"}.mdi-alpha-y:before{content:"\59"}.mdi-alpha-y-box:before{content:"\FB05"}.mdi-alpha-y-box-outline:before{content:"\FC0F"}.mdi-alpha-y-circle:before{content:"\FC10"}.mdi-alpha-y-circle-outline:before{content:"\FC11"}.mdi-alpha-z:before{content:"\5A"}.mdi-alpha-z-box:before{content:"\FB06"}.mdi-alpha-z-box-outline:before{content:"\FC12"}.mdi-alpha-z-circle:before{content:"\FC13"}.mdi-alpha-z-circle-outline:before{content:"\FC14"}.mdi-alphabetical:before{content:"\F02C"}.mdi-altimeter:before{content:"\F5D7"}.mdi-amazon:before{content:"\F02D"}.mdi-amazon-alexa:before{content:"\F8C5"}.mdi-amazon-drive:before{content:"\F02E"}.mdi-ambulance:before{content:"\F02F"}.mdi-ammunition:before{content:"\FCC4"}.mdi-ampersand:before{content:"\FA8C"}.mdi-amplifier:before{content:"\F030"}.mdi-anchor:before{content:"\F031"}.mdi-android:before{content:"\F032"}.mdi-android-auto:before{content:"\FA8D"}.mdi-android-debug-bridge:before{content:"\F033"}.mdi-android-head:before{content:"\F78F"}.mdi-android-messages:before{content:"\FD21"}.mdi-android-studio:before{content:"\F034"}.mdi-angle-acute:before{content:"\F936"}.mdi-angle-obtuse:before{content:"\F937"}.mdi-angle-right:before{content:"\F938"}.mdi-angular:before{content:"\F6B1"}.mdi-angularjs:before{content:"\F6BE"}.mdi-animation:before{content:"\F5D8"}.mdi-animation-outline:before{content:"\FA8E"}.mdi-animation-play:before{content:"\F939"}.mdi-animation-play-outline:before{content:"\FA8F"}.mdi-anvil:before{content:"\F89A"}.mdi-apple:before{content:"\F035"}.mdi-apple-finder:before{content:"\F036"}.mdi-apple-icloud:before{content:"\F038"}.mdi-apple-ios:before{content:"\F037"}.mdi-apple-keyboard-caps:before{content:"\F632"}.mdi-apple-keyboard-command:before{content:"\F633"}.mdi-apple-keyboard-control:before{content:"\F634"}.mdi-apple-keyboard-option:before{content:"\F635"}.mdi-apple-keyboard-shift:before{content:"\F636"}.mdi-apple-safari:before{content:"\F039"}.mdi-application:before{content:"\F614"}.mdi-application-export:before{content:"\FD89"}.mdi-application-import:before{content:"\FD8A"}.mdi-apps:before{content:"\F03B"}.mdi-apps-box:before{content:"\FD22"}.mdi-arch:before{content:"\F8C6"}.mdi-archive:before{content:"\F03C"}.mdi-arrange-bring-forward:before{content:"\F03D"}.mdi-arrange-bring-to-front:before{content:"\F03E"}.mdi-arrange-send-backward:before{content:"\F03F"}.mdi-arrange-send-to-back:before{content:"\F040"}.mdi-arrow-all:before{content:"\F041"}.mdi-arrow-bottom-left:before{content:"\F042"}.mdi-arrow-bottom-left-bold-outline:before{content:"\F9B6"}.mdi-arrow-bottom-left-thick:before{content:"\F9B7"}.mdi-arrow-bottom-right:before{content:"\F043"}.mdi-arrow-bottom-right-bold-outline:before{content:"\F9B8"}.mdi-arrow-bottom-right-thick:before{content:"\F9B9"}.mdi-arrow-collapse:before{content:"\F615"}.mdi-arrow-collapse-all:before{content:"\F044"}.mdi-arrow-collapse-down:before{content:"\F791"}.mdi-arrow-collapse-horizontal:before{content:"\F84B"}.mdi-arrow-collapse-left:before{content:"\F792"}.mdi-arrow-collapse-right:before{content:"\F793"}.mdi-arrow-collapse-up:before{content:"\F794"}.mdi-arrow-collapse-vertical:before{content:"\F84C"}.mdi-arrow-decision:before{content:"\F9BA"}.mdi-arrow-decision-auto:before{content:"\F9BB"}.mdi-arrow-decision-auto-outline:before{content:"\F9BC"}.mdi-arrow-decision-outline:before{content:"\F9BD"}.mdi-arrow-down:before{content:"\F045"}.mdi-arrow-down-bold:before{content:"\F72D"}.mdi-arrow-down-bold-box:before{content:"\F72E"}.mdi-arrow-down-bold-box-outline:before{content:"\F72F"}.mdi-arrow-down-bold-circle:before{content:"\F047"}.mdi-arrow-down-bold-circle-outline:before{content:"\F048"}.mdi-arrow-down-bold-hexagon-outline:before{content:"\F049"}.mdi-arrow-down-bold-outline:before{content:"\F9BE"}.mdi-arrow-down-box:before{content:"\F6BF"}.mdi-arrow-down-circle:before{content:"\FCB7"}.mdi-arrow-down-circle-outline:before{content:"\FCB8"}.mdi-arrow-down-drop-circle:before{content:"\F04A"}.mdi-arrow-down-drop-circle-outline:before{content:"\F04B"}.mdi-arrow-down-thick:before{content:"\F046"}.mdi-arrow-expand:before{content:"\F616"}.mdi-arrow-expand-all:before{content:"\F04C"}.mdi-arrow-expand-down:before{content:"\F795"}.mdi-arrow-expand-horizontal:before{content:"\F84D"}.mdi-arrow-expand-left:before{content:"\F796"}.mdi-arrow-expand-right:before{content:"\F797"}.mdi-arrow-expand-up:before{content:"\F798"}.mdi-arrow-expand-vertical:before{content:"\F84E"}.mdi-arrow-left:before{content:"\F04D"}.mdi-arrow-left-bold:before{content:"\F730"}.mdi-arrow-left-bold-box:before{content:"\F731"}.mdi-arrow-left-bold-box-outline:before{content:"\F732"}.mdi-arrow-left-bold-circle:before{content:"\F04F"}.mdi-arrow-left-bold-circle-outline:before{content:"\F050"}.mdi-arrow-left-bold-hexagon-outline:before{content:"\F051"}.mdi-arrow-left-bold-outline:before{content:"\F9BF"}.mdi-arrow-left-box:before{content:"\F6C0"}.mdi-arrow-left-circle:before{content:"\FCB9"}.mdi-arrow-left-circle-outline:before{content:"\FCBA"}.mdi-arrow-left-drop-circle:before{content:"\F052"}.mdi-arrow-left-drop-circle-outline:before{content:"\F053"}.mdi-arrow-left-right-bold-outline:before{content:"\F9C0"}.mdi-arrow-left-thick:before{content:"\F04E"}.mdi-arrow-right:before{content:"\F054"}.mdi-arrow-right-bold:before{content:"\F733"}.mdi-arrow-right-bold-box:before{content:"\F734"}.mdi-arrow-right-bold-box-outline:before{content:"\F735"}.mdi-arrow-right-bold-circle:before{content:"\F056"}.mdi-arrow-right-bold-circle-outline:before{content:"\F057"}.mdi-arrow-right-bold-hexagon-outline:before{content:"\F058"}.mdi-arrow-right-bold-outline:before{content:"\F9C1"}.mdi-arrow-right-box:before{content:"\F6C1"}.mdi-arrow-right-circle:before{content:"\FCBB"}.mdi-arrow-right-circle-outline:before{content:"\FCBC"}.mdi-arrow-right-drop-circle:before{content:"\F059"}.mdi-arrow-right-drop-circle-outline:before{content:"\F05A"}.mdi-arrow-right-thick:before{content:"\F055"}.mdi-arrow-split-horizontal:before{content:"\F93A"}.mdi-arrow-split-vertical:before{content:"\F93B"}.mdi-arrow-top-left:before{content:"\F05B"}.mdi-arrow-top-left-bold-outline:before{content:"\F9C2"}.mdi-arrow-top-left-thick:before{content:"\F9C3"}.mdi-arrow-top-right:before{content:"\F05C"}.mdi-arrow-top-right-bold-outline:before{content:"\F9C4"}.mdi-arrow-top-right-thick:before{content:"\F9C5"}.mdi-arrow-up:before{content:"\F05D"}.mdi-arrow-up-bold:before{content:"\F736"}.mdi-arrow-up-bold-box:before{content:"\F737"}.mdi-arrow-up-bold-box-outline:before{content:"\F738"}.mdi-arrow-up-bold-circle:before{content:"\F05F"}.mdi-arrow-up-bold-circle-outline:before{content:"\F060"}.mdi-arrow-up-bold-hexagon-outline:before{content:"\F061"}.mdi-arrow-up-bold-outline:before{content:"\F9C6"}.mdi-arrow-up-box:before{content:"\F6C2"}.mdi-arrow-up-circle:before{content:"\FCBD"}.mdi-arrow-up-circle-outline:before{content:"\FCBE"}.mdi-arrow-up-down-bold-outline:before{content:"\F9C7"}.mdi-arrow-up-drop-circle:before{content:"\F062"}.mdi-arrow-up-drop-circle-outline:before{content:"\F063"}.mdi-arrow-up-thick:before{content:"\F05E"}.mdi-artist:before{content:"\F802"}.mdi-artist-outline:before{content:"\FCC5"}.mdi-artstation:before{content:"\FB37"}.mdi-aspect-ratio:before{content:"\FA23"}.mdi-assistant:before{content:"\F064"}.mdi-asterisk:before{content:"\F6C3"}.mdi-at:before{content:"\F065"}.mdi-atlassian:before{content:"\F803"}.mdi-atm:before{content:"\FD23"}.mdi-atom:before{content:"\F767"}.mdi-attachment:before{content:"\F066"}.mdi-audio-video:before{content:"\F93C"}.mdi-audiobook:before{content:"\F067"}.mdi-augmented-reality:before{content:"\F84F"}.mdi-auto-fix:before{content:"\F068"}.mdi-auto-upload:before{content:"\F069"}.mdi-autorenew:before{content:"\F06A"}.mdi-av-timer:before{content:"\F06B"}.mdi-axe:before{content:"\F8C7"}.mdi-axis:before{content:"\FD24"}.mdi-axis-arrow:before{content:"\FD25"}.mdi-axis-arrow-lock:before{content:"\FD26"}.mdi-axis-lock:before{content:"\FD27"}.mdi-axis-x-arrow:before{content:"\FD28"}.mdi-axis-x-arrow-lock:before{content:"\FD29"}.mdi-axis-x-rotate-clockwise:before{content:"\FD2A"}.mdi-axis-x-rotate-counterclockwise:before{content:"\FD2B"}.mdi-axis-x-y-arrow-lock:before{content:"\FD2C"}.mdi-axis-y-arrow:before{content:"\FD2D"}.mdi-axis-y-arrow-lock:before{content:"\FD2E"}.mdi-axis-y-rotate-clockwise:before{content:"\FD2F"}.mdi-axis-y-rotate-counterclockwise:before{content:"\FD30"}.mdi-axis-z-arrow:before{content:"\FD31"}.mdi-axis-z-arrow-lock:before{content:"\FD32"}.mdi-axis-z-rotate-clockwise:before{content:"\FD33"}.mdi-axis-z-rotate-counterclockwise:before{content:"\FD34"}.mdi-azure:before{content:"\F804"}.mdi-babel:before{content:"\FA24"}.mdi-baby:before{content:"\F06C"}.mdi-baby-buggy:before{content:"\F68E"}.mdi-backburger:before{content:"\F06D"}.mdi-backspace:before{content:"\F06E"}.mdi-backspace-outline:before{content:"\FB38"}.mdi-backup-restore:before{content:"\F06F"}.mdi-badminton:before{content:"\F850"}.mdi-balloon:before{content:"\FA25"}.mdi-ballot:before{content:"\F9C8"}.mdi-ballot-outline:before{content:"\F9C9"}.mdi-ballot-recount:before{content:"\FC15"}.mdi-ballot-recount-outline:before{content:"\FC16"}.mdi-bandage:before{content:"\FD8B"}.mdi-bandcamp:before{content:"\F674"}.mdi-bank:before{content:"\F070"}.mdi-bank-minus:before{content:"\FD8C"}.mdi-bank-plus:before{content:"\FD8D"}.mdi-bank-remove:before{content:"\FD8E"}.mdi-bank-transfer:before{content:"\FA26"}.mdi-bank-transfer-in:before{content:"\FA27"}.mdi-bank-transfer-out:before{content:"\FA28"}.mdi-barcode:before{content:"\F071"}.mdi-barcode-scan:before{content:"\F072"}.mdi-barley:before{content:"\F073"}.mdi-barley-off:before{content:"\FB39"}.mdi-barn:before{content:"\FB3A"}.mdi-barrel:before{content:"\F074"}.mdi-baseball:before{content:"\F851"}.mdi-baseball-bat:before{content:"\F852"}.mdi-basecamp:before{content:"\F075"}.mdi-basket:before{content:"\F076"}.mdi-basket-fill:before{content:"\F077"}.mdi-basket-unfill:before{content:"\F078"}.mdi-basketball:before{content:"\F805"}.mdi-basketball-hoop:before{content:"\FC17"}.mdi-basketball-hoop-outline:before{content:"\FC18"}.mdi-bat:before{content:"\FB3B"}.mdi-battery:before{content:"\F079"}.mdi-battery-10:before{content:"\F07A"}.mdi-battery-10-bluetooth:before{content:"\F93D"}.mdi-battery-20:before{content:"\F07B"}.mdi-battery-20-bluetooth:before{content:"\F93E"}.mdi-battery-30:before{content:"\F07C"}.mdi-battery-30-bluetooth:before{content:"\F93F"}.mdi-battery-40:before{content:"\F07D"}.mdi-battery-40-bluetooth:before{content:"\F940"}.mdi-battery-50:before{content:"\F07E"}.mdi-battery-50-bluetooth:before{content:"\F941"}.mdi-battery-60:before{content:"\F07F"}.mdi-battery-60-bluetooth:before{content:"\F942"}.mdi-battery-70:before{content:"\F080"}.mdi-battery-70-bluetooth:before{content:"\F943"}.mdi-battery-80:before{content:"\F081"}.mdi-battery-80-bluetooth:before{content:"\F944"}.mdi-battery-90:before{content:"\F082"}.mdi-battery-90-bluetooth:before{content:"\F945"}.mdi-battery-alert:before{content:"\F083"}.mdi-battery-alert-bluetooth:before{content:"\F946"}.mdi-battery-bluetooth:before{content:"\F947"}.mdi-battery-bluetooth-variant:before{content:"\F948"}.mdi-battery-charging:before{content:"\F084"}.mdi-battery-charging-10:before{content:"\F89B"}.mdi-battery-charging-100:before{content:"\F085"}.mdi-battery-charging-20:before{content:"\F086"}.mdi-battery-charging-30:before{content:"\F087"}.mdi-battery-charging-40:before{content:"\F088"}.mdi-battery-charging-50:before{content:"\F89C"}.mdi-battery-charging-60:before{content:"\F089"}.mdi-battery-charging-70:before{content:"\F89D"}.mdi-battery-charging-80:before{content:"\F08A"}.mdi-battery-charging-90:before{content:"\F08B"}.mdi-battery-charging-outline:before{content:"\F89E"}.mdi-battery-charging-wireless:before{content:"\F806"}.mdi-battery-charging-wireless-10:before{content:"\F807"}.mdi-battery-charging-wireless-20:before{content:"\F808"}.mdi-battery-charging-wireless-30:before{content:"\F809"}.mdi-battery-charging-wireless-40:before{content:"\F80A"}.mdi-battery-charging-wireless-50:before{content:"\F80B"}.mdi-battery-charging-wireless-60:before{content:"\F80C"}.mdi-battery-charging-wireless-70:before{content:"\F80D"}.mdi-battery-charging-wireless-80:before{content:"\F80E"}.mdi-battery-charging-wireless-90:before{content:"\F80F"}.mdi-battery-charging-wireless-alert:before{content:"\F810"}.mdi-battery-charging-wireless-outline:before{content:"\F811"}.mdi-battery-minus:before{content:"\F08C"}.mdi-battery-negative:before{content:"\F08D"}.mdi-battery-outline:before{content:"\F08E"}.mdi-battery-plus:before{content:"\F08F"}.mdi-battery-positive:before{content:"\F090"}.mdi-battery-unknown:before{content:"\F091"}.mdi-battery-unknown-bluetooth:before{content:"\F949"}.mdi-battlenet:before{content:"\FB3C"}.mdi-beach:before{content:"\F092"}.mdi-beaker:before{content:"\FCC6"}.mdi-beaker-outline:before{content:"\F68F"}.mdi-beats:before{content:"\F097"}.mdi-bed-empty:before{content:"\F89F"}.mdi-beer:before{content:"\F098"}.mdi-behance:before{content:"\F099"}.mdi-bell:before{content:"\F09A"}.mdi-bell-alert:before{content:"\FD35"}.mdi-bell-circle:before{content:"\FD36"}.mdi-bell-circle-outline:before{content:"\FD37"}.mdi-bell-off:before{content:"\F09B"}.mdi-bell-off-outline:before{content:"\FA90"}.mdi-bell-outline:before{content:"\F09C"}.mdi-bell-plus:before{content:"\F09D"}.mdi-bell-plus-outline:before{content:"\FA91"}.mdi-bell-ring:before{content:"\F09E"}.mdi-bell-ring-outline:before{content:"\F09F"}.mdi-bell-sleep:before{content:"\F0A0"}.mdi-bell-sleep-outline:before{content:"\FA92"}.mdi-beta:before{content:"\F0A1"}.mdi-betamax:before{content:"\F9CA"}.mdi-bible:before{content:"\F0A2"}.mdi-bike:before{content:"\F0A3"}.mdi-billiards:before{content:"\FB3D"}.mdi-billiards-rack:before{content:"\FB3E"}.mdi-bing:before{content:"\F0A4"}.mdi-binoculars:before{content:"\F0A5"}.mdi-bio:before{content:"\F0A6"}.mdi-biohazard:before{content:"\F0A7"}.mdi-bitbucket:before{content:"\F0A8"}.mdi-bitcoin:before{content:"\F812"}.mdi-black-mesa:before{content:"\F0A9"}.mdi-blackberry:before{content:"\F0AA"}.mdi-blender:before{content:"\FCC7"}.mdi-blender-software:before{content:"\F0AB"}.mdi-blinds:before{content:"\F0AC"}.mdi-block-helper:before{content:"\F0AD"}.mdi-blogger:before{content:"\F0AE"}.mdi-blood-bag:before{content:"\FCC8"}.mdi-bluetooth:before{content:"\F0AF"}.mdi-bluetooth-audio:before{content:"\F0B0"}.mdi-bluetooth-connect:before{content:"\F0B1"}.mdi-bluetooth-off:before{content:"\F0B2"}.mdi-bluetooth-settings:before{content:"\F0B3"}.mdi-bluetooth-transfer:before{content:"\F0B4"}.mdi-blur:before{content:"\F0B5"}.mdi-blur-linear:before{content:"\F0B6"}.mdi-blur-off:before{content:"\F0B7"}.mdi-blur-radial:before{content:"\F0B8"}.mdi-bolnisi-cross:before{content:"\FCC9"}.mdi-bolt:before{content:"\FD8F"}.mdi-bomb:before{content:"\F690"}.mdi-bomb-off:before{content:"\F6C4"}.mdi-bone:before{content:"\F0B9"}.mdi-book:before{content:"\F0BA"}.mdi-book-lock:before{content:"\F799"}.mdi-book-lock-open:before{content:"\F79A"}.mdi-book-minus:before{content:"\F5D9"}.mdi-book-multiple:before{content:"\F0BB"}.mdi-book-multiple-minus:before{content:"\FA93"}.mdi-book-multiple-plus:before{content:"\FA94"}.mdi-book-multiple-remove:before{content:"\FA95"}.mdi-book-multiple-variant:before{content:"\F0BC"}.mdi-book-open:before{content:"\F0BD"}.mdi-book-open-outline:before{content:"\FB3F"}.mdi-book-open-page-variant:before{content:"\F5DA"}.mdi-book-open-variant:before{content:"\F0BE"}.mdi-book-outline:before{content:"\FB40"}.mdi-book-plus:before{content:"\F5DB"}.mdi-book-remove:before{content:"\FA96"}.mdi-book-variant:before{content:"\F0BF"}.mdi-bookmark:before{content:"\F0C0"}.mdi-bookmark-check:before{content:"\F0C1"}.mdi-bookmark-minus:before{content:"\F9CB"}.mdi-bookmark-minus-outline:before{content:"\F9CC"}.mdi-bookmark-music:before{content:"\F0C2"}.mdi-bookmark-off:before{content:"\F9CD"}.mdi-bookmark-off-outline:before{content:"\F9CE"}.mdi-bookmark-outline:before{content:"\F0C3"}.mdi-bookmark-plus:before{content:"\F0C5"}.mdi-bookmark-plus-outline:before{content:"\F0C4"}.mdi-bookmark-remove:before{content:"\F0C6"}.mdi-boombox:before{content:"\F5DC"}.mdi-bootstrap:before{content:"\F6C5"}.mdi-border-all:before{content:"\F0C7"}.mdi-border-all-variant:before{content:"\F8A0"}.mdi-border-bottom:before{content:"\F0C8"}.mdi-border-bottom-variant:before{content:"\F8A1"}.mdi-border-color:before{content:"\F0C9"}.mdi-border-horizontal:before{content:"\F0CA"}.mdi-border-inside:before{content:"\F0CB"}.mdi-border-left:before{content:"\F0CC"}.mdi-border-left-variant:before{content:"\F8A2"}.mdi-border-none:before{content:"\F0CD"}.mdi-border-none-variant:before{content:"\F8A3"}.mdi-border-outside:before{content:"\F0CE"}.mdi-border-right:before{content:"\F0CF"}.mdi-border-right-variant:before{content:"\F8A4"}.mdi-border-style:before{content:"\F0D0"}.mdi-border-top:before{content:"\F0D1"}.mdi-border-top-variant:before{content:"\F8A5"}.mdi-border-vertical:before{content:"\F0D2"}.mdi-bottle-wine:before{content:"\F853"}.mdi-bow-tie:before{content:"\F677"}.mdi-bowl:before{content:"\F617"}.mdi-bowling:before{content:"\F0D3"}.mdi-box:before{content:"\F0D4"}.mdi-box-cutter:before{content:"\F0D5"}.mdi-box-shadow:before{content:"\F637"}.mdi-boxing-glove:before{content:"\FB41"}.mdi-braille:before{content:"\F9CF"}.mdi-brain:before{content:"\F9D0"}.mdi-bread-slice:before{content:"\FCCA"}.mdi-bread-slice-outline:before{content:"\FCCB"}.mdi-bridge:before{content:"\F618"}.mdi-briefcase:before{content:"\F0D6"}.mdi-briefcase-account:before{content:"\FCCC"}.mdi-briefcase-account-outline:before{content:"\FCCD"}.mdi-briefcase-check:before{content:"\F0D7"}.mdi-briefcase-download:before{content:"\F0D8"}.mdi-briefcase-download-outline:before{content:"\FC19"}.mdi-briefcase-edit:before{content:"\FA97"}.mdi-briefcase-edit-outline:before{content:"\FC1A"}.mdi-briefcase-minus:before{content:"\FA29"}.mdi-briefcase-minus-outline:before{content:"\FC1B"}.mdi-briefcase-outline:before{content:"\F813"}.mdi-briefcase-plus:before{content:"\FA2A"}.mdi-briefcase-plus-outline:before{content:"\FC1C"}.mdi-briefcase-remove:before{content:"\FA2B"}.mdi-briefcase-remove-outline:before{content:"\FC1D"}.mdi-briefcase-search:before{content:"\FA2C"}.mdi-briefcase-search-outline:before{content:"\FC1E"}.mdi-briefcase-upload:before{content:"\F0D9"}.mdi-briefcase-upload-outline:before{content:"\FC1F"}.mdi-brightness-1:before{content:"\F0DA"}.mdi-brightness-2:before{content:"\F0DB"}.mdi-brightness-3:before{content:"\F0DC"}.mdi-brightness-4:before{content:"\F0DD"}.mdi-brightness-5:before{content:"\F0DE"}.mdi-brightness-6:before{content:"\F0DF"}.mdi-brightness-7:before{content:"\F0E0"}.mdi-brightness-auto:before{content:"\F0E1"}.mdi-brightness-percent:before{content:"\FCCE"}.mdi-broom:before{content:"\F0E2"}.mdi-brush:before{content:"\F0E3"}.mdi-buddhism:before{content:"\F94A"}.mdi-buffer:before{content:"\F619"}.mdi-bug:before{content:"\F0E4"}.mdi-bug-check:before{content:"\FA2D"}.mdi-bug-check-outline:before{content:"\FA2E"}.mdi-bug-outline:before{content:"\FA2F"}.mdi-bugle:before{content:"\FD90"}.mdi-bulldozer:before{content:"\FB07"}.mdi-bullet:before{content:"\FCCF"}.mdi-bulletin-board:before{content:"\F0E5"}.mdi-bullhorn:before{content:"\F0E6"}.mdi-bullhorn-outline:before{content:"\FB08"}.mdi-bullseye:before{content:"\F5DD"}.mdi-bullseye-arrow:before{content:"\F8C8"}.mdi-bus:before{content:"\F0E7"}.mdi-bus-alert:before{content:"\FA98"}.mdi-bus-articulated-end:before{content:"\F79B"}.mdi-bus-articulated-front:before{content:"\F79C"}.mdi-bus-clock:before{content:"\F8C9"}.mdi-bus-double-decker:before{content:"\F79D"}.mdi-bus-school:before{content:"\F79E"}.mdi-bus-side:before{content:"\F79F"}.mdi-cached:before{content:"\F0E8"}.mdi-cactus:before{content:"\FD91"}.mdi-cake:before{content:"\F0E9"}.mdi-cake-layered:before{content:"\F0EA"}.mdi-cake-variant:before{content:"\F0EB"}.mdi-calculator:before{content:"\F0EC"}.mdi-calculator-variant:before{content:"\FA99"}.mdi-calendar:before{content:"\F0ED"}.mdi-calendar-alert:before{content:"\FA30"}.mdi-calendar-blank:before{content:"\F0EE"}.mdi-calendar-blank-outline:before{content:"\FB42"}.mdi-calendar-check:before{content:"\F0EF"}.mdi-calendar-check-outline:before{content:"\FC20"}.mdi-calendar-clock:before{content:"\F0F0"}.mdi-calendar-edit:before{content:"\F8A6"}.mdi-calendar-export:before{content:"\FB09"}.mdi-calendar-heart:before{content:"\F9D1"}.mdi-calendar-import:before{content:"\FB0A"}.mdi-calendar-minus:before{content:"\FD38"}.mdi-calendar-multiple:before{content:"\F0F1"}.mdi-calendar-multiple-check:before{content:"\F0F2"}.mdi-calendar-multiselect:before{content:"\FA31"}.mdi-calendar-outline:before{content:"\FB43"}.mdi-calendar-plus:before{content:"\F0F3"}.mdi-calendar-question:before{content:"\F691"}.mdi-calendar-range:before{content:"\F678"}.mdi-calendar-range-outline:before{content:"\FB44"}.mdi-calendar-remove:before{content:"\F0F4"}.mdi-calendar-remove-outline:before{content:"\FC21"}.mdi-calendar-search:before{content:"\F94B"}.mdi-calendar-star:before{content:"\F9D2"}.mdi-calendar-text:before{content:"\F0F5"}.mdi-calendar-text-outline:before{content:"\FC22"}.mdi-calendar-today:before{content:"\F0F6"}.mdi-calendar-week:before{content:"\FA32"}.mdi-calendar-week-begin:before{content:"\FA33"}.mdi-call-made:before{content:"\F0F7"}.mdi-call-merge:before{content:"\F0F8"}.mdi-call-missed:before{content:"\F0F9"}.mdi-call-received:before{content:"\F0FA"}.mdi-call-split:before{content:"\F0FB"}.mdi-camcorder:before{content:"\F0FC"}.mdi-camcorder-box:before{content:"\F0FD"}.mdi-camcorder-box-off:before{content:"\F0FE"}.mdi-camcorder-off:before{content:"\F0FF"}.mdi-camera:before{content:"\F100"}.mdi-camera-account:before{content:"\F8CA"}.mdi-camera-burst:before{content:"\F692"}.mdi-camera-control:before{content:"\FB45"}.mdi-camera-enhance:before{content:"\F101"}.mdi-camera-enhance-outline:before{content:"\FB46"}.mdi-camera-front:before{content:"\F102"}.mdi-camera-front-variant:before{content:"\F103"}.mdi-camera-gopro:before{content:"\F7A0"}.mdi-camera-image:before{content:"\F8CB"}.mdi-camera-iris:before{content:"\F104"}.mdi-camera-metering-center:before{content:"\F7A1"}.mdi-camera-metering-matrix:before{content:"\F7A2"}.mdi-camera-metering-partial:before{content:"\F7A3"}.mdi-camera-metering-spot:before{content:"\F7A4"}.mdi-camera-off:before{content:"\F5DF"}.mdi-camera-outline:before{content:"\FD39"}.mdi-camera-party-mode:before{content:"\F105"}.mdi-camera-rear:before{content:"\F106"}.mdi-camera-rear-variant:before{content:"\F107"}.mdi-camera-switch:before{content:"\F108"}.mdi-camera-timer:before{content:"\F109"}.mdi-camera-wireless:before{content:"\FD92"}.mdi-camera-wireless-outline:before{content:"\FD93"}.mdi-cancel:before{content:"\F739"}.mdi-candle:before{content:"\F5E2"}.mdi-candycane:before{content:"\F10A"}.mdi-cannabis:before{content:"\F7A5"}.mdi-caps-lock:before{content:"\FA9A"}.mdi-car:before{content:"\F10B"}.mdi-car-battery:before{content:"\F10C"}.mdi-car-brake-abs:before{content:"\FC23"}.mdi-car-brake-alert:before{content:"\FC24"}.mdi-car-brake-hold:before{content:"\FD3A"}.mdi-car-brake-parking:before{content:"\FD3B"}.mdi-car-connected:before{content:"\F10D"}.mdi-car-convertible:before{content:"\F7A6"}.mdi-car-cruise-control:before{content:"\FD3C"}.mdi-car-defrost-front:before{content:"\FD3D"}.mdi-car-defrost-rear:before{content:"\FD3E"}.mdi-car-door:before{content:"\FB47"}.mdi-car-electric:before{content:"\FB48"}.mdi-car-esp:before{content:"\FC25"}.mdi-car-estate:before{content:"\F7A7"}.mdi-car-hatchback:before{content:"\F7A8"}.mdi-car-key:before{content:"\FB49"}.mdi-car-light-dimmed:before{content:"\FC26"}.mdi-car-light-fog:before{content:"\FC27"}.mdi-car-light-high:before{content:"\FC28"}.mdi-car-limousine:before{content:"\F8CC"}.mdi-car-multiple:before{content:"\FB4A"}.mdi-car-parking-lights:before{content:"\FD3F"}.mdi-car-pickup:before{content:"\F7A9"}.mdi-car-side:before{content:"\F7AA"}.mdi-car-sports:before{content:"\F7AB"}.mdi-car-tire-alert:before{content:"\FC29"}.mdi-car-traction-control:before{content:"\FD40"}.mdi-car-wash:before{content:"\F10E"}.mdi-caravan:before{content:"\F7AC"}.mdi-card:before{content:"\FB4B"}.mdi-card-bulleted:before{content:"\FB4C"}.mdi-card-bulleted-off:before{content:"\FB4D"}.mdi-card-bulleted-off-outline:before{content:"\FB4E"}.mdi-card-bulleted-outline:before{content:"\FB4F"}.mdi-card-bulleted-settings:before{content:"\FB50"}.mdi-card-bulleted-settings-outline:before{content:"\FB51"}.mdi-card-outline:before{content:"\FB52"}.mdi-card-text:before{content:"\FB53"}.mdi-card-text-outline:before{content:"\FB54"}.mdi-cards:before{content:"\F638"}.mdi-cards-club:before{content:"\F8CD"}.mdi-cards-diamond:before{content:"\F8CE"}.mdi-cards-heart:before{content:"\F8CF"}.mdi-cards-outline:before{content:"\F639"}.mdi-cards-playing-outline:before{content:"\F63A"}.mdi-cards-spade:before{content:"\F8D0"}.mdi-cards-variant:before{content:"\F6C6"}.mdi-carrot:before{content:"\F10F"}.mdi-carry-on-bag-check:before{content:"\FD41"}.mdi-cart:before{content:"\F110"}.mdi-cart-arrow-down:before{content:"\FD42"}.mdi-cart-arrow-right:before{content:"\FC2A"}.mdi-cart-arrow-up:before{content:"\FD43"}.mdi-cart-minus:before{content:"\FD44"}.mdi-cart-off:before{content:"\F66B"}.mdi-cart-outline:before{content:"\F111"}.mdi-cart-plus:before{content:"\F112"}.mdi-cart-remove:before{content:"\FD45"}.mdi-case-sensitive-alt:before{content:"\F113"}.mdi-cash:before{content:"\F114"}.mdi-cash-100:before{content:"\F115"}.mdi-cash-marker:before{content:"\FD94"}.mdi-cash-multiple:before{content:"\F116"}.mdi-cash-refund:before{content:"\FA9B"}.mdi-cash-register:before{content:"\FCD0"}.mdi-cash-usd:before{content:"\F117"}.mdi-cassette:before{content:"\F9D3"}.mdi-cast:before{content:"\F118"}.mdi-cast-connected:before{content:"\F119"}.mdi-cast-off:before{content:"\F789"}.mdi-castle:before{content:"\F11A"}.mdi-cat:before{content:"\F11B"}.mdi-cctv:before{content:"\F7AD"}.mdi-ceiling-light:before{content:"\F768"}.mdi-cellphone:before{content:"\F11C"}.mdi-cellphone-android:before{content:"\F11D"}.mdi-cellphone-arrow-down:before{content:"\F9D4"}.mdi-cellphone-basic:before{content:"\F11E"}.mdi-cellphone-dock:before{content:"\F11F"}.mdi-cellphone-erase:before{content:"\F94C"}.mdi-cellphone-iphone:before{content:"\F120"}.mdi-cellphone-key:before{content:"\F94D"}.mdi-cellphone-link:before{content:"\F121"}.mdi-cellphone-link-off:before{content:"\F122"}.mdi-cellphone-lock:before{content:"\F94E"}.mdi-cellphone-message:before{content:"\F8D2"}.mdi-cellphone-off:before{content:"\F94F"}.mdi-cellphone-screenshot:before{content:"\FA34"}.mdi-cellphone-settings:before{content:"\F123"}.mdi-cellphone-settings-variant:before{content:"\F950"}.mdi-cellphone-sound:before{content:"\F951"}.mdi-cellphone-text:before{content:"\F8D1"}.mdi-cellphone-wireless:before{content:"\F814"}.mdi-celtic-cross:before{content:"\FCD1"}.mdi-certificate:before{content:"\F124"}.mdi-chair-school:before{content:"\F125"}.mdi-charity:before{content:"\FC2B"}.mdi-chart-arc:before{content:"\F126"}.mdi-chart-areaspline:before{content:"\F127"}.mdi-chart-bar:before{content:"\F128"}.mdi-chart-bar-stacked:before{content:"\F769"}.mdi-chart-bell-curve:before{content:"\FC2C"}.mdi-chart-bubble:before{content:"\F5E3"}.mdi-chart-donut:before{content:"\F7AE"}.mdi-chart-donut-variant:before{content:"\F7AF"}.mdi-chart-gantt:before{content:"\F66C"}.mdi-chart-histogram:before{content:"\F129"}.mdi-chart-line:before{content:"\F12A"}.mdi-chart-line-stacked:before{content:"\F76A"}.mdi-chart-line-variant:before{content:"\F7B0"}.mdi-chart-multiline:before{content:"\F8D3"}.mdi-chart-pie:before{content:"\F12B"}.mdi-chart-scatterplot-hexbin:before{content:"\F66D"}.mdi-chart-timeline:before{content:"\F66E"}.mdi-chat:before{content:"\FB55"}.mdi-chat-alert:before{content:"\FB56"}.mdi-chat-processing:before{content:"\FB57"}.mdi-check:before{content:"\F12C"}.mdi-check-all:before{content:"\F12D"}.mdi-check-box-multiple-outline:before{content:"\FC2D"}.mdi-check-box-outline:before{content:"\FC2E"}.mdi-check-circle:before{content:"\F5E0"}.mdi-check-circle-outline:before{content:"\F5E1"}.mdi-check-decagram:before{content:"\F790"}.mdi-check-network:before{content:"\FC2F"}.mdi-check-network-outline:before{content:"\FC30"}.mdi-check-outline:before{content:"\F854"}.mdi-checkbook:before{content:"\FA9C"}.mdi-checkbox-blank:before{content:"\F12E"}.mdi-checkbox-blank-circle:before{content:"\F12F"}.mdi-checkbox-blank-circle-outline:before{content:"\F130"}.mdi-checkbox-blank-outline:before{content:"\F131"}.mdi-checkbox-intermediate:before{content:"\F855"}.mdi-checkbox-marked:before{content:"\F132"}.mdi-checkbox-marked-circle:before{content:"\F133"}.mdi-checkbox-marked-circle-outline:before{content:"\F134"}.mdi-checkbox-marked-outline:before{content:"\F135"}.mdi-checkbox-multiple-blank:before{content:"\F136"}.mdi-checkbox-multiple-blank-circle:before{content:"\F63B"}.mdi-checkbox-multiple-blank-circle-outline:before{content:"\F63C"}.mdi-checkbox-multiple-blank-outline:before{content:"\F137"}.mdi-checkbox-multiple-marked:before{content:"\F138"}.mdi-checkbox-multiple-marked-circle:before{content:"\F63D"}.mdi-checkbox-multiple-marked-circle-outline:before{content:"\F63E"}.mdi-checkbox-multiple-marked-outline:before{content:"\F139"}.mdi-checkerboard:before{content:"\F13A"}.mdi-chef-hat:before{content:"\FB58"}.mdi-chemical-weapon:before{content:"\F13B"}.mdi-chess-bishop:before{content:"\F85B"}.mdi-chess-king:before{content:"\F856"}.mdi-chess-knight:before{content:"\F857"}.mdi-chess-pawn:before{content:"\F858"}.mdi-chess-queen:before{content:"\F859"}.mdi-chess-rook:before{content:"\F85A"}.mdi-chevron-double-down:before{content:"\F13C"}.mdi-chevron-double-left:before{content:"\F13D"}.mdi-chevron-double-right:before{content:"\F13E"}.mdi-chevron-double-up:before{content:"\F13F"}.mdi-chevron-down:before{content:"\F140"}.mdi-chevron-down-box:before{content:"\F9D5"}.mdi-chevron-down-box-outline:before{content:"\F9D6"}.mdi-chevron-down-circle:before{content:"\FB0B"}.mdi-chevron-down-circle-outline:before{content:"\FB0C"}.mdi-chevron-left:before{content:"\F141"}.mdi-chevron-left-box:before{content:"\F9D7"}.mdi-chevron-left-box-outline:before{content:"\F9D8"}.mdi-chevron-left-circle:before{content:"\FB0D"}.mdi-chevron-left-circle-outline:before{content:"\FB0E"}.mdi-chevron-right:before{content:"\F142"}.mdi-chevron-right-box:before{content:"\F9D9"}.mdi-chevron-right-box-outline:before{content:"\F9DA"}.mdi-chevron-right-circle:before{content:"\FB0F"}.mdi-chevron-right-circle-outline:before{content:"\FB10"}.mdi-chevron-triple-down:before{content:"\FD95"}.mdi-chevron-triple-left:before{content:"\FD96"}.mdi-chevron-triple-right:before{content:"\FD97"}.mdi-chevron-triple-up:before{content:"\FD98"}.mdi-chevron-up:before{content:"\F143"}.mdi-chevron-up-box:before{content:"\F9DB"}.mdi-chevron-up-box-outline:before{content:"\F9DC"}.mdi-chevron-up-circle:before{content:"\FB11"}.mdi-chevron-up-circle-outline:before{content:"\FB12"}.mdi-chili-hot:before{content:"\F7B1"}.mdi-chili-medium:before{content:"\F7B2"}.mdi-chili-mild:before{content:"\F7B3"}.mdi-chip:before{content:"\F61A"}.mdi-christianity:before{content:"\F952"}.mdi-christianity-outline:before{content:"\FCD2"}.mdi-church:before{content:"\F144"}.mdi-circle:before{content:"\F764"}.mdi-circle-edit-outline:before{content:"\F8D4"}.mdi-circle-medium:before{content:"\F9DD"}.mdi-circle-outline:before{content:"\F765"}.mdi-circle-slice-1:before{content:"\FA9D"}.mdi-circle-slice-2:before{content:"\FA9E"}.mdi-circle-slice-3:before{content:"\FA9F"}.mdi-circle-slice-4:before{content:"\FAA0"}.mdi-circle-slice-5:before{content:"\FAA1"}.mdi-circle-slice-6:before{content:"\FAA2"}.mdi-circle-slice-7:before{content:"\FAA3"}.mdi-circle-slice-8:before{content:"\FAA4"}.mdi-circle-small:before{content:"\F9DE"}.mdi-cisco-webex:before{content:"\F145"}.mdi-city:before{content:"\F146"}.mdi-city-variant:before{content:"\FA35"}.mdi-city-variant-outline:before{content:"\FA36"}.mdi-clipboard:before{content:"\F147"}.mdi-clipboard-account:before{content:"\F148"}.mdi-clipboard-account-outline:before{content:"\FC31"}.mdi-clipboard-alert:before{content:"\F149"}.mdi-clipboard-alert-outline:before{content:"\FCD3"}.mdi-clipboard-arrow-down:before{content:"\F14A"}.mdi-clipboard-arrow-down-outline:before{content:"\FC32"}.mdi-clipboard-arrow-left:before{content:"\F14B"}.mdi-clipboard-arrow-left-outline:before{content:"\FCD4"}.mdi-clipboard-arrow-right:before{content:"\FCD5"}.mdi-clipboard-arrow-right-outline:before{content:"\FCD6"}.mdi-clipboard-arrow-up:before{content:"\FC33"}.mdi-clipboard-arrow-up-outline:before{content:"\FC34"}.mdi-clipboard-check:before{content:"\F14C"}.mdi-clipboard-check-outline:before{content:"\F8A7"}.mdi-clipboard-flow:before{content:"\F6C7"}.mdi-clipboard-outline:before{content:"\F14D"}.mdi-clipboard-play:before{content:"\FC35"}.mdi-clipboard-play-outline:before{content:"\FC36"}.mdi-clipboard-plus:before{content:"\F750"}.mdi-clipboard-pulse:before{content:"\F85C"}.mdi-clipboard-pulse-outline:before{content:"\F85D"}.mdi-clipboard-text:before{content:"\F14E"}.mdi-clipboard-text-outline:before{content:"\FA37"}.mdi-clipboard-text-play:before{content:"\FC37"}.mdi-clipboard-text-play-outline:before{content:"\FC38"}.mdi-clippy:before{content:"\F14F"}.mdi-clock:before{content:"\F953"}.mdi-clock-alert:before{content:"\F954"}.mdi-clock-alert-outline:before{content:"\F5CE"}.mdi-clock-end:before{content:"\F151"}.mdi-clock-fast:before{content:"\F152"}.mdi-clock-in:before{content:"\F153"}.mdi-clock-out:before{content:"\F154"}.mdi-clock-outline:before{content:"\F150"}.mdi-clock-start:before{content:"\F155"}.mdi-close:before{content:"\F156"}.mdi-close-box:before{content:"\F157"}.mdi-close-box-multiple:before{content:"\FC39"}.mdi-close-box-multiple-outline:before{content:"\FC3A"}.mdi-close-box-outline:before{content:"\F158"}.mdi-close-circle:before{content:"\F159"}.mdi-close-circle-outline:before{content:"\F15A"}.mdi-close-network:before{content:"\F15B"}.mdi-close-network-outline:before{content:"\FC3B"}.mdi-close-octagon:before{content:"\F15C"}.mdi-close-octagon-outline:before{content:"\F15D"}.mdi-close-outline:before{content:"\F6C8"}.mdi-closed-caption:before{content:"\F15E"}.mdi-closed-caption-outline:before{content:"\FD99"}.mdi-cloud:before{content:"\F15F"}.mdi-cloud-alert:before{content:"\F9DF"}.mdi-cloud-braces:before{content:"\F7B4"}.mdi-cloud-check:before{content:"\F160"}.mdi-cloud-circle:before{content:"\F161"}.mdi-cloud-download:before{content:"\F162"}.mdi-cloud-download-outline:before{content:"\FB59"}.mdi-cloud-off-outline:before{content:"\F164"}.mdi-cloud-outline:before{content:"\F163"}.mdi-cloud-print:before{content:"\F165"}.mdi-cloud-print-outline:before{content:"\F166"}.mdi-cloud-question:before{content:"\FA38"}.mdi-cloud-search:before{content:"\F955"}.mdi-cloud-search-outline:before{content:"\F956"}.mdi-cloud-sync:before{content:"\F63F"}.mdi-cloud-tags:before{content:"\F7B5"}.mdi-cloud-upload:before{content:"\F167"}.mdi-cloud-upload-outline:before{content:"\FB5A"}.mdi-clover:before{content:"\F815"}.mdi-code-array:before{content:"\F168"}.mdi-code-braces:before{content:"\F169"}.mdi-code-brackets:before{content:"\F16A"}.mdi-code-equal:before{content:"\F16B"}.mdi-code-greater-than:before{content:"\F16C"}.mdi-code-greater-than-or-equal:before{content:"\F16D"}.mdi-code-less-than:before{content:"\F16E"}.mdi-code-less-than-or-equal:before{content:"\F16F"}.mdi-code-not-equal:before{content:"\F170"}.mdi-code-not-equal-variant:before{content:"\F171"}.mdi-code-parentheses:before{content:"\F172"}.mdi-code-string:before{content:"\F173"}.mdi-code-tags:before{content:"\F174"}.mdi-code-tags-check:before{content:"\F693"}.mdi-codepen:before{content:"\F175"}.mdi-coffee:before{content:"\F176"}.mdi-coffee-outline:before{content:"\F6C9"}.mdi-coffee-to-go:before{content:"\F177"}.mdi-coffin:before{content:"\FB5B"}.mdi-cogs:before{content:"\F8D5"}.mdi-coin:before{content:"\F178"}.mdi-coins:before{content:"\F694"}.mdi-collage:before{content:"\F640"}.mdi-collapse-all:before{content:"\FAA5"}.mdi-collapse-all-outline:before{content:"\FAA6"}.mdi-color-helper:before{content:"\F179"}.mdi-comment:before{content:"\F17A"}.mdi-comment-account:before{content:"\F17B"}.mdi-comment-account-outline:before{content:"\F17C"}.mdi-comment-alert:before{content:"\F17D"}.mdi-comment-alert-outline:before{content:"\F17E"}.mdi-comment-arrow-left:before{content:"\F9E0"}.mdi-comment-arrow-left-outline:before{content:"\F9E1"}.mdi-comment-arrow-right:before{content:"\F9E2"}.mdi-comment-arrow-right-outline:before{content:"\F9E3"}.mdi-comment-check:before{content:"\F17F"}.mdi-comment-check-outline:before{content:"\F180"}.mdi-comment-eye:before{content:"\FA39"}.mdi-comment-eye-outline:before{content:"\FA3A"}.mdi-comment-multiple:before{content:"\F85E"}.mdi-comment-multiple-outline:before{content:"\F181"}.mdi-comment-outline:before{content:"\F182"}.mdi-comment-plus:before{content:"\F9E4"}.mdi-comment-plus-outline:before{content:"\F183"}.mdi-comment-processing:before{content:"\F184"}.mdi-comment-processing-outline:before{content:"\F185"}.mdi-comment-question:before{content:"\F816"}.mdi-comment-question-outline:before{content:"\F186"}.mdi-comment-remove:before{content:"\F5DE"}.mdi-comment-remove-outline:before{content:"\F187"}.mdi-comment-search:before{content:"\FA3B"}.mdi-comment-search-outline:before{content:"\FA3C"}.mdi-comment-text:before{content:"\F188"}.mdi-comment-text-multiple:before{content:"\F85F"}.mdi-comment-text-multiple-outline:before{content:"\F860"}.mdi-comment-text-outline:before{content:"\F189"}.mdi-compare:before{content:"\F18A"}.mdi-compass:before{content:"\F18B"}.mdi-compass-off:before{content:"\FB5C"}.mdi-compass-off-outline:before{content:"\FB5D"}.mdi-compass-outline:before{content:"\F18C"}.mdi-console:before{content:"\F18D"}.mdi-console-line:before{content:"\F7B6"}.mdi-console-network:before{content:"\F8A8"}.mdi-console-network-outline:before{content:"\FC3C"}.mdi-contact-mail:before{content:"\F18E"}.mdi-contactless-payment:before{content:"\FD46"}.mdi-contacts:before{content:"\F6CA"}.mdi-contain:before{content:"\FA3D"}.mdi-contain-end:before{content:"\FA3E"}.mdi-contain-start:before{content:"\FA3F"}.mdi-content-copy:before{content:"\F18F"}.mdi-content-cut:before{content:"\F190"}.mdi-content-duplicate:before{content:"\F191"}.mdi-content-paste:before{content:"\F192"}.mdi-content-save:before{content:"\F193"}.mdi-content-save-all:before{content:"\F194"}.mdi-content-save-edit:before{content:"\FCD7"}.mdi-content-save-edit-outline:before{content:"\FCD8"}.mdi-content-save-outline:before{content:"\F817"}.mdi-content-save-settings:before{content:"\F61B"}.mdi-content-save-settings-outline:before{content:"\FB13"}.mdi-contrast:before{content:"\F195"}.mdi-contrast-box:before{content:"\F196"}.mdi-contrast-circle:before{content:"\F197"}.mdi-controller-classic:before{content:"\FB5E"}.mdi-controller-classic-outline:before{content:"\FB5F"}.mdi-cookie:before{content:"\F198"}.mdi-copyright:before{content:"\F5E6"}.mdi-cordova:before{content:"\F957"}.mdi-corn:before{content:"\F7B7"}.mdi-counter:before{content:"\F199"}.mdi-cow:before{content:"\F19A"}.mdi-crane:before{content:"\F861"}.mdi-creation:before{content:"\F1C9"}.mdi-creative-commons:before{content:"\FD47"}.mdi-credit-card:before{content:"\F19B"}.mdi-credit-card-marker:before{content:"\FD9A"}.mdi-credit-card-multiple:before{content:"\F19C"}.mdi-credit-card-off:before{content:"\F5E4"}.mdi-credit-card-plus:before{content:"\F675"}.mdi-credit-card-refund:before{content:"\FAA7"}.mdi-credit-card-scan:before{content:"\F19D"}.mdi-credit-card-settings:before{content:"\F8D6"}.mdi-credit-card-wireless:before{content:"\FD48"}.mdi-cricket:before{content:"\FD49"}.mdi-crop:before{content:"\F19E"}.mdi-crop-free:before{content:"\F19F"}.mdi-crop-landscape:before{content:"\F1A0"}.mdi-crop-portrait:before{content:"\F1A1"}.mdi-crop-rotate:before{content:"\F695"}.mdi-crop-square:before{content:"\F1A2"}.mdi-crosshairs:before{content:"\F1A3"}.mdi-crosshairs-gps:before{content:"\F1A4"}.mdi-crown:before{content:"\F1A5"}.mdi-cryengine:before{content:"\F958"}.mdi-crystal-ball:before{content:"\FB14"}.mdi-cube:before{content:"\F1A6"}.mdi-cube-outline:before{content:"\F1A7"}.mdi-cube-scan:before{content:"\FB60"}.mdi-cube-send:before{content:"\F1A8"}.mdi-cube-unfolded:before{content:"\F1A9"}.mdi-cup:before{content:"\F1AA"}.mdi-cup-off:before{content:"\F5E5"}.mdi-cup-water:before{content:"\F1AB"}.mdi-cupcake:before{content:"\F959"}.mdi-curling:before{content:"\F862"}.mdi-currency-bdt:before{content:"\F863"}.mdi-currency-brl:before{content:"\FB61"}.mdi-currency-btc:before{content:"\F1AC"}.mdi-currency-chf:before{content:"\F7B8"}.mdi-currency-cny:before{content:"\F7B9"}.mdi-currency-eth:before{content:"\F7BA"}.mdi-currency-eur:before{content:"\F1AD"}.mdi-currency-gbp:before{content:"\F1AE"}.mdi-currency-ils:before{content:"\FC3D"}.mdi-currency-inr:before{content:"\F1AF"}.mdi-currency-jpy:before{content:"\F7BB"}.mdi-currency-krw:before{content:"\F7BC"}.mdi-currency-kzt:before{content:"\F864"}.mdi-currency-ngn:before{content:"\F1B0"}.mdi-currency-php:before{content:"\F9E5"}.mdi-currency-rub:before{content:"\F1B1"}.mdi-currency-sign:before{content:"\F7BD"}.mdi-currency-try:before{content:"\F1B2"}.mdi-currency-twd:before{content:"\F7BE"}.mdi-currency-usd:before{content:"\F1B3"}.mdi-currency-usd-off:before{content:"\F679"}.mdi-current-ac:before{content:"\F95A"}.mdi-current-dc:before{content:"\F95B"}.mdi-cursor-default:before{content:"\F1B4"}.mdi-cursor-default-click:before{content:"\FCD9"}.mdi-cursor-default-click-outline:before{content:"\FCDA"}.mdi-cursor-default-outline:before{content:"\F1B5"}.mdi-cursor-move:before{content:"\F1B6"}.mdi-cursor-pointer:before{content:"\F1B7"}.mdi-cursor-text:before{content:"\F5E7"}.mdi-database:before{content:"\F1B8"}.mdi-database-check:before{content:"\FAA8"}.mdi-database-edit:before{content:"\FB62"}.mdi-database-export:before{content:"\F95D"}.mdi-database-import:before{content:"\F95C"}.mdi-database-lock:before{content:"\FAA9"}.mdi-database-minus:before{content:"\F1B9"}.mdi-database-plus:before{content:"\F1BA"}.mdi-database-refresh:before{content:"\FCDB"}.mdi-database-remove:before{content:"\FCDC"}.mdi-database-search:before{content:"\F865"}.mdi-database-settings:before{content:"\FCDD"}.mdi-death-star:before{content:"\F8D7"}.mdi-death-star-variant:before{content:"\F8D8"}.mdi-deathly-hallows:before{content:"\FB63"}.mdi-debian:before{content:"\F8D9"}.mdi-debug-step-into:before{content:"\F1BB"}.mdi-debug-step-out:before{content:"\F1BC"}.mdi-debug-step-over:before{content:"\F1BD"}.mdi-decagram:before{content:"\F76B"}.mdi-decagram-outline:before{content:"\F76C"}.mdi-decimal-decrease:before{content:"\F1BE"}.mdi-decimal-increase:before{content:"\F1BF"}.mdi-delete:before{content:"\F1C0"}.mdi-delete-circle:before{content:"\F682"}.mdi-delete-circle-outline:before{content:"\FB64"}.mdi-delete-empty:before{content:"\F6CB"}.mdi-delete-forever:before{content:"\F5E8"}.mdi-delete-forever-outline:before{content:"\FB65"}.mdi-delete-outline:before{content:"\F9E6"}.mdi-delete-restore:before{content:"\F818"}.mdi-delete-sweep:before{content:"\F5E9"}.mdi-delete-sweep-outline:before{content:"\FC3E"}.mdi-delete-variant:before{content:"\F1C1"}.mdi-delta:before{content:"\F1C2"}.mdi-desk-lamp:before{content:"\F95E"}.mdi-deskphone:before{content:"\F1C3"}.mdi-desktop-classic:before{content:"\F7BF"}.mdi-desktop-mac:before{content:"\F1C4"}.mdi-desktop-mac-dashboard:before{content:"\F9E7"}.mdi-desktop-tower:before{content:"\F1C5"}.mdi-desktop-tower-monitor:before{content:"\FAAA"}.mdi-details:before{content:"\F1C6"}.mdi-dev-to:before{content:"\FD4A"}.mdi-developer-board:before{content:"\F696"}.mdi-deviantart:before{content:"\F1C7"}.mdi-dialpad:before{content:"\F61C"}.mdi-diameter:before{content:"\FC3F"}.mdi-diameter-outline:before{content:"\FC40"}.mdi-diameter-variant:before{content:"\FC41"}.mdi-diamond:before{content:"\FB66"}.mdi-diamond-outline:before{content:"\FB67"}.mdi-diamond-stone:before{content:"\F1C8"}.mdi-dice-1:before{content:"\F1CA"}.mdi-dice-2:before{content:"\F1CB"}.mdi-dice-3:before{content:"\F1CC"}.mdi-dice-4:before{content:"\F1CD"}.mdi-dice-5:before{content:"\F1CE"}.mdi-dice-6:before{content:"\F1CF"}.mdi-dice-d10:before{content:"\F76E"}.mdi-dice-d12:before{content:"\F866"}.mdi-dice-d20:before{content:"\F5EA"}.mdi-dice-d4:before{content:"\F5EB"}.mdi-dice-d6:before{content:"\F5EC"}.mdi-dice-d8:before{content:"\F5ED"}.mdi-dice-multiple:before{content:"\F76D"}.mdi-dictionary:before{content:"\F61D"}.mdi-dip-switch:before{content:"\F7C0"}.mdi-directions:before{content:"\F1D0"}.mdi-directions-fork:before{content:"\F641"}.mdi-disc:before{content:"\F5EE"}.mdi-disc-alert:before{content:"\F1D1"}.mdi-disc-player:before{content:"\F95F"}.mdi-discord:before{content:"\F66F"}.mdi-dishwasher:before{content:"\FAAB"}.mdi-disqus:before{content:"\F1D2"}.mdi-disqus-outline:before{content:"\F1D3"}.mdi-diving-flippers:before{content:"\FD9B"}.mdi-diving-helmet:before{content:"\FD9C"}.mdi-diving-scuba:before{content:"\FD9D"}.mdi-diving-scuba-flag:before{content:"\FD9E"}.mdi-diving-scuba-tank:before{content:"\FD9F"}.mdi-diving-scuba-tank-multiple:before{content:"\FDA0"}.mdi-diving-snorkel:before{content:"\FDA1"}.mdi-division:before{content:"\F1D4"}.mdi-division-box:before{content:"\F1D5"}.mdi-dlna:before{content:"\FA40"}.mdi-dna:before{content:"\F683"}.mdi-dns:before{content:"\F1D6"}.mdi-dns-outline:before{content:"\FB68"}.mdi-do-not-disturb:before{content:"\F697"}.mdi-do-not-disturb-off:before{content:"\F698"}.mdi-docker:before{content:"\F867"}.mdi-doctor:before{content:"\FA41"}.mdi-dog:before{content:"\FA42"}.mdi-dog-service:before{content:"\FAAC"}.mdi-dog-side:before{content:"\FA43"}.mdi-dolby:before{content:"\F6B2"}.mdi-domain:before{content:"\F1D7"}.mdi-domain-off:before{content:"\FD4B"}.mdi-donkey:before{content:"\F7C1"}.mdi-door:before{content:"\F819"}.mdi-door-closed:before{content:"\F81A"}.mdi-door-open:before{content:"\F81B"}.mdi-doorbell-video:before{content:"\F868"}.mdi-dot-net:before{content:"\FAAD"}.mdi-dots-horizontal:before{content:"\F1D8"}.mdi-dots-horizontal-circle:before{content:"\F7C2"}.mdi-dots-horizontal-circle-outline:before{content:"\FB69"}.mdi-dots-vertical:before{content:"\F1D9"}.mdi-dots-vertical-circle:before{content:"\F7C3"}.mdi-dots-vertical-circle-outline:before{content:"\FB6A"}.mdi-douban:before{content:"\F699"}.mdi-download:before{content:"\F1DA"}.mdi-download-multiple:before{content:"\F9E8"}.mdi-download-network:before{content:"\F6F3"}.mdi-download-network-outline:before{content:"\FC42"}.mdi-download-outline:before{content:"\FB6B"}.mdi-drag:before{content:"\F1DB"}.mdi-drag-horizontal:before{content:"\F1DC"}.mdi-drag-variant:before{content:"\FB6C"}.mdi-drag-vertical:before{content:"\F1DD"}.mdi-drama-masks:before{content:"\FCDE"}.mdi-drawing:before{content:"\F1DE"}.mdi-drawing-box:before{content:"\F1DF"}.mdi-dribbble:before{content:"\F1E0"}.mdi-dribbble-box:before{content:"\F1E1"}.mdi-drone:before{content:"\F1E2"}.mdi-dropbox:before{content:"\F1E3"}.mdi-drupal:before{content:"\F1E4"}.mdi-duck:before{content:"\F1E5"}.mdi-dumbbell:before{content:"\F1E6"}.mdi-dump-truck:before{content:"\FC43"}.mdi-ear-hearing:before{content:"\F7C4"}.mdi-ear-hearing-off:before{content:"\FA44"}.mdi-earth:before{content:"\F1E7"}.mdi-earth-box:before{content:"\F6CC"}.mdi-earth-box-off:before{content:"\F6CD"}.mdi-earth-off:before{content:"\F1E8"}.mdi-edge:before{content:"\F1E9"}.mdi-egg:before{content:"\FAAE"}.mdi-egg-easter:before{content:"\FAAF"}.mdi-eight-track:before{content:"\F9E9"}.mdi-eject:before{content:"\F1EA"}.mdi-eject-outline:before{content:"\FB6D"}.mdi-elephant:before{content:"\F7C5"}.mdi-elevation-decline:before{content:"\F1EB"}.mdi-elevation-rise:before{content:"\F1EC"}.mdi-elevator:before{content:"\F1ED"}.mdi-email:before{content:"\F1EE"}.mdi-email-alert:before{content:"\F6CE"}.mdi-email-box:before{content:"\FCDF"}.mdi-email-check:before{content:"\FAB0"}.mdi-email-check-outline:before{content:"\FAB1"}.mdi-email-lock:before{content:"\F1F1"}.mdi-email-mark-as-unread:before{content:"\FB6E"}.mdi-email-open:before{content:"\F1EF"}.mdi-email-open-outline:before{content:"\F5EF"}.mdi-email-outline:before{content:"\F1F0"}.mdi-email-plus:before{content:"\F9EA"}.mdi-email-plus-outline:before{content:"\F9EB"}.mdi-email-search:before{content:"\F960"}.mdi-email-search-outline:before{content:"\F961"}.mdi-email-variant:before{content:"\F5F0"}.mdi-ember:before{content:"\FB15"}.mdi-emby:before{content:"\F6B3"}.mdi-emoticon:before{content:"\FC44"}.mdi-emoticon-angry:before{content:"\FC45"}.mdi-emoticon-angry-outline:before{content:"\FC46"}.mdi-emoticon-cool:before{content:"\FC47"}.mdi-emoticon-cool-outline:before{content:"\F1F3"}.mdi-emoticon-cry:before{content:"\FC48"}.mdi-emoticon-cry-outline:before{content:"\FC49"}.mdi-emoticon-dead:before{content:"\FC4A"}.mdi-emoticon-dead-outline:before{content:"\F69A"}.mdi-emoticon-devil:before{content:"\FC4B"}.mdi-emoticon-devil-outline:before{content:"\F1F4"}.mdi-emoticon-excited:before{content:"\FC4C"}.mdi-emoticon-excited-outline:before{content:"\F69B"}.mdi-emoticon-happy:before{content:"\FC4D"}.mdi-emoticon-happy-outline:before{content:"\F1F5"}.mdi-emoticon-kiss:before{content:"\FC4E"}.mdi-emoticon-kiss-outline:before{content:"\FC4F"}.mdi-emoticon-neutral:before{content:"\FC50"}.mdi-emoticon-neutral-outline:before{content:"\F1F6"}.mdi-emoticon-outline:before{content:"\F1F2"}.mdi-emoticon-poop:before{content:"\F1F7"}.mdi-emoticon-poop-outline:before{content:"\FC51"}.mdi-emoticon-sad:before{content:"\FC52"}.mdi-emoticon-sad-outline:before{content:"\F1F8"}.mdi-emoticon-tongue:before{content:"\F1F9"}.mdi-emoticon-tongue-outline:before{content:"\FC53"}.mdi-emoticon-wink:before{content:"\FC54"}.mdi-emoticon-wink-outline:before{content:"\FC55"}.mdi-engine:before{content:"\F1FA"}.mdi-engine-off:before{content:"\FA45"}.mdi-engine-off-outline:before{content:"\FA46"}.mdi-engine-outline:before{content:"\F1FB"}.mdi-equal:before{content:"\F1FC"}.mdi-equal-box:before{content:"\F1FD"}.mdi-eraser:before{content:"\F1FE"}.mdi-eraser-variant:before{content:"\F642"}.mdi-escalator:before{content:"\F1FF"}.mdi-eslint:before{content:"\FC56"}.mdi-et:before{content:"\FAB2"}.mdi-ethereum:before{content:"\F869"}.mdi-ethernet:before{content:"\F200"}.mdi-ethernet-cable:before{content:"\F201"}.mdi-ethernet-cable-off:before{content:"\F202"}.mdi-etsy:before{content:"\F203"}.mdi-ev-station:before{content:"\F5F1"}.mdi-eventbrite:before{content:"\F7C6"}.mdi-evernote:before{content:"\F204"}.mdi-exclamation:before{content:"\F205"}.mdi-exit-run:before{content:"\FA47"}.mdi-exit-to-app:before{content:"\F206"}.mdi-expand-all:before{content:"\FAB3"}.mdi-expand-all-outline:before{content:"\FAB4"}.mdi-exponent:before{content:"\F962"}.mdi-exponent-box:before{content:"\F963"}.mdi-export:before{content:"\F207"}.mdi-export-variant:before{content:"\FB6F"}.mdi-eye:before{content:"\F208"}.mdi-eye-check:before{content:"\FCE0"}.mdi-eye-check-outline:before{content:"\FCE1"}.mdi-eye-circle:before{content:"\FB70"}.mdi-eye-circle-outline:before{content:"\FB71"}.mdi-eye-off:before{content:"\F209"}.mdi-eye-off-outline:before{content:"\F6D0"}.mdi-eye-outline:before{content:"\F6CF"}.mdi-eye-plus:before{content:"\F86A"}.mdi-eye-plus-outline:before{content:"\F86B"}.mdi-eye-settings:before{content:"\F86C"}.mdi-eye-settings-outline:before{content:"\F86D"}.mdi-eyedropper:before{content:"\F20A"}.mdi-eyedropper-variant:before{content:"\F20B"}.mdi-face:before{content:"\F643"}.mdi-face-agent:before{content:"\FD4C"}.mdi-face-outline:before{content:"\FB72"}.mdi-face-profile:before{content:"\F644"}.mdi-face-recognition:before{content:"\FC57"}.mdi-facebook:before{content:"\F20C"}.mdi-facebook-box:before{content:"\F20D"}.mdi-facebook-messenger:before{content:"\F20E"}.mdi-facebook-workplace:before{content:"\FB16"}.mdi-factory:before{content:"\F20F"}.mdi-fan:before{content:"\F210"}.mdi-fan-off:before{content:"\F81C"}.mdi-fast-forward:before{content:"\F211"}.mdi-fast-forward-10:before{content:"\FD4D"}.mdi-fast-forward-30:before{content:"\FCE2"}.mdi-fast-forward-outline:before{content:"\F6D1"}.mdi-fax:before{content:"\F212"}.mdi-feather:before{content:"\F6D2"}.mdi-feature-search:before{content:"\FA48"}.mdi-feature-search-outline:before{content:"\FA49"}.mdi-fedora:before{content:"\F8DA"}.mdi-ferry:before{content:"\F213"}.mdi-file:before{content:"\F214"}.mdi-file-account:before{content:"\F73A"}.mdi-file-alert:before{content:"\FA4A"}.mdi-file-alert-outline:before{content:"\FA4B"}.mdi-file-cabinet:before{content:"\FAB5"}.mdi-file-cancel:before{content:"\FDA2"}.mdi-file-cancel-outline:before{content:"\FDA3"}.mdi-file-chart:before{content:"\F215"}.mdi-file-check:before{content:"\F216"}.mdi-file-cloud:before{content:"\F217"}.mdi-file-compare:before{content:"\F8A9"}.mdi-file-delimited:before{content:"\F218"}.mdi-file-document:before{content:"\F219"}.mdi-file-document-box:before{content:"\F21A"}.mdi-file-document-box-multiple:before{content:"\FAB6"}.mdi-file-document-box-multiple-outline:before{content:"\FAB7"}.mdi-file-document-box-outline:before{content:"\F9EC"}.mdi-file-document-edit:before{content:"\FDA4"}.mdi-file-document-edit-outline:before{content:"\FDA5"}.mdi-file-document-outline:before{content:"\F9ED"}.mdi-file-download:before{content:"\F964"}.mdi-file-download-outline:before{content:"\F965"}.mdi-file-excel:before{content:"\F21B"}.mdi-file-excel-box:before{content:"\F21C"}.mdi-file-export:before{content:"\F21D"}.mdi-file-eye:before{content:"\FDA6"}.mdi-file-eye-outline:before{content:"\FDA7"}.mdi-file-find:before{content:"\F21E"}.mdi-file-find-outline:before{content:"\FB73"}.mdi-file-hidden:before{content:"\F613"}.mdi-file-image:before{content:"\F21F"}.mdi-file-import:before{content:"\F220"}.mdi-file-lock:before{content:"\F221"}.mdi-file-move:before{content:"\FAB8"}.mdi-file-multiple:before{content:"\F222"}.mdi-file-music:before{content:"\F223"}.mdi-file-outline:before{content:"\F224"}.mdi-file-pdf:before{content:"\F225"}.mdi-file-pdf-box:before{content:"\F226"}.mdi-file-percent:before{content:"\F81D"}.mdi-file-plus:before{content:"\F751"}.mdi-file-powerpoint:before{content:"\F227"}.mdi-file-powerpoint-box:before{content:"\F228"}.mdi-file-presentation-box:before{content:"\F229"}.mdi-file-question:before{content:"\F86E"}.mdi-file-remove:before{content:"\FB74"}.mdi-file-replace:before{content:"\FB17"}.mdi-file-replace-outline:before{content:"\FB18"}.mdi-file-restore:before{content:"\F670"}.mdi-file-search:before{content:"\FC58"}.mdi-file-search-outline:before{content:"\FC59"}.mdi-file-send:before{content:"\F22A"}.mdi-file-table:before{content:"\FC5A"}.mdi-file-table-outline:before{content:"\FC5B"}.mdi-file-tree:before{content:"\F645"}.mdi-file-undo:before{content:"\F8DB"}.mdi-file-upload:before{content:"\FA4C"}.mdi-file-upload-outline:before{content:"\FA4D"}.mdi-file-video:before{content:"\F22B"}.mdi-file-word:before{content:"\F22C"}.mdi-file-word-box:before{content:"\F22D"}.mdi-file-xml:before{content:"\F22E"}.mdi-film:before{content:"\F22F"}.mdi-filmstrip:before{content:"\F230"}.mdi-filmstrip-off:before{content:"\F231"}.mdi-filter:before{content:"\F232"}.mdi-filter-outline:before{content:"\F233"}.mdi-filter-remove:before{content:"\F234"}.mdi-filter-remove-outline:before{content:"\F235"}.mdi-filter-variant:before{content:"\F236"}.mdi-finance:before{content:"\F81E"}.mdi-find-replace:before{content:"\F6D3"}.mdi-fingerprint:before{content:"\F237"}.mdi-fire:before{content:"\F238"}.mdi-fire-truck:before{content:"\F8AA"}.mdi-firebase:before{content:"\F966"}.mdi-firefox:before{content:"\F239"}.mdi-fish:before{content:"\F23A"}.mdi-flag:before{content:"\F23B"}.mdi-flag-checkered:before{content:"\F23C"}.mdi-flag-minus:before{content:"\FB75"}.mdi-flag-outline:before{content:"\F23D"}.mdi-flag-plus:before{content:"\FB76"}.mdi-flag-remove:before{content:"\FB77"}.mdi-flag-triangle:before{content:"\F23F"}.mdi-flag-variant:before{content:"\F240"}.mdi-flag-variant-outline:before{content:"\F23E"}.mdi-flare:before{content:"\FD4E"}.mdi-flash:before{content:"\F241"}.mdi-flash-auto:before{content:"\F242"}.mdi-flash-circle:before{content:"\F81F"}.mdi-flash-off:before{content:"\F243"}.mdi-flash-outline:before{content:"\F6D4"}.mdi-flash-red-eye:before{content:"\F67A"}.mdi-flashlight:before{content:"\F244"}.mdi-flashlight-off:before{content:"\F245"}.mdi-flask:before{content:"\F093"}.mdi-flask-empty:before{content:"\F094"}.mdi-flask-empty-outline:before{content:"\F095"}.mdi-flask-outline:before{content:"\F096"}.mdi-flattr:before{content:"\F246"}.mdi-flickr:before{content:"\FCE3"}.mdi-flip-to-back:before{content:"\F247"}.mdi-flip-to-front:before{content:"\F248"}.mdi-floor-lamp:before{content:"\F8DC"}.mdi-floor-plan:before{content:"\F820"}.mdi-floppy:before{content:"\F249"}.mdi-floppy-variant:before{content:"\F9EE"}.mdi-flower:before{content:"\F24A"}.mdi-flower-outline:before{content:"\F9EF"}.mdi-flower-poppy:before{content:"\FCE4"}.mdi-flower-tulip:before{content:"\F9F0"}.mdi-flower-tulip-outline:before{content:"\F9F1"}.mdi-folder:before{content:"\F24B"}.mdi-folder-account:before{content:"\F24C"}.mdi-folder-account-outline:before{content:"\FB78"}.mdi-folder-alert:before{content:"\FDA8"}.mdi-folder-alert-outline:before{content:"\FDA9"}.mdi-folder-clock:before{content:"\FAB9"}.mdi-folder-clock-outline:before{content:"\FABA"}.mdi-folder-download:before{content:"\F24D"}.mdi-folder-edit:before{content:"\F8DD"}.mdi-folder-edit-outline:before{content:"\FDAA"}.mdi-folder-google-drive:before{content:"\F24E"}.mdi-folder-image:before{content:"\F24F"}.mdi-folder-key:before{content:"\F8AB"}.mdi-folder-key-network:before{content:"\F8AC"}.mdi-folder-key-network-outline:before{content:"\FC5C"}.mdi-folder-lock:before{content:"\F250"}.mdi-folder-lock-open:before{content:"\F251"}.mdi-folder-move:before{content:"\F252"}.mdi-folder-multiple:before{content:"\F253"}.mdi-folder-multiple-image:before{content:"\F254"}.mdi-folder-multiple-outline:before{content:"\F255"}.mdi-folder-network:before{content:"\F86F"}.mdi-folder-network-outline:before{content:"\FC5D"}.mdi-folder-open:before{content:"\F76F"}.mdi-folder-open-outline:before{content:"\FDAB"}.mdi-folder-outline:before{content:"\F256"}.mdi-folder-plus:before{content:"\F257"}.mdi-folder-plus-outline:before{content:"\FB79"}.mdi-folder-pound:before{content:"\FCE5"}.mdi-folder-pound-outline:before{content:"\FCE6"}.mdi-folder-remove:before{content:"\F258"}.mdi-folder-remove-outline:before{content:"\FB7A"}.mdi-folder-search:before{content:"\F967"}.mdi-folder-search-outline:before{content:"\F968"}.mdi-folder-star:before{content:"\F69C"}.mdi-folder-star-outline:before{content:"\FB7B"}.mdi-folder-sync:before{content:"\FCE7"}.mdi-folder-sync-outline:before{content:"\FCE8"}.mdi-folder-text:before{content:"\FC5E"}.mdi-folder-text-outline:before{content:"\FC5F"}.mdi-folder-upload:before{content:"\F259"}.mdi-font-awesome:before{content:"\F03A"}.mdi-food:before{content:"\F25A"}.mdi-food-apple:before{content:"\F25B"}.mdi-food-apple-outline:before{content:"\FC60"}.mdi-food-croissant:before{content:"\F7C7"}.mdi-food-fork-drink:before{content:"\F5F2"}.mdi-food-off:before{content:"\F5F3"}.mdi-food-variant:before{content:"\F25C"}.mdi-football:before{content:"\F25D"}.mdi-football-australian:before{content:"\F25E"}.mdi-football-helmet:before{content:"\F25F"}.mdi-forklift:before{content:"\F7C8"}.mdi-format-align-bottom:before{content:"\F752"}.mdi-format-align-center:before{content:"\F260"}.mdi-format-align-justify:before{content:"\F261"}.mdi-format-align-left:before{content:"\F262"}.mdi-format-align-middle:before{content:"\F753"}.mdi-format-align-right:before{content:"\F263"}.mdi-format-align-top:before{content:"\F754"}.mdi-format-annotation-minus:before{content:"\FABB"}.mdi-format-annotation-plus:before{content:"\F646"}.mdi-format-bold:before{content:"\F264"}.mdi-format-clear:before{content:"\F265"}.mdi-format-color-fill:before{content:"\F266"}.mdi-format-color-text:before{content:"\F69D"}.mdi-format-columns:before{content:"\F8DE"}.mdi-format-float-center:before{content:"\F267"}.mdi-format-float-left:before{content:"\F268"}.mdi-format-float-none:before{content:"\F269"}.mdi-format-float-right:before{content:"\F26A"}.mdi-format-font:before{content:"\F6D5"}.mdi-format-font-size-decrease:before{content:"\F9F2"}.mdi-format-font-size-increase:before{content:"\F9F3"}.mdi-format-header-1:before{content:"\F26B"}.mdi-format-header-2:before{content:"\F26C"}.mdi-format-header-3:before{content:"\F26D"}.mdi-format-header-4:before{content:"\F26E"}.mdi-format-header-5:before{content:"\F26F"}.mdi-format-header-6:before{content:"\F270"}.mdi-format-header-decrease:before{content:"\F271"}.mdi-format-header-equal:before{content:"\F272"}.mdi-format-header-increase:before{content:"\F273"}.mdi-format-header-pound:before{content:"\F274"}.mdi-format-horizontal-align-center:before{content:"\F61E"}.mdi-format-horizontal-align-left:before{content:"\F61F"}.mdi-format-horizontal-align-right:before{content:"\F620"}.mdi-format-indent-decrease:before{content:"\F275"}.mdi-format-indent-increase:before{content:"\F276"}.mdi-format-italic:before{content:"\F277"}.mdi-format-letter-case:before{content:"\FB19"}.mdi-format-letter-case-lower:before{content:"\FB1A"}.mdi-format-letter-case-upper:before{content:"\FB1B"}.mdi-format-line-spacing:before{content:"\F278"}.mdi-format-line-style:before{content:"\F5C8"}.mdi-format-line-weight:before{content:"\F5C9"}.mdi-format-list-bulleted:before{content:"\F279"}.mdi-format-list-bulleted-square:before{content:"\FDAC"}.mdi-format-list-bulleted-type:before{content:"\F27A"}.mdi-format-list-checkbox:before{content:"\F969"}.mdi-format-list-checks:before{content:"\F755"}.mdi-format-list-numbered:before{content:"\F27B"}.mdi-format-list-numbered-rtl:before{content:"\FCE9"}.mdi-format-page-break:before{content:"\F6D6"}.mdi-format-paint:before{content:"\F27C"}.mdi-format-paragraph:before{content:"\F27D"}.mdi-format-pilcrow:before{content:"\F6D7"}.mdi-format-quote-close:before{content:"\F27E"}.mdi-format-quote-open:before{content:"\F756"}.mdi-format-rotate-90:before{content:"\F6A9"}.mdi-format-section:before{content:"\F69E"}.mdi-format-size:before{content:"\F27F"}.mdi-format-strikethrough:before{content:"\F280"}.mdi-format-strikethrough-variant:before{content:"\F281"}.mdi-format-subscript:before{content:"\F282"}.mdi-format-superscript:before{content:"\F283"}.mdi-format-text:before{content:"\F284"}.mdi-format-text-rotation-down:before{content:"\FD4F"}.mdi-format-text-rotation-none:before{content:"\FD50"}.mdi-format-text-wrapping-clip:before{content:"\FCEA"}.mdi-format-text-wrapping-overflow:before{content:"\FCEB"}.mdi-format-text-wrapping-wrap:before{content:"\FCEC"}.mdi-format-textbox:before{content:"\FCED"}.mdi-format-textdirection-l-to-r:before{content:"\F285"}.mdi-format-textdirection-r-to-l:before{content:"\F286"}.mdi-format-title:before{content:"\F5F4"}.mdi-format-underline:before{content:"\F287"}.mdi-format-vertical-align-bottom:before{content:"\F621"}.mdi-format-vertical-align-center:before{content:"\F622"}.mdi-format-vertical-align-top:before{content:"\F623"}.mdi-format-wrap-inline:before{content:"\F288"}.mdi-format-wrap-square:before{content:"\F289"}.mdi-format-wrap-tight:before{content:"\F28A"}.mdi-format-wrap-top-bottom:before{content:"\F28B"}.mdi-forum:before{content:"\F28C"}.mdi-forum-outline:before{content:"\F821"}.mdi-forward:before{content:"\F28D"}.mdi-forwardburger:before{content:"\FD51"}.mdi-fountain:before{content:"\F96A"}.mdi-fountain-pen:before{content:"\FCEE"}.mdi-fountain-pen-tip:before{content:"\FCEF"}.mdi-foursquare:before{content:"\F28E"}.mdi-freebsd:before{content:"\F8DF"}.mdi-fridge:before{content:"\F290"}.mdi-fridge-bottom:before{content:"\F292"}.mdi-fridge-outline:before{content:"\F28F"}.mdi-fridge-top:before{content:"\F291"}.mdi-fuel:before{content:"\F7C9"}.mdi-fullscreen:before{content:"\F293"}.mdi-fullscreen-exit:before{content:"\F294"}.mdi-function:before{content:"\F295"}.mdi-function-variant:before{content:"\F870"}.mdi-fuse:before{content:"\FC61"}.mdi-fuse-blade:before{content:"\FC62"}.mdi-gamepad:before{content:"\F296"}.mdi-gamepad-variant:before{content:"\F297"}.mdi-gantry-crane:before{content:"\FDAD"}.mdi-garage:before{content:"\F6D8"}.mdi-garage-alert:before{content:"\F871"}.mdi-garage-open:before{content:"\F6D9"}.mdi-gas-cylinder:before{content:"\F647"}.mdi-gas-station:before{content:"\F298"}.mdi-gate:before{content:"\F299"}.mdi-gate-and:before{content:"\F8E0"}.mdi-gate-nand:before{content:"\F8E1"}.mdi-gate-nor:before{content:"\F8E2"}.mdi-gate-not:before{content:"\F8E3"}.mdi-gate-or:before{content:"\F8E4"}.mdi-gate-xnor:before{content:"\F8E5"}.mdi-gate-xor:before{content:"\F8E6"}.mdi-gauge:before{content:"\F29A"}.mdi-gauge-empty:before{content:"\F872"}.mdi-gauge-full:before{content:"\F873"}.mdi-gauge-low:before{content:"\F874"}.mdi-gavel:before{content:"\F29B"}.mdi-gender-female:before{content:"\F29C"}.mdi-gender-male:before{content:"\F29D"}.mdi-gender-male-female:before{content:"\F29E"}.mdi-gender-transgender:before{content:"\F29F"}.mdi-gentoo:before{content:"\F8E7"}.mdi-gesture:before{content:"\F7CA"}.mdi-gesture-double-tap:before{content:"\F73B"}.mdi-gesture-pinch:before{content:"\FABC"}.mdi-gesture-spread:before{content:"\FABD"}.mdi-gesture-swipe:before{content:"\FD52"}.mdi-gesture-swipe-down:before{content:"\F73C"}.mdi-gesture-swipe-horizontal:before{content:"\FABE"}.mdi-gesture-swipe-left:before{content:"\F73D"}.mdi-gesture-swipe-right:before{content:"\F73E"}.mdi-gesture-swipe-up:before{content:"\F73F"}.mdi-gesture-swipe-vertical:before{content:"\FABF"}.mdi-gesture-tap:before{content:"\F740"}.mdi-gesture-tap-hold:before{content:"\FD53"}.mdi-gesture-two-double-tap:before{content:"\F741"}.mdi-gesture-two-tap:before{content:"\F742"}.mdi-ghost:before{content:"\F2A0"}.mdi-ghost-off:before{content:"\F9F4"}.mdi-gif:before{content:"\FD54"}.mdi-gift:before{content:"\F2A1"}.mdi-git:before{content:"\F2A2"}.mdi-github-box:before{content:"\F2A3"}.mdi-github-circle:before{content:"\F2A4"}.mdi-github-face:before{content:"\F6DA"}.mdi-gitlab:before{content:"\FB7C"}.mdi-glass-cocktail:before{content:"\F356"}.mdi-glass-flute:before{content:"\F2A5"}.mdi-glass-mug:before{content:"\F2A6"}.mdi-glass-stange:before{content:"\F2A7"}.mdi-glass-tulip:before{content:"\F2A8"}.mdi-glass-wine:before{content:"\F875"}.mdi-glassdoor:before{content:"\F2A9"}.mdi-glasses:before{content:"\F2AA"}.mdi-globe-model:before{content:"\F8E8"}.mdi-gmail:before{content:"\F2AB"}.mdi-gnome:before{content:"\F2AC"}.mdi-go-kart:before{content:"\FD55"}.mdi-go-kart-track:before{content:"\FD56"}.mdi-gog:before{content:"\FB7D"}.mdi-golf:before{content:"\F822"}.mdi-gondola:before{content:"\F685"}.mdi-goodreads:before{content:"\FD57"}.mdi-google:before{content:"\F2AD"}.mdi-google-adwords:before{content:"\FC63"}.mdi-google-allo:before{content:"\F801"}.mdi-google-analytics:before{content:"\F7CB"}.mdi-google-assistant:before{content:"\F7CC"}.mdi-google-cardboard:before{content:"\F2AE"}.mdi-google-chrome:before{content:"\F2AF"}.mdi-google-circles:before{content:"\F2B0"}.mdi-google-circles-communities:before{content:"\F2B1"}.mdi-google-circles-extended:before{content:"\F2B2"}.mdi-google-circles-group:before{content:"\F2B3"}.mdi-google-classroom:before{content:"\F2C0"}.mdi-google-controller:before{content:"\F2B4"}.mdi-google-controller-off:before{content:"\F2B5"}.mdi-google-drive:before{content:"\F2B6"}.mdi-google-earth:before{content:"\F2B7"}.mdi-google-fit:before{content:"\F96B"}.mdi-google-glass:before{content:"\F2B8"}.mdi-google-hangouts:before{content:"\F2C9"}.mdi-google-home:before{content:"\F823"}.mdi-google-keep:before{content:"\F6DB"}.mdi-google-lens:before{content:"\F9F5"}.mdi-google-maps:before{content:"\F5F5"}.mdi-google-nearby:before{content:"\F2B9"}.mdi-google-pages:before{content:"\F2BA"}.mdi-google-photos:before{content:"\F6DC"}.mdi-google-physical-web:before{content:"\F2BB"}.mdi-google-play:before{content:"\F2BC"}.mdi-google-plus:before{content:"\F2BD"}.mdi-google-plus-box:before{content:"\F2BE"}.mdi-google-spreadsheet:before{content:"\F9F6"}.mdi-google-street-view:before{content:"\FC64"}.mdi-google-translate:before{content:"\F2BF"}.mdi-gpu:before{content:"\F8AD"}.mdi-gradient:before{content:"\F69F"}.mdi-grain:before{content:"\FD58"}.mdi-graphql:before{content:"\F876"}.mdi-grave-stone:before{content:"\FB7E"}.mdi-grease-pencil:before{content:"\F648"}.mdi-greater-than:before{content:"\F96C"}.mdi-greater-than-or-equal:before{content:"\F96D"}.mdi-grid:before{content:"\F2C1"}.mdi-grid-large:before{content:"\F757"}.mdi-grid-off:before{content:"\F2C2"}.mdi-group:before{content:"\F2C3"}.mdi-guitar-acoustic:before{content:"\F770"}.mdi-guitar-electric:before{content:"\F2C4"}.mdi-guitar-pick:before{content:"\F2C5"}.mdi-guitar-pick-outline:before{content:"\F2C6"}.mdi-guy-fawkes-mask:before{content:"\F824"}.mdi-hackernews:before{content:"\F624"}.mdi-hail:before{content:"\FAC0"}.mdi-halloween:before{content:"\FB7F"}.mdi-hamburger:before{content:"\F684"}.mdi-hammer:before{content:"\F8E9"}.mdi-hand:before{content:"\FA4E"}.mdi-hand-okay:before{content:"\FA4F"}.mdi-hand-peace:before{content:"\FA50"}.mdi-hand-peace-variant:before{content:"\FA51"}.mdi-hand-pointing-down:before{content:"\FA52"}.mdi-hand-pointing-left:before{content:"\FA53"}.mdi-hand-pointing-right:before{content:"\F2C7"}.mdi-hand-pointing-up:before{content:"\FA54"}.mdi-hanger:before{content:"\F2C8"}.mdi-hard-hat:before{content:"\F96E"}.mdi-harddisk:before{content:"\F2CA"}.mdi-hat-fedora:before{content:"\FB80"}.mdi-hazard-lights:before{content:"\FC65"}.mdi-hdr:before{content:"\FD59"}.mdi-hdr-off:before{content:"\FD5A"}.mdi-headphones:before{content:"\F2CB"}.mdi-headphones-bluetooth:before{content:"\F96F"}.mdi-headphones-box:before{content:"\F2CC"}.mdi-headphones-off:before{content:"\F7CD"}.mdi-headphones-settings:before{content:"\F2CD"}.mdi-headset:before{content:"\F2CE"}.mdi-headset-dock:before{content:"\F2CF"}.mdi-headset-off:before{content:"\F2D0"}.mdi-heart:before{content:"\F2D1"}.mdi-heart-box:before{content:"\F2D2"}.mdi-heart-box-outline:before{content:"\F2D3"}.mdi-heart-broken:before{content:"\F2D4"}.mdi-heart-broken-outline:before{content:"\FCF0"}.mdi-heart-circle:before{content:"\F970"}.mdi-heart-circle-outline:before{content:"\F971"}.mdi-heart-half:before{content:"\F6DE"}.mdi-heart-half-full:before{content:"\F6DD"}.mdi-heart-half-outline:before{content:"\F6DF"}.mdi-heart-multiple:before{content:"\FA55"}.mdi-heart-multiple-outline:before{content:"\FA56"}.mdi-heart-off:before{content:"\F758"}.mdi-heart-outline:before{content:"\F2D5"}.mdi-heart-pulse:before{content:"\F5F6"}.mdi-helicopter:before{content:"\FAC1"}.mdi-help:before{content:"\F2D6"}.mdi-help-box:before{content:"\F78A"}.mdi-help-circle:before{content:"\F2D7"}.mdi-help-circle-outline:before{content:"\F625"}.mdi-help-network:before{content:"\F6F4"}.mdi-help-network-outline:before{content:"\FC66"}.mdi-help-rhombus:before{content:"\FB81"}.mdi-help-rhombus-outline:before{content:"\FB82"}.mdi-hexagon:before{content:"\F2D8"}.mdi-hexagon-multiple:before{content:"\F6E0"}.mdi-hexagon-outline:before{content:"\F2D9"}.mdi-hexagon-slice-1:before{content:"\FAC2"}.mdi-hexagon-slice-2:before{content:"\FAC3"}.mdi-hexagon-slice-3:before{content:"\FAC4"}.mdi-hexagon-slice-4:before{content:"\FAC5"}.mdi-hexagon-slice-5:before{content:"\FAC6"}.mdi-hexagon-slice-6:before{content:"\FAC7"}.mdi-hexagram:before{content:"\FAC8"}.mdi-hexagram-outline:before{content:"\FAC9"}.mdi-high-definition:before{content:"\F7CE"}.mdi-high-definition-box:before{content:"\F877"}.mdi-highway:before{content:"\F5F7"}.mdi-hiking:before{content:"\FD5B"}.mdi-hinduism:before{content:"\F972"}.mdi-history:before{content:"\F2DA"}.mdi-hockey-puck:before{content:"\F878"}.mdi-hockey-sticks:before{content:"\F879"}.mdi-hololens:before{content:"\F2DB"}.mdi-home:before{content:"\F2DC"}.mdi-home-account:before{content:"\F825"}.mdi-home-alert:before{content:"\F87A"}.mdi-home-assistant:before{content:"\F7CF"}.mdi-home-automation:before{content:"\F7D0"}.mdi-home-circle:before{content:"\F7D1"}.mdi-home-city:before{content:"\FCF1"}.mdi-home-city-outline:before{content:"\FCF2"}.mdi-home-currency-usd:before{content:"\F8AE"}.mdi-home-floor-0:before{content:"\FDAE"}.mdi-home-floor-1:before{content:"\FD5C"}.mdi-home-floor-2:before{content:"\FD5D"}.mdi-home-floor-3:before{content:"\FD5E"}.mdi-home-floor-a:before{content:"\FD5F"}.mdi-home-floor-b:before{content:"\FD60"}.mdi-home-floor-g:before{content:"\FD61"}.mdi-home-floor-l:before{content:"\FD62"}.mdi-home-floor-negative-1:before{content:"\FDAF"}.mdi-home-group:before{content:"\FDB0"}.mdi-home-heart:before{content:"\F826"}.mdi-home-lock:before{content:"\F8EA"}.mdi-home-lock-open:before{content:"\F8EB"}.mdi-home-map-marker:before{content:"\F5F8"}.mdi-home-minus:before{content:"\F973"}.mdi-home-modern:before{content:"\F2DD"}.mdi-home-outline:before{content:"\F6A0"}.mdi-home-plus:before{content:"\F974"}.mdi-home-variant:before{content:"\F2DE"}.mdi-home-variant-outline:before{content:"\FB83"}.mdi-hook:before{content:"\F6E1"}.mdi-hook-off:before{content:"\F6E2"}.mdi-hops:before{content:"\F2DF"}.mdi-horseshoe:before{content:"\FA57"}.mdi-hospital:before{content:"\F2E0"}.mdi-hospital-building:before{content:"\F2E1"}.mdi-hospital-marker:before{content:"\F2E2"}.mdi-hot-tub:before{content:"\F827"}.mdi-hotel:before{content:"\F2E3"}.mdi-houzz:before{content:"\F2E4"}.mdi-houzz-box:before{content:"\F2E5"}.mdi-hubspot:before{content:"\FCF3"}.mdi-hulu:before{content:"\F828"}.mdi-human:before{content:"\F2E6"}.mdi-human-child:before{content:"\F2E7"}.mdi-human-female:before{content:"\F649"}.mdi-human-female-boy:before{content:"\FA58"}.mdi-human-female-female:before{content:"\FA59"}.mdi-human-female-girl:before{content:"\FA5A"}.mdi-human-greeting:before{content:"\F64A"}.mdi-human-handsdown:before{content:"\F64B"}.mdi-human-handsup:before{content:"\F64C"}.mdi-human-male:before{content:"\F64D"}.mdi-human-male-boy:before{content:"\FA5B"}.mdi-human-male-female:before{content:"\F2E8"}.mdi-human-male-girl:before{content:"\FA5C"}.mdi-human-male-male:before{content:"\FA5D"}.mdi-human-pregnant:before{content:"\F5CF"}.mdi-humble-bundle:before{content:"\F743"}.mdi-ice-cream:before{content:"\F829"}.mdi-iframe:before{content:"\FC67"}.mdi-iframe-outline:before{content:"\FC68"}.mdi-image:before{content:"\F2E9"}.mdi-image-album:before{content:"\F2EA"}.mdi-image-area:before{content:"\F2EB"}.mdi-image-area-close:before{content:"\F2EC"}.mdi-image-broken:before{content:"\F2ED"}.mdi-image-broken-variant:before{content:"\F2EE"}.mdi-image-filter:before{content:"\F2EF"}.mdi-image-filter-black-white:before{content:"\F2F0"}.mdi-image-filter-center-focus:before{content:"\F2F1"}.mdi-image-filter-center-focus-weak:before{content:"\F2F2"}.mdi-image-filter-drama:before{content:"\F2F3"}.mdi-image-filter-frames:before{content:"\F2F4"}.mdi-image-filter-hdr:before{content:"\F2F5"}.mdi-image-filter-none:before{content:"\F2F6"}.mdi-image-filter-tilt-shift:before{content:"\F2F7"}.mdi-image-filter-vintage:before{content:"\F2F8"}.mdi-image-move:before{content:"\F9F7"}.mdi-image-multiple:before{content:"\F2F9"}.mdi-image-off:before{content:"\F82A"}.mdi-image-outline:before{content:"\F975"}.mdi-image-plus:before{content:"\F87B"}.mdi-image-search:before{content:"\F976"}.mdi-image-search-outline:before{content:"\F977"}.mdi-image-size-select-actual:before{content:"\FC69"}.mdi-image-size-select-large:before{content:"\FC6A"}.mdi-image-size-select-small:before{content:"\FC6B"}.mdi-import:before{content:"\F2FA"}.mdi-inbox:before{content:"\F686"}.mdi-inbox-arrow-down:before{content:"\F2FB"}.mdi-inbox-arrow-up:before{content:"\F3D1"}.mdi-inbox-multiple:before{content:"\F8AF"}.mdi-inbox-multiple-outline:before{content:"\FB84"}.mdi-incognito:before{content:"\F5F9"}.mdi-infinity:before{content:"\F6E3"}.mdi-information:before{content:"\F2FC"}.mdi-information-outline:before{content:"\F2FD"}.mdi-information-variant:before{content:"\F64E"}.mdi-instagram:before{content:"\F2FE"}.mdi-instapaper:before{content:"\F2FF"}.mdi-internet-explorer:before{content:"\F300"}.mdi-invert-colors:before{content:"\F301"}.mdi-ip:before{content:"\FA5E"}.mdi-ip-network:before{content:"\FA5F"}.mdi-ip-network-outline:before{content:"\FC6C"}.mdi-ipod:before{content:"\FC6D"}.mdi-islam:before{content:"\F978"}.mdi-itunes:before{content:"\F676"}.mdi-jabber:before{content:"\FDB1"}.mdi-jeepney:before{content:"\F302"}.mdi-jira:before{content:"\F303"}.mdi-jquery:before{content:"\F87C"}.mdi-jsfiddle:before{content:"\F304"}.mdi-json:before{content:"\F626"}.mdi-judaism:before{content:"\F979"}.mdi-kabaddi:before{content:"\FD63"}.mdi-karate:before{content:"\F82B"}.mdi-keg:before{content:"\F305"}.mdi-kettle:before{content:"\F5FA"}.mdi-key:before{content:"\F306"}.mdi-key-change:before{content:"\F307"}.mdi-key-minus:before{content:"\F308"}.mdi-key-outline:before{content:"\FDB2"}.mdi-key-plus:before{content:"\F309"}.mdi-key-remove:before{content:"\F30A"}.mdi-key-variant:before{content:"\F30B"}.mdi-keyboard:before{content:"\F30C"}.mdi-keyboard-backspace:before{content:"\F30D"}.mdi-keyboard-caps:before{content:"\F30E"}.mdi-keyboard-close:before{content:"\F30F"}.mdi-keyboard-off:before{content:"\F310"}.mdi-keyboard-outline:before{content:"\F97A"}.mdi-keyboard-return:before{content:"\F311"}.mdi-keyboard-settings:before{content:"\F9F8"}.mdi-keyboard-settings-outline:before{content:"\F9F9"}.mdi-keyboard-tab:before{content:"\F312"}.mdi-keyboard-variant:before{content:"\F313"}.mdi-kickstarter:before{content:"\F744"}.mdi-knife:before{content:"\F9FA"}.mdi-knife-military:before{content:"\F9FB"}.mdi-kodi:before{content:"\F314"}.mdi-label:before{content:"\F315"}.mdi-label-off:before{content:"\FACA"}.mdi-label-off-outline:before{content:"\FACB"}.mdi-label-outline:before{content:"\F316"}.mdi-label-variant:before{content:"\FACC"}.mdi-label-variant-outline:before{content:"\FACD"}.mdi-ladybug:before{content:"\F82C"}.mdi-lambda:before{content:"\F627"}.mdi-lamp:before{content:"\F6B4"}.mdi-lan:before{content:"\F317"}.mdi-lan-connect:before{content:"\F318"}.mdi-lan-disconnect:before{content:"\F319"}.mdi-lan-pending:before{content:"\F31A"}.mdi-language-c:before{content:"\F671"}.mdi-language-cpp:before{content:"\F672"}.mdi-language-csharp:before{content:"\F31B"}.mdi-language-css3:before{content:"\F31C"}.mdi-language-go:before{content:"\F7D2"}.mdi-language-haskell:before{content:"\FC6E"}.mdi-language-html5:before{content:"\F31D"}.mdi-language-java:before{content:"\FB1C"}.mdi-language-javascript:before{content:"\F31E"}.mdi-language-lua:before{content:"\F8B0"}.mdi-language-php:before{content:"\F31F"}.mdi-language-python:before{content:"\F320"}.mdi-language-python-text:before{content:"\F321"}.mdi-language-r:before{content:"\F7D3"}.mdi-language-ruby-on-rails:before{content:"\FACE"}.mdi-language-swift:before{content:"\F6E4"}.mdi-language-typescript:before{content:"\F6E5"}.mdi-laptop:before{content:"\F322"}.mdi-laptop-chromebook:before{content:"\F323"}.mdi-laptop-mac:before{content:"\F324"}.mdi-laptop-off:before{content:"\F6E6"}.mdi-laptop-windows:before{content:"\F325"}.mdi-laravel:before{content:"\FACF"}.mdi-lastfm:before{content:"\F326"}.mdi-lastpass:before{content:"\F446"}.mdi-launch:before{content:"\F327"}.mdi-lava-lamp:before{content:"\F7D4"}.mdi-layers:before{content:"\F328"}.mdi-layers-off:before{content:"\F329"}.mdi-layers-off-outline:before{content:"\F9FC"}.mdi-layers-outline:before{content:"\F9FD"}.mdi-lead-pencil:before{content:"\F64F"}.mdi-leaf:before{content:"\F32A"}.mdi-leaf-maple:before{content:"\FC6F"}.mdi-leak:before{content:"\FDB3"}.mdi-leak-off:before{content:"\FDB4"}.mdi-led-off:before{content:"\F32B"}.mdi-led-on:before{content:"\F32C"}.mdi-led-outline:before{content:"\F32D"}.mdi-led-strip:before{content:"\F7D5"}.mdi-led-variant-off:before{content:"\F32E"}.mdi-led-variant-on:before{content:"\F32F"}.mdi-led-variant-outline:before{content:"\F330"}.mdi-less-than:before{content:"\F97B"}.mdi-less-than-or-equal:before{content:"\F97C"}.mdi-library:before{content:"\F331"}.mdi-library-books:before{content:"\F332"}.mdi-library-movie:before{content:"\FCF4"}.mdi-library-music:before{content:"\F333"}.mdi-library-plus:before{content:"\F334"}.mdi-library-shelves:before{content:"\FB85"}.mdi-library-video:before{content:"\FCF5"}.mdi-lifebuoy:before{content:"\F87D"}.mdi-light-switch:before{content:"\F97D"}.mdi-lightbulb:before{content:"\F335"}.mdi-lightbulb-on:before{content:"\F6E7"}.mdi-lightbulb-on-outline:before{content:"\F6E8"}.mdi-lightbulb-outline:before{content:"\F336"}.mdi-lighthouse:before{content:"\F9FE"}.mdi-lighthouse-on:before{content:"\F9FF"}.mdi-link:before{content:"\F337"}.mdi-link-box:before{content:"\FCF6"}.mdi-link-box-outline:before{content:"\FCF7"}.mdi-link-box-variant:before{content:"\FCF8"}.mdi-link-box-variant-outline:before{content:"\FCF9"}.mdi-link-off:before{content:"\F338"}.mdi-link-plus:before{content:"\FC70"}.mdi-link-variant:before{content:"\F339"}.mdi-link-variant-off:before{content:"\F33A"}.mdi-linkedin:before{content:"\F33B"}.mdi-linkedin-box:before{content:"\F33C"}.mdi-linux:before{content:"\F33D"}.mdi-linux-mint:before{content:"\F8EC"}.mdi-litecoin:before{content:"\FA60"}.mdi-loading:before{content:"\F771"}.mdi-lock:before{content:"\F33E"}.mdi-lock-alert:before{content:"\F8ED"}.mdi-lock-clock:before{content:"\F97E"}.mdi-lock-open:before{content:"\F33F"}.mdi-lock-open-outline:before{content:"\F340"}.mdi-lock-outline:before{content:"\F341"}.mdi-lock-pattern:before{content:"\F6E9"}.mdi-lock-plus:before{content:"\F5FB"}.mdi-lock-question:before{content:"\F8EE"}.mdi-lock-reset:before{content:"\F772"}.mdi-lock-smart:before{content:"\F8B1"}.mdi-locker:before{content:"\F7D6"}.mdi-locker-multiple:before{content:"\F7D7"}.mdi-login:before{content:"\F342"}.mdi-login-variant:before{content:"\F5FC"}.mdi-logout:before{content:"\F343"}.mdi-logout-variant:before{content:"\F5FD"}.mdi-looks:before{content:"\F344"}.mdi-loop:before{content:"\F6EA"}.mdi-loupe:before{content:"\F345"}.mdi-lumx:before{content:"\F346"}.mdi-lyft:before{content:"\FB1D"}.mdi-magnet:before{content:"\F347"}.mdi-magnet-on:before{content:"\F348"}.mdi-magnify:before{content:"\F349"}.mdi-magnify-close:before{content:"\F97F"}.mdi-magnify-minus:before{content:"\F34A"}.mdi-magnify-minus-cursor:before{content:"\FA61"}.mdi-magnify-minus-outline:before{content:"\F6EB"}.mdi-magnify-plus:before{content:"\F34B"}.mdi-magnify-plus-cursor:before{content:"\FA62"}.mdi-magnify-plus-outline:before{content:"\F6EC"}.mdi-mail-ru:before{content:"\F34C"}.mdi-mailbox:before{content:"\F6ED"}.mdi-mailbox-open:before{content:"\FD64"}.mdi-mailbox-open-outline:before{content:"\FD65"}.mdi-mailbox-open-up:before{content:"\FD66"}.mdi-mailbox-open-up-outline:before{content:"\FD67"}.mdi-mailbox-outline:before{content:"\FD68"}.mdi-mailbox-up:before{content:"\FD69"}.mdi-mailbox-up-outline:before{content:"\FD6A"}.mdi-map:before{content:"\F34D"}.mdi-map-clock:before{content:"\FCFA"}.mdi-map-clock-outline:before{content:"\FCFB"}.mdi-map-legend:before{content:"\FA00"}.mdi-map-marker:before{content:"\F34E"}.mdi-map-marker-check:before{content:"\FC71"}.mdi-map-marker-circle:before{content:"\F34F"}.mdi-map-marker-distance:before{content:"\F8EF"}.mdi-map-marker-minus:before{content:"\F650"}.mdi-map-marker-multiple:before{content:"\F350"}.mdi-map-marker-off:before{content:"\F351"}.mdi-map-marker-outline:before{content:"\F7D8"}.mdi-map-marker-path:before{content:"\FCFC"}.mdi-map-marker-plus:before{content:"\F651"}.mdi-map-marker-radius:before{content:"\F352"}.mdi-map-minus:before{content:"\F980"}.mdi-map-outline:before{content:"\F981"}.mdi-map-plus:before{content:"\F982"}.mdi-map-search:before{content:"\F983"}.mdi-map-search-outline:before{content:"\F984"}.mdi-mapbox:before{content:"\FB86"}.mdi-margin:before{content:"\F353"}.mdi-markdown:before{content:"\F354"}.mdi-marker:before{content:"\F652"}.mdi-marker-cancel:before{content:"\FDB5"}.mdi-marker-check:before{content:"\F355"}.mdi-mastodon:before{content:"\FAD0"}.mdi-mastodon-variant:before{content:"\FAD1"}.mdi-material-design:before{content:"\F985"}.mdi-material-ui:before{content:"\F357"}.mdi-math-compass:before{content:"\F358"}.mdi-math-cos:before{content:"\FC72"}.mdi-math-sin:before{content:"\FC73"}.mdi-math-tan:before{content:"\FC74"}.mdi-matrix:before{content:"\F628"}.mdi-maxcdn:before{content:"\F359"}.mdi-medal:before{content:"\F986"}.mdi-medical-bag:before{content:"\F6EE"}.mdi-medium:before{content:"\F35A"}.mdi-meetup:before{content:"\FAD2"}.mdi-memory:before{content:"\F35B"}.mdi-menu:before{content:"\F35C"}.mdi-menu-down:before{content:"\F35D"}.mdi-menu-down-outline:before{content:"\F6B5"}.mdi-menu-left:before{content:"\F35E"}.mdi-menu-left-outline:before{content:"\FA01"}.mdi-menu-open:before{content:"\FB87"}.mdi-menu-right:before{content:"\F35F"}.mdi-menu-right-outline:before{content:"\FA02"}.mdi-menu-swap:before{content:"\FA63"}.mdi-menu-swap-outline:before{content:"\FA64"}.mdi-menu-up:before{content:"\F360"}.mdi-menu-up-outline:before{content:"\F6B6"}.mdi-message:before{content:"\F361"}.mdi-message-alert:before{content:"\F362"}.mdi-message-alert-outline:before{content:"\FA03"}.mdi-message-bulleted:before{content:"\F6A1"}.mdi-message-bulleted-off:before{content:"\F6A2"}.mdi-message-draw:before{content:"\F363"}.mdi-message-image:before{content:"\F364"}.mdi-message-outline:before{content:"\F365"}.mdi-message-plus:before{content:"\F653"}.mdi-message-processing:before{content:"\F366"}.mdi-message-reply:before{content:"\F367"}.mdi-message-reply-text:before{content:"\F368"}.mdi-message-settings:before{content:"\F6EF"}.mdi-message-settings-variant:before{content:"\F6F0"}.mdi-message-text:before{content:"\F369"}.mdi-message-text-outline:before{content:"\F36A"}.mdi-message-video:before{content:"\F36B"}.mdi-meteor:before{content:"\F629"}.mdi-metronome:before{content:"\F7D9"}.mdi-metronome-tick:before{content:"\F7DA"}.mdi-micro-sd:before{content:"\F7DB"}.mdi-microphone:before{content:"\F36C"}.mdi-microphone-minus:before{content:"\F8B2"}.mdi-microphone-off:before{content:"\F36D"}.mdi-microphone-outline:before{content:"\F36E"}.mdi-microphone-plus:before{content:"\F8B3"}.mdi-microphone-settings:before{content:"\F36F"}.mdi-microphone-variant:before{content:"\F370"}.mdi-microphone-variant-off:before{content:"\F371"}.mdi-microscope:before{content:"\F654"}.mdi-microsoft:before{content:"\F372"}.mdi-microsoft-dynamics:before{content:"\F987"}.mdi-microwave:before{content:"\FC75"}.mdi-midi:before{content:"\F8F0"}.mdi-midi-port:before{content:"\F8F1"}.mdi-mine:before{content:"\FDB6"}.mdi-minecraft:before{content:"\F373"}.mdi-mini-sd:before{content:"\FA04"}.mdi-minidisc:before{content:"\FA05"}.mdi-minus:before{content:"\F374"}.mdi-minus-box:before{content:"\F375"}.mdi-minus-box-outline:before{content:"\F6F1"}.mdi-minus-circle:before{content:"\F376"}.mdi-minus-circle-outline:before{content:"\F377"}.mdi-minus-network:before{content:"\F378"}.mdi-minus-network-outline:before{content:"\FC76"}.mdi-mixcloud:before{content:"\F62A"}.mdi-mixed-martial-arts:before{content:"\FD6B"}.mdi-mixed-reality:before{content:"\F87E"}.mdi-mixer:before{content:"\F7DC"}.mdi-molecule:before{content:"\FB88"}.mdi-monitor:before{content:"\F379"}.mdi-monitor-cellphone:before{content:"\F988"}.mdi-monitor-cellphone-star:before{content:"\F989"}.mdi-monitor-dashboard:before{content:"\FA06"}.mdi-monitor-lock:before{content:"\FDB7"}.mdi-monitor-multiple:before{content:"\F37A"}.mdi-monitor-off:before{content:"\FD6C"}.mdi-monitor-star:before{content:"\FDB8"}.mdi-more:before{content:"\F37B"}.mdi-mother-nurse:before{content:"\FCFD"}.mdi-motion-sensor:before{content:"\FD6D"}.mdi-motorbike:before{content:"\F37C"}.mdi-mouse:before{content:"\F37D"}.mdi-mouse-bluetooth:before{content:"\F98A"}.mdi-mouse-off:before{content:"\F37E"}.mdi-mouse-variant:before{content:"\F37F"}.mdi-mouse-variant-off:before{content:"\F380"}.mdi-move-resize:before{content:"\F655"}.mdi-move-resize-variant:before{content:"\F656"}.mdi-movie:before{content:"\F381"}.mdi-movie-outline:before{content:"\FDB9"}.mdi-movie-roll:before{content:"\F7DD"}.mdi-muffin:before{content:"\F98B"}.mdi-multiplication:before{content:"\F382"}.mdi-multiplication-box:before{content:"\F383"}.mdi-mushroom:before{content:"\F7DE"}.mdi-mushroom-outline:before{content:"\F7DF"}.mdi-music:before{content:"\F759"}.mdi-music-box:before{content:"\F384"}.mdi-music-box-outline:before{content:"\F385"}.mdi-music-circle:before{content:"\F386"}.mdi-music-circle-outline:before{content:"\FAD3"}.mdi-music-note:before{content:"\F387"}.mdi-music-note-bluetooth:before{content:"\F5FE"}.mdi-music-note-bluetooth-off:before{content:"\F5FF"}.mdi-music-note-eighth:before{content:"\F388"}.mdi-music-note-half:before{content:"\F389"}.mdi-music-note-off:before{content:"\F38A"}.mdi-music-note-plus:before{content:"\FDBA"}.mdi-music-note-quarter:before{content:"\F38B"}.mdi-music-note-sixteenth:before{content:"\F38C"}.mdi-music-note-whole:before{content:"\F38D"}.mdi-music-off:before{content:"\F75A"}.mdi-nail:before{content:"\FDBB"}.mdi-nas:before{content:"\F8F2"}.mdi-nativescript:before{content:"\F87F"}.mdi-nature:before{content:"\F38E"}.mdi-nature-people:before{content:"\F38F"}.mdi-navigation:before{content:"\F390"}.mdi-near-me:before{content:"\F5CD"}.mdi-needle:before{content:"\F391"}.mdi-netflix:before{content:"\F745"}.mdi-network:before{content:"\F6F2"}.mdi-network-off:before{content:"\FC77"}.mdi-network-off-outline:before{content:"\FC78"}.mdi-network-outline:before{content:"\FC79"}.mdi-network-strength-1:before{content:"\F8F3"}.mdi-network-strength-1-alert:before{content:"\F8F4"}.mdi-network-strength-2:before{content:"\F8F5"}.mdi-network-strength-2-alert:before{content:"\F8F6"}.mdi-network-strength-3:before{content:"\F8F7"}.mdi-network-strength-3-alert:before{content:"\F8F8"}.mdi-network-strength-4:before{content:"\F8F9"}.mdi-network-strength-4-alert:before{content:"\F8FA"}.mdi-network-strength-off:before{content:"\F8FB"}.mdi-network-strength-off-outline:before{content:"\F8FC"}.mdi-network-strength-outline:before{content:"\F8FD"}.mdi-new-box:before{content:"\F394"}.mdi-newspaper:before{content:"\F395"}.mdi-nfc:before{content:"\F396"}.mdi-nfc-tap:before{content:"\F397"}.mdi-nfc-variant:before{content:"\F398"}.mdi-ninja:before{content:"\F773"}.mdi-nintendo-switch:before{content:"\F7E0"}.mdi-nodejs:before{content:"\F399"}.mdi-not-equal:before{content:"\F98C"}.mdi-not-equal-variant:before{content:"\F98D"}.mdi-note:before{content:"\F39A"}.mdi-note-multiple:before{content:"\F6B7"}.mdi-note-multiple-outline:before{content:"\F6B8"}.mdi-note-outline:before{content:"\F39B"}.mdi-note-plus:before{content:"\F39C"}.mdi-note-plus-outline:before{content:"\F39D"}.mdi-note-text:before{content:"\F39E"}.mdi-notebook:before{content:"\F82D"}.mdi-notification-clear-all:before{content:"\F39F"}.mdi-npm:before{content:"\F6F6"}.mdi-npm-variant:before{content:"\F98E"}.mdi-npm-variant-outline:before{content:"\F98F"}.mdi-nuke:before{content:"\F6A3"}.mdi-null:before{content:"\F7E1"}.mdi-numeric:before{content:"\F3A0"}.mdi-numeric-0:before{content:"\30"}.mdi-numeric-0-box:before{content:"\F3A1"}.mdi-numeric-0-box-multiple-outline:before{content:"\F3A2"}.mdi-numeric-0-box-outline:before{content:"\F3A3"}.mdi-numeric-0-circle:before{content:"\FC7A"}.mdi-numeric-0-circle-outline:before{content:"\FC7B"}.mdi-numeric-1:before{content:"\31"}.mdi-numeric-1-box:before{content:"\F3A4"}.mdi-numeric-1-box-multiple-outline:before{content:"\F3A5"}.mdi-numeric-1-box-outline:before{content:"\F3A6"}.mdi-numeric-1-circle:before{content:"\FC7C"}.mdi-numeric-1-circle-outline:before{content:"\FC7D"}.mdi-numeric-2:before{content:"\32"}.mdi-numeric-2-box:before{content:"\F3A7"}.mdi-numeric-2-box-multiple-outline:before{content:"\F3A8"}.mdi-numeric-2-box-outline:before{content:"\F3A9"}.mdi-numeric-2-circle:before{content:"\FC7E"}.mdi-numeric-2-circle-outline:before{content:"\FC7F"}.mdi-numeric-3:before{content:"\33"}.mdi-numeric-3-box:before{content:"\F3AA"}.mdi-numeric-3-box-multiple-outline:before{content:"\F3AB"}.mdi-numeric-3-box-outline:before{content:"\F3AC"}.mdi-numeric-3-circle:before{content:"\FC80"}.mdi-numeric-3-circle-outline:before{content:"\FC81"}.mdi-numeric-4:before{content:"\34"}.mdi-numeric-4-box:before{content:"\F3AD"}.mdi-numeric-4-box-multiple-outline:before{content:"\F3AE"}.mdi-numeric-4-box-outline:before{content:"\F3AF"}.mdi-numeric-4-circle:before{content:"\FC82"}.mdi-numeric-4-circle-outline:before{content:"\FC83"}.mdi-numeric-5:before{content:"\35"}.mdi-numeric-5-box:before{content:"\F3B0"}.mdi-numeric-5-box-multiple-outline:before{content:"\F3B1"}.mdi-numeric-5-box-outline:before{content:"\F3B2"}.mdi-numeric-5-circle:before{content:"\FC84"}.mdi-numeric-5-circle-outline:before{content:"\FC85"}.mdi-numeric-6:before{content:"\36"}.mdi-numeric-6-box:before{content:"\F3B3"}.mdi-numeric-6-box-multiple-outline:before{content:"\F3B4"}.mdi-numeric-6-box-outline:before{content:"\F3B5"}.mdi-numeric-6-circle:before{content:"\FC86"}.mdi-numeric-6-circle-outline:before{content:"\FC87"}.mdi-numeric-7:before{content:"\37"}.mdi-numeric-7-box:before{content:"\F3B6"}.mdi-numeric-7-box-multiple-outline:before{content:"\F3B7"}.mdi-numeric-7-box-outline:before{content:"\F3B8"}.mdi-numeric-7-circle:before{content:"\FC88"}.mdi-numeric-7-circle-outline:before{content:"\FC89"}.mdi-numeric-8:before{content:"\38"}.mdi-numeric-8-box:before{content:"\F3B9"}.mdi-numeric-8-box-multiple-outline:before{content:"\F3BA"}.mdi-numeric-8-box-outline:before{content:"\F3BB"}.mdi-numeric-8-circle:before{content:"\FC8A"}.mdi-numeric-8-circle-outline:before{content:"\FC8B"}.mdi-numeric-9:before{content:"\39"}.mdi-numeric-9-box:before{content:"\F3BC"}.mdi-numeric-9-box-multiple-outline:before{content:"\F3BD"}.mdi-numeric-9-box-outline:before{content:"\F3BE"}.mdi-numeric-9-circle:before{content:"\FC8C"}.mdi-numeric-9-circle-outline:before{content:"\FC8D"}.mdi-numeric-9-plus-box:before{content:"\F3BF"}.mdi-numeric-9-plus-box-multiple-outline:before{content:"\F3C0"}.mdi-numeric-9-plus-box-outline:before{content:"\F3C1"}.mdi-numeric-9-plus-circle:before{content:"\FC8E"}.mdi-numeric-9-plus-circle-outline:before{content:"\FC8F"}.mdi-nut:before{content:"\F6F7"}.mdi-nutrition:before{content:"\F3C2"}.mdi-oar:before{content:"\F67B"}.mdi-ocarina:before{content:"\FDBC"}.mdi-octagon:before{content:"\F3C3"}.mdi-octagon-outline:before{content:"\F3C4"}.mdi-octagram:before{content:"\F6F8"}.mdi-octagram-outline:before{content:"\F774"}.mdi-odnoklassniki:before{content:"\F3C5"}.mdi-office:before{content:"\F3C6"}.mdi-office-building:before{content:"\F990"}.mdi-oil:before{content:"\F3C7"}.mdi-oil-temperature:before{content:"\F3C8"}.mdi-omega:before{content:"\F3C9"}.mdi-one-up:before{content:"\FB89"}.mdi-onedrive:before{content:"\F3CA"}.mdi-onenote:before{content:"\F746"}.mdi-onepassword:before{content:"\F880"}.mdi-opacity:before{content:"\F5CC"}.mdi-open-in-app:before{content:"\F3CB"}.mdi-open-in-new:before{content:"\F3CC"}.mdi-open-source-initiative:before{content:"\FB8A"}.mdi-openid:before{content:"\F3CD"}.mdi-opera:before{content:"\F3CE"}.mdi-orbit:before{content:"\F018"}.mdi-origin:before{content:"\FB2B"}.mdi-ornament:before{content:"\F3CF"}.mdi-ornament-variant:before{content:"\F3D0"}.mdi-outlook:before{content:"\FCFE"}.mdi-owl:before{content:"\F3D2"}.mdi-pac-man:before{content:"\FB8B"}.mdi-package:before{content:"\F3D3"}.mdi-package-down:before{content:"\F3D4"}.mdi-package-up:before{content:"\F3D5"}.mdi-package-variant:before{content:"\F3D6"}.mdi-package-variant-closed:before{content:"\F3D7"}.mdi-page-first:before{content:"\F600"}.mdi-page-last:before{content:"\F601"}.mdi-page-layout-body:before{content:"\F6F9"}.mdi-page-layout-footer:before{content:"\F6FA"}.mdi-page-layout-header:before{content:"\F6FB"}.mdi-page-layout-sidebar-left:before{content:"\F6FC"}.mdi-page-layout-sidebar-right:before{content:"\F6FD"}.mdi-page-next:before{content:"\FB8C"}.mdi-page-next-outline:before{content:"\FB8D"}.mdi-page-previous:before{content:"\FB8E"}.mdi-page-previous-outline:before{content:"\FB8F"}.mdi-palette:before{content:"\F3D8"}.mdi-palette-advanced:before{content:"\F3D9"}.mdi-palette-outline:before{content:"\FDE8"}.mdi-palette-swatch:before{content:"\F8B4"}.mdi-pan:before{content:"\FB90"}.mdi-pan-bottom-left:before{content:"\FB91"}.mdi-pan-bottom-right:before{content:"\FB92"}.mdi-pan-down:before{content:"\FB93"}.mdi-pan-horizontal:before{content:"\FB94"}.mdi-pan-left:before{content:"\FB95"}.mdi-pan-right:before{content:"\FB96"}.mdi-pan-top-left:before{content:"\FB97"}.mdi-pan-top-right:before{content:"\FB98"}.mdi-pan-up:before{content:"\FB99"}.mdi-pan-vertical:before{content:"\FB9A"}.mdi-panda:before{content:"\F3DA"}.mdi-pandora:before{content:"\F3DB"}.mdi-panorama:before{content:"\F3DC"}.mdi-panorama-fisheye:before{content:"\F3DD"}.mdi-panorama-horizontal:before{content:"\F3DE"}.mdi-panorama-vertical:before{content:"\F3DF"}.mdi-panorama-wide-angle:before{content:"\F3E0"}.mdi-paper-cut-vertical:before{content:"\F3E1"}.mdi-paperclip:before{content:"\F3E2"}.mdi-parachute:before{content:"\FC90"}.mdi-parachute-outline:before{content:"\FC91"}.mdi-parking:before{content:"\F3E3"}.mdi-passport:before{content:"\F7E2"}.mdi-passport-biometric:before{content:"\FDBD"}.mdi-patreon:before{content:"\F881"}.mdi-pause:before{content:"\F3E4"}.mdi-pause-circle:before{content:"\F3E5"}.mdi-pause-circle-outline:before{content:"\F3E6"}.mdi-pause-octagon:before{content:"\F3E7"}.mdi-pause-octagon-outline:before{content:"\F3E8"}.mdi-paw:before{content:"\F3E9"}.mdi-paw-off:before{content:"\F657"}.mdi-paypal:before{content:"\F882"}.mdi-peace:before{content:"\F883"}.mdi-pen:before{content:"\F3EA"}.mdi-pen-lock:before{content:"\FDBE"}.mdi-pen-minus:before{content:"\FDBF"}.mdi-pen-off:before{content:"\FDC0"}.mdi-pen-plus:before{content:"\FDC1"}.mdi-pen-remove:before{content:"\FDC2"}.mdi-pencil:before{content:"\F3EB"}.mdi-pencil-box:before{content:"\F3EC"}.mdi-pencil-box-outline:before{content:"\F3ED"}.mdi-pencil-circle:before{content:"\F6FE"}.mdi-pencil-circle-outline:before{content:"\F775"}.mdi-pencil-lock:before{content:"\F3EE"}.mdi-pencil-lock-outline:before{content:"\FDC3"}.mdi-pencil-minus:before{content:"\FDC4"}.mdi-pencil-minus-outline:before{content:"\FDC5"}.mdi-pencil-off:before{content:"\F3EF"}.mdi-pencil-off-outline:before{content:"\FDC6"}.mdi-pencil-outline:before{content:"\FC92"}.mdi-pencil-plus:before{content:"\FDC7"}.mdi-pencil-plus-outline:before{content:"\FDC8"}.mdi-pencil-remove:before{content:"\FDC9"}.mdi-pencil-remove-outline:before{content:"\FDCA"}.mdi-pentagon:before{content:"\F6FF"}.mdi-pentagon-outline:before{content:"\F700"}.mdi-percent:before{content:"\F3F0"}.mdi-periodic-table:before{content:"\F8B5"}.mdi-periodic-table-co2:before{content:"\F7E3"}.mdi-periscope:before{content:"\F747"}.mdi-perspective-less:before{content:"\FCFF"}.mdi-perspective-more:before{content:"\FD00"}.mdi-pharmacy:before{content:"\F3F1"}.mdi-phone:before{content:"\F3F2"}.mdi-phone-bluetooth:before{content:"\F3F3"}.mdi-phone-classic:before{content:"\F602"}.mdi-phone-forward:before{content:"\F3F4"}.mdi-phone-hangup:before{content:"\F3F5"}.mdi-phone-in-talk:before{content:"\F3F6"}.mdi-phone-incoming:before{content:"\F3F7"}.mdi-phone-lock:before{content:"\F3F8"}.mdi-phone-log:before{content:"\F3F9"}.mdi-phone-minus:before{content:"\F658"}.mdi-phone-missed:before{content:"\F3FA"}.mdi-phone-off:before{content:"\FDCB"}.mdi-phone-outgoing:before{content:"\F3FB"}.mdi-phone-outline:before{content:"\FDCC"}.mdi-phone-paused:before{content:"\F3FC"}.mdi-phone-plus:before{content:"\F659"}.mdi-phone-return:before{content:"\F82E"}.mdi-phone-rotate-landscape:before{content:"\F884"}.mdi-phone-rotate-portrait:before{content:"\F885"}.mdi-phone-settings:before{content:"\F3FD"}.mdi-phone-voip:before{content:"\F3FE"}.mdi-pi:before{content:"\F3FF"}.mdi-pi-box:before{content:"\F400"}.mdi-pi-hole:before{content:"\FDCD"}.mdi-piano:before{content:"\F67C"}.mdi-pickaxe:before{content:"\F8B6"}.mdi-pier:before{content:"\F886"}.mdi-pier-crane:before{content:"\F887"}.mdi-pig:before{content:"\F401"}.mdi-pill:before{content:"\F402"}.mdi-pillar:before{content:"\F701"}.mdi-pin:before{content:"\F403"}.mdi-pin-off:before{content:"\F404"}.mdi-pin-off-outline:before{content:"\F92F"}.mdi-pin-outline:before{content:"\F930"}.mdi-pine-tree:before{content:"\F405"}.mdi-pine-tree-box:before{content:"\F406"}.mdi-pinterest:before{content:"\F407"}.mdi-pinterest-box:before{content:"\F408"}.mdi-pinwheel:before{content:"\FAD4"}.mdi-pinwheel-outline:before{content:"\FAD5"}.mdi-pipe:before{content:"\F7E4"}.mdi-pipe-disconnected:before{content:"\F7E5"}.mdi-pipe-leak:before{content:"\F888"}.mdi-pirate:before{content:"\FA07"}.mdi-pistol:before{content:"\F702"}.mdi-piston:before{content:"\F889"}.mdi-pizza:before{content:"\F409"}.mdi-play:before{content:"\F40A"}.mdi-play-box-outline:before{content:"\F40B"}.mdi-play-circle:before{content:"\F40C"}.mdi-play-circle-outline:before{content:"\F40D"}.mdi-play-network:before{content:"\F88A"}.mdi-play-network-outline:before{content:"\FC93"}.mdi-play-pause:before{content:"\F40E"}.mdi-play-protected-content:before{content:"\F40F"}.mdi-play-speed:before{content:"\F8FE"}.mdi-playlist-check:before{content:"\F5C7"}.mdi-playlist-edit:before{content:"\F8FF"}.mdi-playlist-minus:before{content:"\F410"}.mdi-playlist-music:before{content:"\FC94"}.mdi-playlist-music-outline:before{content:"\FC95"}.mdi-playlist-play:before{content:"\F411"}.mdi-playlist-plus:before{content:"\F412"}.mdi-playlist-remove:before{content:"\F413"}.mdi-playlist-star:before{content:"\FDCE"}.mdi-playstation:before{content:"\F414"}.mdi-plex:before{content:"\F6B9"}.mdi-plus:before{content:"\F415"}.mdi-plus-box:before{content:"\F416"}.mdi-plus-box-outline:before{content:"\F703"}.mdi-plus-circle:before{content:"\F417"}.mdi-plus-circle-multiple-outline:before{content:"\F418"}.mdi-plus-circle-outline:before{content:"\F419"}.mdi-plus-minus:before{content:"\F991"}.mdi-plus-minus-box:before{content:"\F992"}.mdi-plus-network:before{content:"\F41A"}.mdi-plus-network-outline:before{content:"\FC96"}.mdi-plus-one:before{content:"\F41B"}.mdi-plus-outline:before{content:"\F704"}.mdi-pocket:before{content:"\F41C"}.mdi-podcast:before{content:"\F993"}.mdi-podium:before{content:"\FD01"}.mdi-podium-bronze:before{content:"\FD02"}.mdi-podium-gold:before{content:"\FD03"}.mdi-podium-silver:before{content:"\FD04"}.mdi-point-of-sale:before{content:"\FD6E"}.mdi-pokeball:before{content:"\F41D"}.mdi-pokemon-go:before{content:"\FA08"}.mdi-poker-chip:before{content:"\F82F"}.mdi-polaroid:before{content:"\F41E"}.mdi-poll:before{content:"\F41F"}.mdi-poll-box:before{content:"\F420"}.mdi-polymer:before{content:"\F421"}.mdi-pool:before{content:"\F606"}.mdi-popcorn:before{content:"\F422"}.mdi-postage-stamp:before{content:"\FC97"}.mdi-pot:before{content:"\F65A"}.mdi-pot-mix:before{content:"\F65B"}.mdi-pound:before{content:"\F423"}.mdi-pound-box:before{content:"\F424"}.mdi-power:before{content:"\F425"}.mdi-power-cycle:before{content:"\F900"}.mdi-power-off:before{content:"\F901"}.mdi-power-on:before{content:"\F902"}.mdi-power-plug:before{content:"\F6A4"}.mdi-power-plug-off:before{content:"\F6A5"}.mdi-power-settings:before{content:"\F426"}.mdi-power-sleep:before{content:"\F903"}.mdi-power-socket:before{content:"\F427"}.mdi-power-socket-au:before{content:"\F904"}.mdi-power-socket-eu:before{content:"\F7E6"}.mdi-power-socket-uk:before{content:"\F7E7"}.mdi-power-socket-us:before{content:"\F7E8"}.mdi-power-standby:before{content:"\F905"}.mdi-powershell:before{content:"\FA09"}.mdi-prescription:before{content:"\F705"}.mdi-presentation:before{content:"\F428"}.mdi-presentation-play:before{content:"\F429"}.mdi-printer:before{content:"\F42A"}.mdi-printer-3d:before{content:"\F42B"}.mdi-printer-alert:before{content:"\F42C"}.mdi-printer-settings:before{content:"\F706"}.mdi-printer-wireless:before{content:"\FA0A"}.mdi-priority-high:before{content:"\F603"}.mdi-priority-low:before{content:"\F604"}.mdi-professional-hexagon:before{content:"\F42D"}.mdi-progress-alert:before{content:"\FC98"}.mdi-progress-check:before{content:"\F994"}.mdi-progress-clock:before{content:"\F995"}.mdi-progress-download:before{content:"\F996"}.mdi-progress-upload:before{content:"\F997"}.mdi-progress-wrench:before{content:"\FC99"}.mdi-projector:before{content:"\F42E"}.mdi-projector-screen:before{content:"\F42F"}.mdi-publish:before{content:"\F6A6"}.mdi-pulse:before{content:"\F430"}.mdi-pumpkin:before{content:"\FB9B"}.mdi-puzzle:before{content:"\F431"}.mdi-puzzle-outline:before{content:"\FA65"}.mdi-qi:before{content:"\F998"}.mdi-qqchat:before{content:"\F605"}.mdi-qrcode:before{content:"\F432"}.mdi-qrcode-edit:before{content:"\F8B7"}.mdi-qrcode-scan:before{content:"\F433"}.mdi-quadcopter:before{content:"\F434"}.mdi-quality-high:before{content:"\F435"}.mdi-quality-low:before{content:"\FA0B"}.mdi-quality-medium:before{content:"\FA0C"}.mdi-quicktime:before{content:"\F436"}.mdi-quora:before{content:"\FD05"}.mdi-rabbit:before{content:"\F906"}.mdi-racing-helmet:before{content:"\FD6F"}.mdi-racquetball:before{content:"\FD70"}.mdi-radar:before{content:"\F437"}.mdi-radiator:before{content:"\F438"}.mdi-radiator-disabled:before{content:"\FAD6"}.mdi-radiator-off:before{content:"\FAD7"}.mdi-radio:before{content:"\F439"}.mdi-radio-am:before{content:"\FC9A"}.mdi-radio-fm:before{content:"\FC9B"}.mdi-radio-handheld:before{content:"\F43A"}.mdi-radio-tower:before{content:"\F43B"}.mdi-radioactive:before{content:"\F43C"}.mdi-radiobox-blank:before{content:"\F43D"}.mdi-radiobox-marked:before{content:"\F43E"}.mdi-radius:before{content:"\FC9C"}.mdi-radius-outline:before{content:"\FC9D"}.mdi-raspberry-pi:before{content:"\F43F"}.mdi-ray-end:before{content:"\F440"}.mdi-ray-end-arrow:before{content:"\F441"}.mdi-ray-start:before{content:"\F442"}.mdi-ray-start-arrow:before{content:"\F443"}.mdi-ray-start-end:before{content:"\F444"}.mdi-ray-vertex:before{content:"\F445"}.mdi-react:before{content:"\F707"}.mdi-read:before{content:"\F447"}.mdi-receipt:before{content:"\F449"}.mdi-record:before{content:"\F44A"}.mdi-record-player:before{content:"\F999"}.mdi-record-rec:before{content:"\F44B"}.mdi-recycle:before{content:"\F44C"}.mdi-reddit:before{content:"\F44D"}.mdi-redo:before{content:"\F44E"}.mdi-redo-variant:before{content:"\F44F"}.mdi-reflect-horizontal:before{content:"\FA0D"}.mdi-reflect-vertical:before{content:"\FA0E"}.mdi-refresh:before{content:"\F450"}.mdi-regex:before{content:"\F451"}.mdi-registered-trademark:before{content:"\FA66"}.mdi-relative-scale:before{content:"\F452"}.mdi-reload:before{content:"\F453"}.mdi-reminder:before{content:"\F88B"}.mdi-remote:before{content:"\F454"}.mdi-remote-desktop:before{content:"\F8B8"}.mdi-rename-box:before{content:"\F455"}.mdi-reorder-horizontal:before{content:"\F687"}.mdi-reorder-vertical:before{content:"\F688"}.mdi-repeat:before{content:"\F456"}.mdi-repeat-off:before{content:"\F457"}.mdi-repeat-once:before{content:"\F458"}.mdi-replay:before{content:"\F459"}.mdi-reply:before{content:"\F45A"}.mdi-reply-all:before{content:"\F45B"}.mdi-reproduction:before{content:"\F45C"}.mdi-resistor:before{content:"\FB1F"}.mdi-resistor-nodes:before{content:"\FB20"}.mdi-resize:before{content:"\FA67"}.mdi-resize-bottom-right:before{content:"\F45D"}.mdi-responsive:before{content:"\F45E"}.mdi-restart:before{content:"\F708"}.mdi-restart-off:before{content:"\FD71"}.mdi-restore:before{content:"\F99A"}.mdi-restore-clock:before{content:"\F6A7"}.mdi-rewind:before{content:"\F45F"}.mdi-rewind-10:before{content:"\FD06"}.mdi-rewind-30:before{content:"\FD72"}.mdi-rewind-outline:before{content:"\F709"}.mdi-rhombus:before{content:"\F70A"}.mdi-rhombus-medium:before{content:"\FA0F"}.mdi-rhombus-outline:before{content:"\F70B"}.mdi-rhombus-split:before{content:"\FA10"}.mdi-ribbon:before{content:"\F460"}.mdi-rice:before{content:"\F7E9"}.mdi-ring:before{content:"\F7EA"}.mdi-road:before{content:"\F461"}.mdi-road-variant:before{content:"\F462"}.mdi-robot:before{content:"\F6A8"}.mdi-robot-industrial:before{content:"\FB21"}.mdi-robot-vacuum:before{content:"\F70C"}.mdi-robot-vacuum-variant:before{content:"\F907"}.mdi-rocket:before{content:"\F463"}.mdi-roller-skate:before{content:"\FD07"}.mdi-rollerblade:before{content:"\FD08"}.mdi-rollupjs:before{content:"\FB9C"}.mdi-room-service:before{content:"\F88C"}.mdi-room-service-outline:before{content:"\FD73"}.mdi-rotate-3d:before{content:"\F464"}.mdi-rotate-left:before{content:"\F465"}.mdi-rotate-left-variant:before{content:"\F466"}.mdi-rotate-orbit:before{content:"\FD74"}.mdi-rotate-right:before{content:"\F467"}.mdi-rotate-right-variant:before{content:"\F468"}.mdi-rounded-corner:before{content:"\F607"}.mdi-router-wireless:before{content:"\F469"}.mdi-router-wireless-settings:before{content:"\FA68"}.mdi-routes:before{content:"\F46A"}.mdi-rowing:before{content:"\F608"}.mdi-rss:before{content:"\F46B"}.mdi-rss-box:before{content:"\F46C"}.mdi-ruby:before{content:"\FD09"}.mdi-rugby:before{content:"\FD75"}.mdi-ruler:before{content:"\F46D"}.mdi-ruler-square:before{content:"\FC9E"}.mdi-run:before{content:"\F70D"}.mdi-run-fast:before{content:"\F46E"}.mdi-sack:before{content:"\FD0A"}.mdi-sack-percent:before{content:"\FD0B"}.mdi-safe:before{content:"\FA69"}.mdi-safety-goggles:before{content:"\FD0C"}.mdi-sale:before{content:"\F46F"}.mdi-salesforce:before{content:"\F88D"}.mdi-sass:before{content:"\F7EB"}.mdi-satellite:before{content:"\F470"}.mdi-satellite-uplink:before{content:"\F908"}.mdi-satellite-variant:before{content:"\F471"}.mdi-sausage:before{content:"\F8B9"}.mdi-saxophone:before{content:"\F609"}.mdi-scale:before{content:"\F472"}.mdi-scale-balance:before{content:"\F5D1"}.mdi-scale-bathroom:before{content:"\F473"}.mdi-scanner:before{content:"\F6AA"}.mdi-scanner-off:before{content:"\F909"}.mdi-school:before{content:"\F474"}.mdi-scissors-cutting:before{content:"\FA6A"}.mdi-screen-rotation:before{content:"\F475"}.mdi-screen-rotation-lock:before{content:"\F476"}.mdi-screw-flat-top:before{content:"\FDCF"}.mdi-screw-lag:before{content:"\FDD0"}.mdi-screw-machine-flat-top:before{content:"\FDD1"}.mdi-screw-machine-round-top:before{content:"\FDD2"}.mdi-screw-round-top:before{content:"\FDD3"}.mdi-screwdriver:before{content:"\F477"}.mdi-script:before{content:"\FB9D"}.mdi-script-outline:before{content:"\F478"}.mdi-script-text:before{content:"\FB9E"}.mdi-script-text-outline:before{content:"\FB9F"}.mdi-sd:before{content:"\F479"}.mdi-seal:before{content:"\F47A"}.mdi-search-web:before{content:"\F70E"}.mdi-seat:before{content:"\FC9F"}.mdi-seat-flat:before{content:"\F47B"}.mdi-seat-flat-angled:before{content:"\F47C"}.mdi-seat-individual-suite:before{content:"\F47D"}.mdi-seat-legroom-extra:before{content:"\F47E"}.mdi-seat-legroom-normal:before{content:"\F47F"}.mdi-seat-legroom-reduced:before{content:"\F480"}.mdi-seat-outline:before{content:"\FCA0"}.mdi-seat-recline-extra:before{content:"\F481"}.mdi-seat-recline-normal:before{content:"\F482"}.mdi-seatbelt:before{content:"\FCA1"}.mdi-security:before{content:"\F483"}.mdi-security-network:before{content:"\F484"}.mdi-select:before{content:"\F485"}.mdi-select-all:before{content:"\F486"}.mdi-select-color:before{content:"\FD0D"}.mdi-select-compare:before{content:"\FAD8"}.mdi-select-drag:before{content:"\FA6B"}.mdi-select-inverse:before{content:"\F487"}.mdi-select-off:before{content:"\F488"}.mdi-selection:before{content:"\F489"}.mdi-selection-drag:before{content:"\FA6C"}.mdi-selection-ellipse:before{content:"\FD0E"}.mdi-selection-off:before{content:"\F776"}.mdi-send:before{content:"\F48A"}.mdi-send-circle:before{content:"\FDD4"}.mdi-send-circle-outline:before{content:"\FDD5"}.mdi-send-lock:before{content:"\F7EC"}.mdi-serial-port:before{content:"\F65C"}.mdi-server:before{content:"\F48B"}.mdi-server-minus:before{content:"\F48C"}.mdi-server-network:before{content:"\F48D"}.mdi-server-network-off:before{content:"\F48E"}.mdi-server-off:before{content:"\F48F"}.mdi-server-plus:before{content:"\F490"}.mdi-server-remove:before{content:"\F491"}.mdi-server-security:before{content:"\F492"}.mdi-set-all:before{content:"\F777"}.mdi-set-center:before{content:"\F778"}.mdi-set-center-right:before{content:"\F779"}.mdi-set-left:before{content:"\F77A"}.mdi-set-left-center:before{content:"\F77B"}.mdi-set-left-right:before{content:"\F77C"}.mdi-set-none:before{content:"\F77D"}.mdi-set-right:before{content:"\F77E"}.mdi-set-top-box:before{content:"\F99E"}.mdi-settings:before{content:"\F493"}.mdi-settings-box:before{content:"\F494"}.mdi-settings-helper:before{content:"\FA6D"}.mdi-settings-outline:before{content:"\F8BA"}.mdi-shape:before{content:"\F830"}.mdi-shape-circle-plus:before{content:"\F65D"}.mdi-shape-outline:before{content:"\F831"}.mdi-shape-plus:before{content:"\F495"}.mdi-shape-polygon-plus:before{content:"\F65E"}.mdi-shape-rectangle-plus:before{content:"\F65F"}.mdi-shape-square-plus:before{content:"\F660"}.mdi-share:before{content:"\F496"}.mdi-share-outline:before{content:"\F931"}.mdi-share-variant:before{content:"\F497"}.mdi-sheep:before{content:"\FCA2"}.mdi-shield:before{content:"\F498"}.mdi-shield-account:before{content:"\F88E"}.mdi-shield-account-outline:before{content:"\FA11"}.mdi-shield-airplane:before{content:"\F6BA"}.mdi-shield-airplane-outline:before{content:"\FCA3"}.mdi-shield-check:before{content:"\F565"}.mdi-shield-check-outline:before{content:"\FCA4"}.mdi-shield-cross:before{content:"\FCA5"}.mdi-shield-cross-outline:before{content:"\FCA6"}.mdi-shield-half-full:before{content:"\F77F"}.mdi-shield-home:before{content:"\F689"}.mdi-shield-home-outline:before{content:"\FCA7"}.mdi-shield-key:before{content:"\FBA0"}.mdi-shield-key-outline:before{content:"\FBA1"}.mdi-shield-link-variant:before{content:"\FD0F"}.mdi-shield-link-variant-outline:before{content:"\FD10"}.mdi-shield-lock:before{content:"\F99C"}.mdi-shield-lock-outline:before{content:"\FCA8"}.mdi-shield-off:before{content:"\F99D"}.mdi-shield-off-outline:before{content:"\F99B"}.mdi-shield-outline:before{content:"\F499"}.mdi-shield-plus:before{content:"\FAD9"}.mdi-shield-plus-outline:before{content:"\FADA"}.mdi-shield-remove:before{content:"\FADB"}.mdi-shield-remove-outline:before{content:"\FADC"}.mdi-shield-search:before{content:"\FD76"}.mdi-ship-wheel:before{content:"\F832"}.mdi-shoe-formal:before{content:"\FB22"}.mdi-shoe-heel:before{content:"\FB23"}.mdi-shoe-print:before{content:"\FDD6"}.mdi-shopify:before{content:"\FADD"}.mdi-shopping:before{content:"\F49A"}.mdi-shopping-music:before{content:"\F49B"}.mdi-shovel:before{content:"\F70F"}.mdi-shovel-off:before{content:"\F710"}.mdi-shower:before{content:"\F99F"}.mdi-shower-head:before{content:"\F9A0"}.mdi-shredder:before{content:"\F49C"}.mdi-shuffle:before{content:"\F49D"}.mdi-shuffle-disabled:before{content:"\F49E"}.mdi-shuffle-variant:before{content:"\F49F"}.mdi-sigma:before{content:"\F4A0"}.mdi-sigma-lower:before{content:"\F62B"}.mdi-sign-caution:before{content:"\F4A1"}.mdi-sign-direction:before{content:"\F780"}.mdi-sign-text:before{content:"\F781"}.mdi-signal:before{content:"\F4A2"}.mdi-signal-2g:before{content:"\F711"}.mdi-signal-3g:before{content:"\F712"}.mdi-signal-4g:before{content:"\F713"}.mdi-signal-5g:before{content:"\FA6E"}.mdi-signal-cellular-1:before{content:"\F8BB"}.mdi-signal-cellular-2:before{content:"\F8BC"}.mdi-signal-cellular-3:before{content:"\F8BD"}.mdi-signal-cellular-outline:before{content:"\F8BE"}.mdi-signal-hspa:before{content:"\F714"}.mdi-signal-hspa-plus:before{content:"\F715"}.mdi-signal-off:before{content:"\F782"}.mdi-signal-variant:before{content:"\F60A"}.mdi-signature:before{content:"\FDD7"}.mdi-signature-freehand:before{content:"\FDD8"}.mdi-signature-image:before{content:"\FDD9"}.mdi-signature-text:before{content:"\FDDA"}.mdi-silo:before{content:"\FB24"}.mdi-silverware:before{content:"\F4A3"}.mdi-silverware-fork:before{content:"\F4A4"}.mdi-silverware-fork-knife:before{content:"\FA6F"}.mdi-silverware-spoon:before{content:"\F4A5"}.mdi-silverware-variant:before{content:"\F4A6"}.mdi-sim:before{content:"\F4A7"}.mdi-sim-alert:before{content:"\F4A8"}.mdi-sim-off:before{content:"\F4A9"}.mdi-sina-weibo:before{content:"\FADE"}.mdi-sitemap:before{content:"\F4AA"}.mdi-skate:before{content:"\FD11"}.mdi-skew-less:before{content:"\FD12"}.mdi-skew-more:before{content:"\FD13"}.mdi-skip-backward:before{content:"\F4AB"}.mdi-skip-forward:before{content:"\F4AC"}.mdi-skip-next:before{content:"\F4AD"}.mdi-skip-next-circle:before{content:"\F661"}.mdi-skip-next-circle-outline:before{content:"\F662"}.mdi-skip-previous:before{content:"\F4AE"}.mdi-skip-previous-circle:before{content:"\F663"}.mdi-skip-previous-circle-outline:before{content:"\F664"}.mdi-skull:before{content:"\F68B"}.mdi-skull-crossbones:before{content:"\FBA2"}.mdi-skull-crossbones-outline:before{content:"\FBA3"}.mdi-skull-outline:before{content:"\FBA4"}.mdi-skype:before{content:"\F4AF"}.mdi-skype-business:before{content:"\F4B0"}.mdi-slack:before{content:"\F4B1"}.mdi-slackware:before{content:"\F90A"}.mdi-sleep:before{content:"\F4B2"}.mdi-sleep-off:before{content:"\F4B3"}.mdi-slope-downhill:before{content:"\FDDB"}.mdi-slope-uphill:before{content:"\FDDC"}.mdi-smog:before{content:"\FA70"}.mdi-smoke-detector:before{content:"\F392"}.mdi-smoking:before{content:"\F4B4"}.mdi-smoking-off:before{content:"\F4B5"}.mdi-snapchat:before{content:"\F4B6"}.mdi-snowflake:before{content:"\F716"}.mdi-snowman:before{content:"\F4B7"}.mdi-soccer:before{content:"\F4B8"}.mdi-soccer-field:before{content:"\F833"}.mdi-sofa:before{content:"\F4B9"}.mdi-solar-panel:before{content:"\FD77"}.mdi-solar-panel-large:before{content:"\FD78"}.mdi-solar-power:before{content:"\FA71"}.mdi-solid:before{content:"\F68C"}.mdi-sort:before{content:"\F4BA"}.mdi-sort-alphabetical:before{content:"\F4BB"}.mdi-sort-ascending:before{content:"\F4BC"}.mdi-sort-descending:before{content:"\F4BD"}.mdi-sort-numeric:before{content:"\F4BE"}.mdi-sort-variant:before{content:"\F4BF"}.mdi-sort-variant-lock:before{content:"\FCA9"}.mdi-sort-variant-lock-open:before{content:"\FCAA"}.mdi-soundcloud:before{content:"\F4C0"}.mdi-source-branch:before{content:"\F62C"}.mdi-source-commit:before{content:"\F717"}.mdi-source-commit-end:before{content:"\F718"}.mdi-source-commit-end-local:before{content:"\F719"}.mdi-source-commit-local:before{content:"\F71A"}.mdi-source-commit-next-local:before{content:"\F71B"}.mdi-source-commit-start:before{content:"\F71C"}.mdi-source-commit-start-next-local:before{content:"\F71D"}.mdi-source-fork:before{content:"\F4C1"}.mdi-source-merge:before{content:"\F62D"}.mdi-source-pull:before{content:"\F4C2"}.mdi-source-repository:before{content:"\FCAB"}.mdi-source-repository-multiple:before{content:"\FCAC"}.mdi-soy-sauce:before{content:"\F7ED"}.mdi-spa:before{content:"\FCAD"}.mdi-spa-outline:before{content:"\FCAE"}.mdi-space-invaders:before{content:"\FBA5"}.mdi-speaker:before{content:"\F4C3"}.mdi-speaker-bluetooth:before{content:"\F9A1"}.mdi-speaker-multiple:before{content:"\FD14"}.mdi-speaker-off:before{content:"\F4C4"}.mdi-speaker-wireless:before{content:"\F71E"}.mdi-speedometer:before{content:"\F4C5"}.mdi-spellcheck:before{content:"\F4C6"}.mdi-spider-web:before{content:"\FBA6"}.mdi-spotify:before{content:"\F4C7"}.mdi-spotlight:before{content:"\F4C8"}.mdi-spotlight-beam:before{content:"\F4C9"}.mdi-spray:before{content:"\F665"}.mdi-spray-bottle:before{content:"\FADF"}.mdi-square:before{content:"\F763"}.mdi-square-edit-outline:before{content:"\F90B"}.mdi-square-inc:before{content:"\F4CA"}.mdi-square-inc-cash:before{content:"\F4CB"}.mdi-square-medium:before{content:"\FA12"}.mdi-square-medium-outline:before{content:"\FA13"}.mdi-square-outline:before{content:"\F762"}.mdi-square-root:before{content:"\F783"}.mdi-square-root-box:before{content:"\F9A2"}.mdi-square-small:before{content:"\FA14"}.mdi-squeegee:before{content:"\FAE0"}.mdi-ssh:before{content:"\F8BF"}.mdi-stack-exchange:before{content:"\F60B"}.mdi-stack-overflow:before{content:"\F4CC"}.mdi-stadium:before{content:"\F71F"}.mdi-stairs:before{content:"\F4CD"}.mdi-stamper:before{content:"\FD15"}.mdi-standard-definition:before{content:"\F7EE"}.mdi-star:before{content:"\F4CE"}.mdi-star-box:before{content:"\FA72"}.mdi-star-box-outline:before{content:"\FA73"}.mdi-star-circle:before{content:"\F4CF"}.mdi-star-circle-outline:before{content:"\F9A3"}.mdi-star-face:before{content:"\F9A4"}.mdi-star-four-points:before{content:"\FAE1"}.mdi-star-four-points-outline:before{content:"\FAE2"}.mdi-star-half:before{content:"\F4D0"}.mdi-star-off:before{content:"\F4D1"}.mdi-star-outline:before{content:"\F4D2"}.mdi-star-three-points:before{content:"\FAE3"}.mdi-star-three-points-outline:before{content:"\FAE4"}.mdi-steam:before{content:"\F4D3"}.mdi-steam-box:before{content:"\F90C"}.mdi-steering:before{content:"\F4D4"}.mdi-steering-off:before{content:"\F90D"}.mdi-step-backward:before{content:"\F4D5"}.mdi-step-backward-2:before{content:"\F4D6"}.mdi-step-forward:before{content:"\F4D7"}.mdi-step-forward-2:before{content:"\F4D8"}.mdi-stethoscope:before{content:"\F4D9"}.mdi-sticker:before{content:"\F5D0"}.mdi-sticker-emoji:before{content:"\F784"}.mdi-stocking:before{content:"\F4DA"}.mdi-stop:before{content:"\F4DB"}.mdi-stop-circle:before{content:"\F666"}.mdi-stop-circle-outline:before{content:"\F667"}.mdi-store:before{content:"\F4DC"}.mdi-store-24-hour:before{content:"\F4DD"}.mdi-stove:before{content:"\F4DE"}.mdi-strava:before{content:"\FB25"}.mdi-subdirectory-arrow-left:before{content:"\F60C"}.mdi-subdirectory-arrow-right:before{content:"\F60D"}.mdi-subtitles:before{content:"\FA15"}.mdi-subtitles-outline:before{content:"\FA16"}.mdi-subway:before{content:"\F6AB"}.mdi-subway-alert-variant:before{content:"\FD79"}.mdi-subway-variant:before{content:"\F4DF"}.mdi-summit:before{content:"\F785"}.mdi-sunglasses:before{content:"\F4E0"}.mdi-surround-sound:before{content:"\F5C5"}.mdi-surround-sound-2-0:before{content:"\F7EF"}.mdi-surround-sound-3-1:before{content:"\F7F0"}.mdi-surround-sound-5-1:before{content:"\F7F1"}.mdi-surround-sound-7-1:before{content:"\F7F2"}.mdi-svg:before{content:"\F720"}.mdi-swap-horizontal:before{content:"\F4E1"}.mdi-swap-horizontal-bold:before{content:"\FBA9"}.mdi-swap-horizontal-variant:before{content:"\F8C0"}.mdi-swap-vertical:before{content:"\F4E2"}.mdi-swap-vertical-bold:before{content:"\FBAA"}.mdi-swap-vertical-variant:before{content:"\F8C1"}.mdi-swim:before{content:"\F4E3"}.mdi-switch:before{content:"\F4E4"}.mdi-sword:before{content:"\F4E5"}.mdi-sword-cross:before{content:"\F786"}.mdi-symfony:before{content:"\FAE5"}.mdi-sync:before{content:"\F4E6"}.mdi-sync-alert:before{content:"\F4E7"}.mdi-sync-off:before{content:"\F4E8"}.mdi-tab:before{content:"\F4E9"}.mdi-tab-minus:before{content:"\FB26"}.mdi-tab-plus:before{content:"\F75B"}.mdi-tab-remove:before{content:"\FB27"}.mdi-tab-unselected:before{content:"\F4EA"}.mdi-table:before{content:"\F4EB"}.mdi-table-border:before{content:"\FA17"}.mdi-table-column:before{content:"\F834"}.mdi-table-column-plus-after:before{content:"\F4EC"}.mdi-table-column-plus-before:before{content:"\F4ED"}.mdi-table-column-remove:before{content:"\F4EE"}.mdi-table-column-width:before{content:"\F4EF"}.mdi-table-edit:before{content:"\F4F0"}.mdi-table-large:before{content:"\F4F1"}.mdi-table-merge-cells:before{content:"\F9A5"}.mdi-table-of-contents:before{content:"\F835"}.mdi-table-plus:before{content:"\FA74"}.mdi-table-remove:before{content:"\FA75"}.mdi-table-row:before{content:"\F836"}.mdi-table-row-height:before{content:"\F4F2"}.mdi-table-row-plus-after:before{content:"\F4F3"}.mdi-table-row-plus-before:before{content:"\F4F4"}.mdi-table-row-remove:before{content:"\F4F5"}.mdi-table-search:before{content:"\F90E"}.mdi-table-settings:before{content:"\F837"}.mdi-tablet:before{content:"\F4F6"}.mdi-tablet-android:before{content:"\F4F7"}.mdi-tablet-cellphone:before{content:"\F9A6"}.mdi-tablet-ipad:before{content:"\F4F8"}.mdi-taco:before{content:"\F761"}.mdi-tag:before{content:"\F4F9"}.mdi-tag-faces:before{content:"\F4FA"}.mdi-tag-heart:before{content:"\F68A"}.mdi-tag-heart-outline:before{content:"\FBAB"}.mdi-tag-minus:before{content:"\F90F"}.mdi-tag-multiple:before{content:"\F4FB"}.mdi-tag-outline:before{content:"\F4FC"}.mdi-tag-plus:before{content:"\F721"}.mdi-tag-remove:before{content:"\F722"}.mdi-tag-text-outline:before{content:"\F4FD"}.mdi-tank:before{content:"\FD16"}.mdi-tape-measure:before{content:"\FB28"}.mdi-target:before{content:"\F4FE"}.mdi-target-account:before{content:"\FBAC"}.mdi-target-variant:before{content:"\FA76"}.mdi-taxi:before{content:"\F4FF"}.mdi-tea:before{content:"\FD7A"}.mdi-tea-outline:before{content:"\FD7B"}.mdi-teach:before{content:"\F88F"}.mdi-teamviewer:before{content:"\F500"}.mdi-telegram:before{content:"\F501"}.mdi-telescope:before{content:"\FB29"}.mdi-television:before{content:"\F502"}.mdi-television-box:before{content:"\F838"}.mdi-television-classic:before{content:"\F7F3"}.mdi-television-classic-off:before{content:"\F839"}.mdi-television-guide:before{content:"\F503"}.mdi-television-off:before{content:"\F83A"}.mdi-temperature-celsius:before{content:"\F504"}.mdi-temperature-fahrenheit:before{content:"\F505"}.mdi-temperature-kelvin:before{content:"\F506"}.mdi-tennis:before{content:"\FD7C"}.mdi-tennis-ball:before{content:"\F507"}.mdi-tent:before{content:"\F508"}.mdi-terrain:before{content:"\F509"}.mdi-test-tube:before{content:"\F668"}.mdi-test-tube-empty:before{content:"\F910"}.mdi-test-tube-off:before{content:"\F911"}.mdi-text:before{content:"\F9A7"}.mdi-text-shadow:before{content:"\F669"}.mdi-text-short:before{content:"\F9A8"}.mdi-text-subject:before{content:"\F9A9"}.mdi-text-to-speech:before{content:"\F50A"}.mdi-text-to-speech-off:before{content:"\F50B"}.mdi-textbox:before{content:"\F60E"}.mdi-textbox-password:before{content:"\F7F4"}.mdi-texture:before{content:"\F50C"}.mdi-theater:before{content:"\F50D"}.mdi-theme-light-dark:before{content:"\F50E"}.mdi-thermometer:before{content:"\F50F"}.mdi-thermometer-alert:before{content:"\FDDD"}.mdi-thermometer-chevron-down:before{content:"\FDDE"}.mdi-thermometer-chevron-up:before{content:"\FDDF"}.mdi-thermometer-lines:before{content:"\F510"}.mdi-thermometer-minus:before{content:"\FDE0"}.mdi-thermometer-plus:before{content:"\FDE1"}.mdi-thermostat:before{content:"\F393"}.mdi-thermostat-box:before{content:"\F890"}.mdi-thought-bubble:before{content:"\F7F5"}.mdi-thought-bubble-outline:before{content:"\F7F6"}.mdi-thumb-down:before{content:"\F511"}.mdi-thumb-down-outline:before{content:"\F512"}.mdi-thumb-up:before{content:"\F513"}.mdi-thumb-up-outline:before{content:"\F514"}.mdi-thumbs-up-down:before{content:"\F515"}.mdi-ticket:before{content:"\F516"}.mdi-ticket-account:before{content:"\F517"}.mdi-ticket-confirmation:before{content:"\F518"}.mdi-ticket-outline:before{content:"\F912"}.mdi-ticket-percent:before{content:"\F723"}.mdi-tie:before{content:"\F519"}.mdi-tilde:before{content:"\F724"}.mdi-timelapse:before{content:"\F51A"}.mdi-timeline:before{content:"\FBAD"}.mdi-timeline-outline:before{content:"\FBAE"}.mdi-timeline-text:before{content:"\FBAF"}.mdi-timeline-text-outline:before{content:"\FBB0"}.mdi-timer:before{content:"\F51B"}.mdi-timer-10:before{content:"\F51C"}.mdi-timer-3:before{content:"\F51D"}.mdi-timer-off:before{content:"\F51E"}.mdi-timer-sand:before{content:"\F51F"}.mdi-timer-sand-empty:before{content:"\F6AC"}.mdi-timer-sand-full:before{content:"\F78B"}.mdi-timetable:before{content:"\F520"}.mdi-toaster-oven:before{content:"\FCAF"}.mdi-toggle-switch:before{content:"\F521"}.mdi-toggle-switch-off:before{content:"\F522"}.mdi-toggle-switch-off-outline:before{content:"\FA18"}.mdi-toggle-switch-outline:before{content:"\FA19"}.mdi-toilet:before{content:"\F9AA"}.mdi-toolbox:before{content:"\F9AB"}.mdi-toolbox-outline:before{content:"\F9AC"}.mdi-tooltip:before{content:"\F523"}.mdi-tooltip-account:before{content:"\F00C"}.mdi-tooltip-edit:before{content:"\F524"}.mdi-tooltip-image:before{content:"\F525"}.mdi-tooltip-image-outline:before{content:"\FBB1"}.mdi-tooltip-outline:before{content:"\F526"}.mdi-tooltip-plus:before{content:"\FBB2"}.mdi-tooltip-plus-outline:before{content:"\F527"}.mdi-tooltip-text:before{content:"\F528"}.mdi-tooltip-text-outline:before{content:"\FBB3"}.mdi-tooth:before{content:"\F8C2"}.mdi-tooth-outline:before{content:"\F529"}.mdi-tor:before{content:"\F52A"}.mdi-tortoise:before{content:"\FD17"}.mdi-tournament:before{content:"\F9AD"}.mdi-tower-beach:before{content:"\F680"}.mdi-tower-fire:before{content:"\F681"}.mdi-towing:before{content:"\F83B"}.mdi-track-light:before{content:"\F913"}.mdi-trackpad:before{content:"\F7F7"}.mdi-trackpad-lock:before{content:"\F932"}.mdi-tractor:before{content:"\F891"}.mdi-trademark:before{content:"\FA77"}.mdi-traffic-light:before{content:"\F52B"}.mdi-train:before{content:"\F52C"}.mdi-train-car:before{content:"\FBB4"}.mdi-train-variant:before{content:"\F8C3"}.mdi-tram:before{content:"\F52D"}.mdi-transcribe:before{content:"\F52E"}.mdi-transcribe-close:before{content:"\F52F"}.mdi-transfer-down:before{content:"\FD7D"}.mdi-transfer-left:before{content:"\FD7E"}.mdi-transfer-right:before{content:"\F530"}.mdi-transfer-up:before{content:"\FD7F"}.mdi-transit-connection:before{content:"\FD18"}.mdi-transit-connection-variant:before{content:"\FD19"}.mdi-transit-transfer:before{content:"\F6AD"}.mdi-transition:before{content:"\F914"}.mdi-transition-masked:before{content:"\F915"}.mdi-translate:before{content:"\F5CA"}.mdi-translate-off:before{content:"\FDE2"}.mdi-transmission-tower:before{content:"\FD1A"}.mdi-trash-can:before{content:"\FA78"}.mdi-trash-can-outline:before{content:"\FA79"}.mdi-treasure-chest:before{content:"\F725"}.mdi-tree:before{content:"\F531"}.mdi-trello:before{content:"\F532"}.mdi-trending-down:before{content:"\F533"}.mdi-trending-neutral:before{content:"\F534"}.mdi-trending-up:before{content:"\F535"}.mdi-triangle:before{content:"\F536"}.mdi-triangle-outline:before{content:"\F537"}.mdi-triforce:before{content:"\FBB5"}.mdi-trophy:before{content:"\F538"}.mdi-trophy-award:before{content:"\F539"}.mdi-trophy-broken:before{content:"\FD80"}.mdi-trophy-outline:before{content:"\F53A"}.mdi-trophy-variant:before{content:"\F53B"}.mdi-trophy-variant-outline:before{content:"\F53C"}.mdi-truck:before{content:"\F53D"}.mdi-truck-check:before{content:"\FCB0"}.mdi-truck-delivery:before{content:"\F53E"}.mdi-truck-fast:before{content:"\F787"}.mdi-truck-trailer:before{content:"\F726"}.mdi-tshirt-crew:before{content:"\FA7A"}.mdi-tshirt-crew-outline:before{content:"\F53F"}.mdi-tshirt-v:before{content:"\FA7B"}.mdi-tshirt-v-outline:before{content:"\F540"}.mdi-tumble-dryer:before{content:"\F916"}.mdi-tumblr:before{content:"\F541"}.mdi-tumblr-box:before{content:"\F917"}.mdi-tumblr-reblog:before{content:"\F542"}.mdi-tune:before{content:"\F62E"}.mdi-tune-vertical:before{content:"\F66A"}.mdi-turnstile:before{content:"\FCB1"}.mdi-turnstile-outline:before{content:"\FCB2"}.mdi-turtle:before{content:"\FCB3"}.mdi-twitch:before{content:"\F543"}.mdi-twitter:before{content:"\F544"}.mdi-twitter-box:before{content:"\F545"}.mdi-twitter-circle:before{content:"\F546"}.mdi-twitter-retweet:before{content:"\F547"}.mdi-two-factor-authentication:before{content:"\F9AE"}.mdi-uber:before{content:"\F748"}.mdi-ubisoft:before{content:"\FBB6"}.mdi-ubuntu:before{content:"\F548"}.mdi-ultra-high-definition:before{content:"\F7F8"}.mdi-umbraco:before{content:"\F549"}.mdi-umbrella:before{content:"\F54A"}.mdi-umbrella-closed:before{content:"\F9AF"}.mdi-umbrella-outline:before{content:"\F54B"}.mdi-undo:before{content:"\F54C"}.mdi-undo-variant:before{content:"\F54D"}.mdi-unfold-less-horizontal:before{content:"\F54E"}.mdi-unfold-less-vertical:before{content:"\F75F"}.mdi-unfold-more-horizontal:before{content:"\F54F"}.mdi-unfold-more-vertical:before{content:"\F760"}.mdi-ungroup:before{content:"\F550"}.mdi-unity:before{content:"\F6AE"}.mdi-unreal:before{content:"\F9B0"}.mdi-untappd:before{content:"\F551"}.mdi-update:before{content:"\F6AF"}.mdi-upload:before{content:"\F552"}.mdi-upload-multiple:before{content:"\F83C"}.mdi-upload-network:before{content:"\F6F5"}.mdi-upload-network-outline:before{content:"\FCB4"}.mdi-upload-outline:before{content:"\FDE3"}.mdi-usb:before{content:"\F553"}.mdi-van-passenger:before{content:"\F7F9"}.mdi-van-utility:before{content:"\F7FA"}.mdi-vanish:before{content:"\F7FB"}.mdi-variable:before{content:"\FAE6"}.mdi-vector-arrange-above:before{content:"\F554"}.mdi-vector-arrange-below:before{content:"\F555"}.mdi-vector-bezier:before{content:"\FAE7"}.mdi-vector-circle:before{content:"\F556"}.mdi-vector-circle-variant:before{content:"\F557"}.mdi-vector-combine:before{content:"\F558"}.mdi-vector-curve:before{content:"\F559"}.mdi-vector-difference:before{content:"\F55A"}.mdi-vector-difference-ab:before{content:"\F55B"}.mdi-vector-difference-ba:before{content:"\F55C"}.mdi-vector-ellipse:before{content:"\F892"}.mdi-vector-intersection:before{content:"\F55D"}.mdi-vector-line:before{content:"\F55E"}.mdi-vector-point:before{content:"\F55F"}.mdi-vector-polygon:before{content:"\F560"}.mdi-vector-polyline:before{content:"\F561"}.mdi-vector-radius:before{content:"\F749"}.mdi-vector-rectangle:before{content:"\F5C6"}.mdi-vector-selection:before{content:"\F562"}.mdi-vector-square:before{content:"\F001"}.mdi-vector-triangle:before{content:"\F563"}.mdi-vector-union:before{content:"\F564"}.mdi-venmo:before{content:"\F578"}.mdi-vhs:before{content:"\FA1A"}.mdi-vibrate:before{content:"\F566"}.mdi-vibrate-off:before{content:"\FCB5"}.mdi-video:before{content:"\F567"}.mdi-video-3d:before{content:"\F7FC"}.mdi-video-4k-box:before{content:"\F83D"}.mdi-video-account:before{content:"\F918"}.mdi-video-image:before{content:"\F919"}.mdi-video-input-antenna:before{content:"\F83E"}.mdi-video-input-component:before{content:"\F83F"}.mdi-video-input-hdmi:before{content:"\F840"}.mdi-video-input-svideo:before{content:"\F841"}.mdi-video-minus:before{content:"\F9B1"}.mdi-video-off:before{content:"\F568"}.mdi-video-off-outline:before{content:"\FBB7"}.mdi-video-outline:before{content:"\FBB8"}.mdi-video-plus:before{content:"\F9B2"}.mdi-video-stabilization:before{content:"\F91A"}.mdi-video-switch:before{content:"\F569"}.mdi-video-vintage:before{content:"\FA1B"}.mdi-view-agenda:before{content:"\F56A"}.mdi-view-array:before{content:"\F56B"}.mdi-view-carousel:before{content:"\F56C"}.mdi-view-column:before{content:"\F56D"}.mdi-view-dashboard:before{content:"\F56E"}.mdi-view-dashboard-outline:before{content:"\FA1C"}.mdi-view-dashboard-variant:before{content:"\F842"}.mdi-view-day:before{content:"\F56F"}.mdi-view-grid:before{content:"\F570"}.mdi-view-headline:before{content:"\F571"}.mdi-view-list:before{content:"\F572"}.mdi-view-module:before{content:"\F573"}.mdi-view-parallel:before{content:"\F727"}.mdi-view-quilt:before{content:"\F574"}.mdi-view-sequential:before{content:"\F728"}.mdi-view-split-horizontal:before{content:"\FBA7"}.mdi-view-split-vertical:before{content:"\FBA8"}.mdi-view-stream:before{content:"\F575"}.mdi-view-week:before{content:"\F576"}.mdi-vimeo:before{content:"\F577"}.mdi-violin:before{content:"\F60F"}.mdi-virtual-reality:before{content:"\F893"}.mdi-visual-studio:before{content:"\F610"}.mdi-visual-studio-code:before{content:"\FA1D"}.mdi-vk:before{content:"\F579"}.mdi-vk-box:before{content:"\F57A"}.mdi-vk-circle:before{content:"\F57B"}.mdi-vlc:before{content:"\F57C"}.mdi-voice:before{content:"\F5CB"}.mdi-voicemail:before{content:"\F57D"}.mdi-volleyball:before{content:"\F9B3"}.mdi-volume-high:before{content:"\F57E"}.mdi-volume-low:before{content:"\F57F"}.mdi-volume-medium:before{content:"\F580"}.mdi-volume-minus:before{content:"\F75D"}.mdi-volume-mute:before{content:"\F75E"}.mdi-volume-off:before{content:"\F581"}.mdi-volume-plus:before{content:"\F75C"}.mdi-volume-variant-off:before{content:"\FDE4"}.mdi-vote:before{content:"\FA1E"}.mdi-vote-outline:before{content:"\FA1F"}.mdi-vpn:before{content:"\F582"}.mdi-vuejs:before{content:"\F843"}.mdi-walk:before{content:"\F583"}.mdi-wall:before{content:"\F7FD"}.mdi-wall-sconce:before{content:"\F91B"}.mdi-wall-sconce-flat:before{content:"\F91C"}.mdi-wall-sconce-variant:before{content:"\F91D"}.mdi-wallet:before{content:"\F584"}.mdi-wallet-giftcard:before{content:"\F585"}.mdi-wallet-membership:before{content:"\F586"}.mdi-wallet-outline:before{content:"\FBB9"}.mdi-wallet-travel:before{content:"\F587"}.mdi-wallpaper:before{content:"\FDE5"}.mdi-wan:before{content:"\F588"}.mdi-washing-machine:before{content:"\F729"}.mdi-watch:before{content:"\F589"}.mdi-watch-export:before{content:"\F58A"}.mdi-watch-export-variant:before{content:"\F894"}.mdi-watch-import:before{content:"\F58B"}.mdi-watch-import-variant:before{content:"\F895"}.mdi-watch-variant:before{content:"\F896"}.mdi-watch-vibrate:before{content:"\F6B0"}.mdi-watch-vibrate-off:before{content:"\FCB6"}.mdi-water:before{content:"\F58C"}.mdi-water-off:before{content:"\F58D"}.mdi-water-outline:before{content:"\FDE6"}.mdi-water-percent:before{content:"\F58E"}.mdi-water-pump:before{content:"\F58F"}.mdi-watermark:before{content:"\F612"}.mdi-waves:before{content:"\F78C"}.mdi-waze:before{content:"\FBBA"}.mdi-weather-cloudy:before{content:"\F590"}.mdi-weather-fog:before{content:"\F591"}.mdi-weather-hail:before{content:"\F592"}.mdi-weather-hurricane:before{content:"\F897"}.mdi-weather-lightning:before{content:"\F593"}.mdi-weather-lightning-rainy:before{content:"\F67D"}.mdi-weather-night:before{content:"\F594"}.mdi-weather-partlycloudy:before{content:"\F595"}.mdi-weather-pouring:before{content:"\F596"}.mdi-weather-rainy:before{content:"\F597"}.mdi-weather-snowy:before{content:"\F598"}.mdi-weather-snowy-rainy:before{content:"\F67E"}.mdi-weather-sunny:before{content:"\F599"}.mdi-weather-sunset:before{content:"\F59A"}.mdi-weather-sunset-down:before{content:"\F59B"}.mdi-weather-sunset-up:before{content:"\F59C"}.mdi-weather-windy:before{content:"\F59D"}.mdi-weather-windy-variant:before{content:"\F59E"}.mdi-web:before{content:"\F59F"}.mdi-webcam:before{content:"\F5A0"}.mdi-webhook:before{content:"\F62F"}.mdi-webpack:before{content:"\F72A"}.mdi-wechat:before{content:"\F611"}.mdi-weight:before{content:"\F5A1"}.mdi-weight-gram:before{content:"\FD1B"}.mdi-weight-kilogram:before{content:"\F5A2"}.mdi-weight-pound:before{content:"\F9B4"}.mdi-whatsapp:before{content:"\F5A3"}.mdi-wheelchair-accessibility:before{content:"\F5A4"}.mdi-whistle:before{content:"\F9B5"}.mdi-white-balance-auto:before{content:"\F5A5"}.mdi-white-balance-incandescent:before{content:"\F5A6"}.mdi-white-balance-iridescent:before{content:"\F5A7"}.mdi-white-balance-sunny:before{content:"\F5A8"}.mdi-widgets:before{content:"\F72B"}.mdi-wifi:before{content:"\F5A9"}.mdi-wifi-off:before{content:"\F5AA"}.mdi-wifi-star:before{content:"\FDE7"}.mdi-wifi-strength-1:before{content:"\F91E"}.mdi-wifi-strength-1-alert:before{content:"\F91F"}.mdi-wifi-strength-1-lock:before{content:"\F920"}.mdi-wifi-strength-2:before{content:"\F921"}.mdi-wifi-strength-2-alert:before{content:"\F922"}.mdi-wifi-strength-2-lock:before{content:"\F923"}.mdi-wifi-strength-3:before{content:"\F924"}.mdi-wifi-strength-3-alert:before{content:"\F925"}.mdi-wifi-strength-3-lock:before{content:"\F926"}.mdi-wifi-strength-4:before{content:"\F927"}.mdi-wifi-strength-4-alert:before{content:"\F928"}.mdi-wifi-strength-4-lock:before{content:"\F929"}.mdi-wifi-strength-alert-outline:before{content:"\F92A"}.mdi-wifi-strength-lock-outline:before{content:"\F92B"}.mdi-wifi-strength-off:before{content:"\F92C"}.mdi-wifi-strength-off-outline:before{content:"\F92D"}.mdi-wifi-strength-outline:before{content:"\F92E"}.mdi-wii:before{content:"\F5AB"}.mdi-wiiu:before{content:"\F72C"}.mdi-wikipedia:before{content:"\F5AC"}.mdi-wind-turbine:before{content:"\FD81"}.mdi-window-close:before{content:"\F5AD"}.mdi-window-closed:before{content:"\F5AE"}.mdi-window-maximize:before{content:"\F5AF"}.mdi-window-minimize:before{content:"\F5B0"}.mdi-window-open:before{content:"\F5B1"}.mdi-window-restore:before{content:"\F5B2"}.mdi-windows:before{content:"\F5B3"}.mdi-windows-classic:before{content:"\FA20"}.mdi-wiper:before{content:"\FAE8"}.mdi-wiper-wash:before{content:"\FD82"}.mdi-wordpress:before{content:"\F5B4"}.mdi-worker:before{content:"\F5B5"}.mdi-wrap:before{content:"\F5B6"}.mdi-wrap-disabled:before{content:"\FBBB"}.mdi-wrench:before{content:"\F5B7"}.mdi-wrench-outline:before{content:"\FBBC"}.mdi-wunderlist:before{content:"\F5B8"}.mdi-xamarin:before{content:"\F844"}.mdi-xamarin-outline:before{content:"\F845"}.mdi-xaml:before{content:"\F673"}.mdi-xbox:before{content:"\F5B9"}.mdi-xbox-controller:before{content:"\F5BA"}.mdi-xbox-controller-battery-alert:before{content:"\F74A"}.mdi-xbox-controller-battery-charging:before{content:"\FA21"}.mdi-xbox-controller-battery-empty:before{content:"\F74B"}.mdi-xbox-controller-battery-full:before{content:"\F74C"}.mdi-xbox-controller-battery-low:before{content:"\F74D"}.mdi-xbox-controller-battery-medium:before{content:"\F74E"}.mdi-xbox-controller-battery-unknown:before{content:"\F74F"}.mdi-xbox-controller-off:before{content:"\F5BB"}.mdi-xda:before{content:"\F5BC"}.mdi-xing:before{content:"\F5BD"}.mdi-xing-box:before{content:"\F5BE"}.mdi-xing-circle:before{content:"\F5BF"}.mdi-xml:before{content:"\F5C0"}.mdi-xmpp:before{content:"\F7FE"}.mdi-yahoo:before{content:"\FB2A"}.mdi-yammer:before{content:"\F788"}.mdi-yeast:before{content:"\F5C1"}.mdi-yelp:before{content:"\F5C2"}.mdi-yin-yang:before{content:"\F67F"}.mdi-youtube:before{content:"\F5C3"}.mdi-youtube-creator-studio:before{content:"\F846"}.mdi-youtube-gaming:before{content:"\F847"}.mdi-youtube-subscription:before{content:"\FD1C"}.mdi-youtube-tv:before{content:"\F448"}.mdi-z-wave:before{content:"\FAE9"}.mdi-zend:before{content:"\FAEA"}.mdi-zigbee:before{content:"\FD1D"}.mdi-zip-box:before{content:"\F5C4"}.mdi-zip-disk:before{content:"\FA22"}.mdi-zodiac-aquarius:before{content:"\FA7C"}.mdi-zodiac-aries:before{content:"\FA7D"}.mdi-zodiac-cancer:before{content:"\FA7E"}.mdi-zodiac-capricorn:before{content:"\FA7F"}.mdi-zodiac-gemini:before{content:"\FA80"}.mdi-zodiac-leo:before{content:"\FA81"}.mdi-zodiac-libra:before{content:"\FA82"}.mdi-zodiac-pisces:before{content:"\FA83"}.mdi-zodiac-sagittarius:before{content:"\FA84"}.mdi-zodiac-scorpio:before{content:"\FA85"}.mdi-zodiac-taurus:before{content:"\FA86"}.mdi-zodiac-virgo:before{content:"\FA87"}.mdi-blank:before{content:"\F68C";visibility:hidden}.mdi-18px.mdi-set,.mdi-18px.mdi:before{font-size:18px}.mdi-24px.mdi-set,.mdi-24px.mdi:before{font-size:24px}.mdi-36px.mdi-set,.mdi-36px.mdi:before{font-size:36px}.mdi-48px.mdi-set,.mdi-48px.mdi:before{font-size:48px}.mdi-dark:before{color:rgba(0,0,0,0.54)}.mdi-dark.mdi-inactive:before{color:rgba(0,0,0,0.26)}.mdi-light:before{color:#fff}.mdi-light.mdi-inactive:before{color:rgba(255,255,255,0.3)}.mdi-rotate-45:before{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.mdi-rotate-90:before{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.mdi-rotate-135:before{-webkit-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg)}.mdi-rotate-180:before{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.mdi-rotate-225:before{-webkit-transform:rotate(225deg);-ms-transform:rotate(225deg);transform:rotate(225deg)}.mdi-rotate-270:before{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.mdi-rotate-315:before{-webkit-transform:rotate(315deg);-ms-transform:rotate(315deg);transform:rotate(315deg)}.mdi-flip-h:before{-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.mdi-flip-v:before{-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.mdi-spin:before{-webkit-animation:mdi-spin 2s infinite linear;animation:mdi-spin 2s infinite linear}@-webkit-keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}} -/*# sourceMappingURL=materialdesignicons.min.css.map */ +/* MaterialDesignIcons.com */@font-face{font-family:"Material Design Icons";src:url("../fonts/materialdesignicons-webfont.eot?v=3.5.95");src:url("../fonts/materialdesignicons-webfont.eot?#iefix&v=3.5.95") format("embedded-opentype"),url("../fonts/materialdesignicons-webfont.woff2?v=3.5.95") format("woff2"),url("../fonts/materialdesignicons-webfont.woff?v=3.5.95") format("woff"),url("../fonts/materialdesignicons-webfont.ttf?v=3.5.95") format("truetype"),url("../fonts/materialdesignicons-webfont.svg?v=3.5.95#materialdesigniconsregular") format("svg");font-weight:normal;font-style:normal}.mdi:before,.mdi-set{display:inline-block;font:normal normal normal 24px/1 "Material Design Icons";font-size:inherit;text-rendering:auto;line-height:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.mdi-access-point:before{content:"\F002"}.mdi-access-point-network:before{content:"\F003"}.mdi-access-point-network-off:before{content:"\FBBD"}.mdi-account:before{content:"\F004"}.mdi-account-alert:before{content:"\F005"}.mdi-account-alert-outline:before{content:"\FB2C"}.mdi-account-arrow-left:before{content:"\FB2D"}.mdi-account-arrow-left-outline:before{content:"\FB2E"}.mdi-account-arrow-right:before{content:"\FB2F"}.mdi-account-arrow-right-outline:before{content:"\FB30"}.mdi-account-badge:before{content:"\FD83"}.mdi-account-badge-alert:before{content:"\FD84"}.mdi-account-badge-alert-outline:before{content:"\FD85"}.mdi-account-badge-outline:before{content:"\FD86"}.mdi-account-box:before{content:"\F006"}.mdi-account-box-multiple:before{content:"\F933"}.mdi-account-box-outline:before{content:"\F007"}.mdi-account-card-details:before{content:"\F5D2"}.mdi-account-card-details-outline:before{content:"\FD87"}.mdi-account-check:before{content:"\F008"}.mdi-account-check-outline:before{content:"\FBBE"}.mdi-account-child:before{content:"\FA88"}.mdi-account-child-circle:before{content:"\FA89"}.mdi-account-circle:before{content:"\F009"}.mdi-account-circle-outline:before{content:"\FB31"}.mdi-account-clock:before{content:"\FB32"}.mdi-account-clock-outline:before{content:"\FB33"}.mdi-account-convert:before{content:"\F00A"}.mdi-account-details:before{content:"\F631"}.mdi-account-edit:before{content:"\F6BB"}.mdi-account-group:before{content:"\F848"}.mdi-account-group-outline:before{content:"\FB34"}.mdi-account-heart:before{content:"\F898"}.mdi-account-heart-outline:before{content:"\FBBF"}.mdi-account-key:before{content:"\F00B"}.mdi-account-key-outline:before{content:"\FBC0"}.mdi-account-minus:before{content:"\F00D"}.mdi-account-minus-outline:before{content:"\FAEB"}.mdi-account-multiple:before{content:"\F00E"}.mdi-account-multiple-check:before{content:"\F8C4"}.mdi-account-multiple-minus:before{content:"\F5D3"}.mdi-account-multiple-minus-outline:before{content:"\FBC1"}.mdi-account-multiple-outline:before{content:"\F00F"}.mdi-account-multiple-plus:before{content:"\F010"}.mdi-account-multiple-plus-outline:before{content:"\F7FF"}.mdi-account-network:before{content:"\F011"}.mdi-account-network-outline:before{content:"\FBC2"}.mdi-account-off:before{content:"\F012"}.mdi-account-off-outline:before{content:"\FBC3"}.mdi-account-outline:before{content:"\F013"}.mdi-account-plus:before{content:"\F014"}.mdi-account-plus-outline:before{content:"\F800"}.mdi-account-question:before{content:"\FB35"}.mdi-account-question-outline:before{content:"\FB36"}.mdi-account-remove:before{content:"\F015"}.mdi-account-remove-outline:before{content:"\FAEC"}.mdi-account-search:before{content:"\F016"}.mdi-account-search-outline:before{content:"\F934"}.mdi-account-settings:before{content:"\F630"}.mdi-account-star:before{content:"\F017"}.mdi-account-star-outline:before{content:"\FBC4"}.mdi-account-supervisor:before{content:"\FA8A"}.mdi-account-supervisor-circle:before{content:"\FA8B"}.mdi-account-switch:before{content:"\F019"}.mdi-account-tie:before{content:"\FCBF"}.mdi-accusoft:before{content:"\F849"}.mdi-adchoices:before{content:"\FD1E"}.mdi-adjust:before{content:"\F01A"}.mdi-adobe:before{content:"\F935"}.mdi-air-conditioner:before{content:"\F01B"}.mdi-air-filter:before{content:"\FD1F"}.mdi-air-horn:before{content:"\FD88"}.mdi-air-purifier:before{content:"\FD20"}.mdi-airbag:before{content:"\FBC5"}.mdi-airballoon:before{content:"\F01C"}.mdi-airplane:before{content:"\F01D"}.mdi-airplane-landing:before{content:"\F5D4"}.mdi-airplane-off:before{content:"\F01E"}.mdi-airplane-takeoff:before{content:"\F5D5"}.mdi-airplay:before{content:"\F01F"}.mdi-airport:before{content:"\F84A"}.mdi-alarm:before{content:"\F020"}.mdi-alarm-bell:before{content:"\F78D"}.mdi-alarm-check:before{content:"\F021"}.mdi-alarm-light:before{content:"\F78E"}.mdi-alarm-light-outline:before{content:"\FBC6"}.mdi-alarm-multiple:before{content:"\F022"}.mdi-alarm-off:before{content:"\F023"}.mdi-alarm-plus:before{content:"\F024"}.mdi-alarm-snooze:before{content:"\F68D"}.mdi-album:before{content:"\F025"}.mdi-alert:before{content:"\F026"}.mdi-alert-box:before{content:"\F027"}.mdi-alert-box-outline:before{content:"\FCC0"}.mdi-alert-circle:before{content:"\F028"}.mdi-alert-circle-outline:before{content:"\F5D6"}.mdi-alert-decagram:before{content:"\F6BC"}.mdi-alert-decagram-outline:before{content:"\FCC1"}.mdi-alert-octagon:before{content:"\F029"}.mdi-alert-octagon-outline:before{content:"\FCC2"}.mdi-alert-octagram:before{content:"\F766"}.mdi-alert-octagram-outline:before{content:"\FCC3"}.mdi-alert-outline:before{content:"\F02A"}.mdi-alien:before{content:"\F899"}.mdi-all-inclusive:before{content:"\F6BD"}.mdi-alpha:before{content:"\F02B"}.mdi-alpha-a:before{content:"\41"}.mdi-alpha-a-box:before{content:"\FAED"}.mdi-alpha-a-box-outline:before{content:"\FBC7"}.mdi-alpha-a-circle:before{content:"\FBC8"}.mdi-alpha-a-circle-outline:before{content:"\FBC9"}.mdi-alpha-b:before{content:"\42"}.mdi-alpha-b-box:before{content:"\FAEE"}.mdi-alpha-b-box-outline:before{content:"\FBCA"}.mdi-alpha-b-circle:before{content:"\FBCB"}.mdi-alpha-b-circle-outline:before{content:"\FBCC"}.mdi-alpha-c:before{content:"\43"}.mdi-alpha-c-box:before{content:"\FAEF"}.mdi-alpha-c-box-outline:before{content:"\FBCD"}.mdi-alpha-c-circle:before{content:"\FBCE"}.mdi-alpha-c-circle-outline:before{content:"\FBCF"}.mdi-alpha-d:before{content:"\44"}.mdi-alpha-d-box:before{content:"\FAF0"}.mdi-alpha-d-box-outline:before{content:"\FBD0"}.mdi-alpha-d-circle:before{content:"\FBD1"}.mdi-alpha-d-circle-outline:before{content:"\FBD2"}.mdi-alpha-e:before{content:"\45"}.mdi-alpha-e-box:before{content:"\FAF1"}.mdi-alpha-e-box-outline:before{content:"\FBD3"}.mdi-alpha-e-circle:before{content:"\FBD4"}.mdi-alpha-e-circle-outline:before{content:"\FBD5"}.mdi-alpha-f:before{content:"\46"}.mdi-alpha-f-box:before{content:"\FAF2"}.mdi-alpha-f-box-outline:before{content:"\FBD6"}.mdi-alpha-f-circle:before{content:"\FBD7"}.mdi-alpha-f-circle-outline:before{content:"\FBD8"}.mdi-alpha-g:before{content:"\47"}.mdi-alpha-g-box:before{content:"\FAF3"}.mdi-alpha-g-box-outline:before{content:"\FBD9"}.mdi-alpha-g-circle:before{content:"\FBDA"}.mdi-alpha-g-circle-outline:before{content:"\FBDB"}.mdi-alpha-h:before{content:"\48"}.mdi-alpha-h-box:before{content:"\FAF4"}.mdi-alpha-h-box-outline:before{content:"\FBDC"}.mdi-alpha-h-circle:before{content:"\FBDD"}.mdi-alpha-h-circle-outline:before{content:"\FBDE"}.mdi-alpha-i:before{content:"\49"}.mdi-alpha-i-box:before{content:"\FAF5"}.mdi-alpha-i-box-outline:before{content:"\FBDF"}.mdi-alpha-i-circle:before{content:"\FBE0"}.mdi-alpha-i-circle-outline:before{content:"\FBE1"}.mdi-alpha-j:before{content:"\4A"}.mdi-alpha-j-box:before{content:"\FAF6"}.mdi-alpha-j-box-outline:before{content:"\FBE2"}.mdi-alpha-j-circle:before{content:"\FBE3"}.mdi-alpha-j-circle-outline:before{content:"\FBE4"}.mdi-alpha-k:before{content:"\4B"}.mdi-alpha-k-box:before{content:"\FAF7"}.mdi-alpha-k-box-outline:before{content:"\FBE5"}.mdi-alpha-k-circle:before{content:"\FBE6"}.mdi-alpha-k-circle-outline:before{content:"\FBE7"}.mdi-alpha-l:before{content:"\4C"}.mdi-alpha-l-box:before{content:"\FAF8"}.mdi-alpha-l-box-outline:before{content:"\FBE8"}.mdi-alpha-l-circle:before{content:"\FBE9"}.mdi-alpha-l-circle-outline:before{content:"\FBEA"}.mdi-alpha-m:before{content:"\4D"}.mdi-alpha-m-box:before{content:"\FAF9"}.mdi-alpha-m-box-outline:before{content:"\FBEB"}.mdi-alpha-m-circle:before{content:"\FBEC"}.mdi-alpha-m-circle-outline:before{content:"\FBED"}.mdi-alpha-n:before{content:"\4E"}.mdi-alpha-n-box:before{content:"\FAFA"}.mdi-alpha-n-box-outline:before{content:"\FBEE"}.mdi-alpha-n-circle:before{content:"\FBEF"}.mdi-alpha-n-circle-outline:before{content:"\FBF0"}.mdi-alpha-o:before{content:"\4F"}.mdi-alpha-o-box:before{content:"\FAFB"}.mdi-alpha-o-box-outline:before{content:"\FBF1"}.mdi-alpha-o-circle:before{content:"\FBF2"}.mdi-alpha-o-circle-outline:before{content:"\FBF3"}.mdi-alpha-p:before{content:"\50"}.mdi-alpha-p-box:before{content:"\FAFC"}.mdi-alpha-p-box-outline:before{content:"\FBF4"}.mdi-alpha-p-circle:before{content:"\FBF5"}.mdi-alpha-p-circle-outline:before{content:"\FBF6"}.mdi-alpha-q:before{content:"\51"}.mdi-alpha-q-box:before{content:"\FAFD"}.mdi-alpha-q-box-outline:before{content:"\FBF7"}.mdi-alpha-q-circle:before{content:"\FBF8"}.mdi-alpha-q-circle-outline:before{content:"\FBF9"}.mdi-alpha-r:before{content:"\52"}.mdi-alpha-r-box:before{content:"\FAFE"}.mdi-alpha-r-box-outline:before{content:"\FBFA"}.mdi-alpha-r-circle:before{content:"\FBFB"}.mdi-alpha-r-circle-outline:before{content:"\FBFC"}.mdi-alpha-s:before{content:"\53"}.mdi-alpha-s-box:before{content:"\FAFF"}.mdi-alpha-s-box-outline:before{content:"\FBFD"}.mdi-alpha-s-circle:before{content:"\FBFE"}.mdi-alpha-s-circle-outline:before{content:"\FBFF"}.mdi-alpha-t:before{content:"\54"}.mdi-alpha-t-box:before{content:"\FB00"}.mdi-alpha-t-box-outline:before{content:"\FC00"}.mdi-alpha-t-circle:before{content:"\FC01"}.mdi-alpha-t-circle-outline:before{content:"\FC02"}.mdi-alpha-u:before{content:"\55"}.mdi-alpha-u-box:before{content:"\FB01"}.mdi-alpha-u-box-outline:before{content:"\FC03"}.mdi-alpha-u-circle:before{content:"\FC04"}.mdi-alpha-u-circle-outline:before{content:"\FC05"}.mdi-alpha-v:before{content:"\56"}.mdi-alpha-v-box:before{content:"\FB02"}.mdi-alpha-v-box-outline:before{content:"\FC06"}.mdi-alpha-v-circle:before{content:"\FC07"}.mdi-alpha-v-circle-outline:before{content:"\FC08"}.mdi-alpha-w:before{content:"\57"}.mdi-alpha-w-box:before{content:"\FB03"}.mdi-alpha-w-box-outline:before{content:"\FC09"}.mdi-alpha-w-circle:before{content:"\FC0A"}.mdi-alpha-w-circle-outline:before{content:"\FC0B"}.mdi-alpha-x:before{content:"\58"}.mdi-alpha-x-box:before{content:"\FB04"}.mdi-alpha-x-box-outline:before{content:"\FC0C"}.mdi-alpha-x-circle:before{content:"\FC0D"}.mdi-alpha-x-circle-outline:before{content:"\FC0E"}.mdi-alpha-y:before{content:"\59"}.mdi-alpha-y-box:before{content:"\FB05"}.mdi-alpha-y-box-outline:before{content:"\FC0F"}.mdi-alpha-y-circle:before{content:"\FC10"}.mdi-alpha-y-circle-outline:before{content:"\FC11"}.mdi-alpha-z:before{content:"\5A"}.mdi-alpha-z-box:before{content:"\FB06"}.mdi-alpha-z-box-outline:before{content:"\FC12"}.mdi-alpha-z-circle:before{content:"\FC13"}.mdi-alpha-z-circle-outline:before{content:"\FC14"}.mdi-alphabetical:before{content:"\F02C"}.mdi-altimeter:before{content:"\F5D7"}.mdi-amazon:before{content:"\F02D"}.mdi-amazon-alexa:before{content:"\F8C5"}.mdi-amazon-drive:before{content:"\F02E"}.mdi-ambulance:before{content:"\F02F"}.mdi-ammunition:before{content:"\FCC4"}.mdi-ampersand:before{content:"\FA8C"}.mdi-amplifier:before{content:"\F030"}.mdi-anchor:before{content:"\F031"}.mdi-android:before{content:"\F032"}.mdi-android-auto:before{content:"\FA8D"}.mdi-android-debug-bridge:before{content:"\F033"}.mdi-android-head:before{content:"\F78F"}.mdi-android-messages:before{content:"\FD21"}.mdi-android-studio:before{content:"\F034"}.mdi-angle-acute:before{content:"\F936"}.mdi-angle-obtuse:before{content:"\F937"}.mdi-angle-right:before{content:"\F938"}.mdi-angular:before{content:"\F6B1"}.mdi-angularjs:before{content:"\F6BE"}.mdi-animation:before{content:"\F5D8"}.mdi-animation-outline:before{content:"\FA8E"}.mdi-animation-play:before{content:"\F939"}.mdi-animation-play-outline:before{content:"\FA8F"}.mdi-anvil:before{content:"\F89A"}.mdi-apple:before{content:"\F035"}.mdi-apple-finder:before{content:"\F036"}.mdi-apple-icloud:before{content:"\F038"}.mdi-apple-ios:before{content:"\F037"}.mdi-apple-keyboard-caps:before{content:"\F632"}.mdi-apple-keyboard-command:before{content:"\F633"}.mdi-apple-keyboard-control:before{content:"\F634"}.mdi-apple-keyboard-option:before{content:"\F635"}.mdi-apple-keyboard-shift:before{content:"\F636"}.mdi-apple-safari:before{content:"\F039"}.mdi-application:before{content:"\F614"}.mdi-application-export:before{content:"\FD89"}.mdi-application-import:before{content:"\FD8A"}.mdi-apps:before{content:"\F03B"}.mdi-apps-box:before{content:"\FD22"}.mdi-arch:before{content:"\F8C6"}.mdi-archive:before{content:"\F03C"}.mdi-arrange-bring-forward:before{content:"\F03D"}.mdi-arrange-bring-to-front:before{content:"\F03E"}.mdi-arrange-send-backward:before{content:"\F03F"}.mdi-arrange-send-to-back:before{content:"\F040"}.mdi-arrow-all:before{content:"\F041"}.mdi-arrow-bottom-left:before{content:"\F042"}.mdi-arrow-bottom-left-bold-outline:before{content:"\F9B6"}.mdi-arrow-bottom-left-thick:before{content:"\F9B7"}.mdi-arrow-bottom-right:before{content:"\F043"}.mdi-arrow-bottom-right-bold-outline:before{content:"\F9B8"}.mdi-arrow-bottom-right-thick:before{content:"\F9B9"}.mdi-arrow-collapse:before{content:"\F615"}.mdi-arrow-collapse-all:before{content:"\F044"}.mdi-arrow-collapse-down:before{content:"\F791"}.mdi-arrow-collapse-horizontal:before{content:"\F84B"}.mdi-arrow-collapse-left:before{content:"\F792"}.mdi-arrow-collapse-right:before{content:"\F793"}.mdi-arrow-collapse-up:before{content:"\F794"}.mdi-arrow-collapse-vertical:before{content:"\F84C"}.mdi-arrow-decision:before{content:"\F9BA"}.mdi-arrow-decision-auto:before{content:"\F9BB"}.mdi-arrow-decision-auto-outline:before{content:"\F9BC"}.mdi-arrow-decision-outline:before{content:"\F9BD"}.mdi-arrow-down:before{content:"\F045"}.mdi-arrow-down-bold:before{content:"\F72D"}.mdi-arrow-down-bold-box:before{content:"\F72E"}.mdi-arrow-down-bold-box-outline:before{content:"\F72F"}.mdi-arrow-down-bold-circle:before{content:"\F047"}.mdi-arrow-down-bold-circle-outline:before{content:"\F048"}.mdi-arrow-down-bold-hexagon-outline:before{content:"\F049"}.mdi-arrow-down-bold-outline:before{content:"\F9BE"}.mdi-arrow-down-box:before{content:"\F6BF"}.mdi-arrow-down-circle:before{content:"\FCB7"}.mdi-arrow-down-circle-outline:before{content:"\FCB8"}.mdi-arrow-down-drop-circle:before{content:"\F04A"}.mdi-arrow-down-drop-circle-outline:before{content:"\F04B"}.mdi-arrow-down-thick:before{content:"\F046"}.mdi-arrow-expand:before{content:"\F616"}.mdi-arrow-expand-all:before{content:"\F04C"}.mdi-arrow-expand-down:before{content:"\F795"}.mdi-arrow-expand-horizontal:before{content:"\F84D"}.mdi-arrow-expand-left:before{content:"\F796"}.mdi-arrow-expand-right:before{content:"\F797"}.mdi-arrow-expand-up:before{content:"\F798"}.mdi-arrow-expand-vertical:before{content:"\F84E"}.mdi-arrow-left:before{content:"\F04D"}.mdi-arrow-left-bold:before{content:"\F730"}.mdi-arrow-left-bold-box:before{content:"\F731"}.mdi-arrow-left-bold-box-outline:before{content:"\F732"}.mdi-arrow-left-bold-circle:before{content:"\F04F"}.mdi-arrow-left-bold-circle-outline:before{content:"\F050"}.mdi-arrow-left-bold-hexagon-outline:before{content:"\F051"}.mdi-arrow-left-bold-outline:before{content:"\F9BF"}.mdi-arrow-left-box:before{content:"\F6C0"}.mdi-arrow-left-circle:before{content:"\FCB9"}.mdi-arrow-left-circle-outline:before{content:"\FCBA"}.mdi-arrow-left-drop-circle:before{content:"\F052"}.mdi-arrow-left-drop-circle-outline:before{content:"\F053"}.mdi-arrow-left-right-bold-outline:before{content:"\F9C0"}.mdi-arrow-left-thick:before{content:"\F04E"}.mdi-arrow-right:before{content:"\F054"}.mdi-arrow-right-bold:before{content:"\F733"}.mdi-arrow-right-bold-box:before{content:"\F734"}.mdi-arrow-right-bold-box-outline:before{content:"\F735"}.mdi-arrow-right-bold-circle:before{content:"\F056"}.mdi-arrow-right-bold-circle-outline:before{content:"\F057"}.mdi-arrow-right-bold-hexagon-outline:before{content:"\F058"}.mdi-arrow-right-bold-outline:before{content:"\F9C1"}.mdi-arrow-right-box:before{content:"\F6C1"}.mdi-arrow-right-circle:before{content:"\FCBB"}.mdi-arrow-right-circle-outline:before{content:"\FCBC"}.mdi-arrow-right-drop-circle:before{content:"\F059"}.mdi-arrow-right-drop-circle-outline:before{content:"\F05A"}.mdi-arrow-right-thick:before{content:"\F055"}.mdi-arrow-split-horizontal:before{content:"\F93A"}.mdi-arrow-split-vertical:before{content:"\F93B"}.mdi-arrow-top-left:before{content:"\F05B"}.mdi-arrow-top-left-bold-outline:before{content:"\F9C2"}.mdi-arrow-top-left-thick:before{content:"\F9C3"}.mdi-arrow-top-right:before{content:"\F05C"}.mdi-arrow-top-right-bold-outline:before{content:"\F9C4"}.mdi-arrow-top-right-thick:before{content:"\F9C5"}.mdi-arrow-up:before{content:"\F05D"}.mdi-arrow-up-bold:before{content:"\F736"}.mdi-arrow-up-bold-box:before{content:"\F737"}.mdi-arrow-up-bold-box-outline:before{content:"\F738"}.mdi-arrow-up-bold-circle:before{content:"\F05F"}.mdi-arrow-up-bold-circle-outline:before{content:"\F060"}.mdi-arrow-up-bold-hexagon-outline:before{content:"\F061"}.mdi-arrow-up-bold-outline:before{content:"\F9C6"}.mdi-arrow-up-box:before{content:"\F6C2"}.mdi-arrow-up-circle:before{content:"\FCBD"}.mdi-arrow-up-circle-outline:before{content:"\FCBE"}.mdi-arrow-up-down-bold-outline:before{content:"\F9C7"}.mdi-arrow-up-drop-circle:before{content:"\F062"}.mdi-arrow-up-drop-circle-outline:before{content:"\F063"}.mdi-arrow-up-thick:before{content:"\F05E"}.mdi-artist:before{content:"\F802"}.mdi-artist-outline:before{content:"\FCC5"}.mdi-artstation:before{content:"\FB37"}.mdi-aspect-ratio:before{content:"\FA23"}.mdi-assistant:before{content:"\F064"}.mdi-asterisk:before{content:"\F6C3"}.mdi-at:before{content:"\F065"}.mdi-atlassian:before{content:"\F803"}.mdi-atm:before{content:"\FD23"}.mdi-atom:before{content:"\F767"}.mdi-attachment:before{content:"\F066"}.mdi-audio-video:before{content:"\F93C"}.mdi-audiobook:before{content:"\F067"}.mdi-augmented-reality:before{content:"\F84F"}.mdi-auto-fix:before{content:"\F068"}.mdi-auto-upload:before{content:"\F069"}.mdi-autorenew:before{content:"\F06A"}.mdi-av-timer:before{content:"\F06B"}.mdi-axe:before{content:"\F8C7"}.mdi-axis:before{content:"\FD24"}.mdi-axis-arrow:before{content:"\FD25"}.mdi-axis-arrow-lock:before{content:"\FD26"}.mdi-axis-lock:before{content:"\FD27"}.mdi-axis-x-arrow:before{content:"\FD28"}.mdi-axis-x-arrow-lock:before{content:"\FD29"}.mdi-axis-x-rotate-clockwise:before{content:"\FD2A"}.mdi-axis-x-rotate-counterclockwise:before{content:"\FD2B"}.mdi-axis-x-y-arrow-lock:before{content:"\FD2C"}.mdi-axis-y-arrow:before{content:"\FD2D"}.mdi-axis-y-arrow-lock:before{content:"\FD2E"}.mdi-axis-y-rotate-clockwise:before{content:"\FD2F"}.mdi-axis-y-rotate-counterclockwise:before{content:"\FD30"}.mdi-axis-z-arrow:before{content:"\FD31"}.mdi-axis-z-arrow-lock:before{content:"\FD32"}.mdi-axis-z-rotate-clockwise:before{content:"\FD33"}.mdi-axis-z-rotate-counterclockwise:before{content:"\FD34"}.mdi-azure:before{content:"\F804"}.mdi-babel:before{content:"\FA24"}.mdi-baby:before{content:"\F06C"}.mdi-baby-buggy:before{content:"\F68E"}.mdi-backburger:before{content:"\F06D"}.mdi-backspace:before{content:"\F06E"}.mdi-backspace-outline:before{content:"\FB38"}.mdi-backup-restore:before{content:"\F06F"}.mdi-badminton:before{content:"\F850"}.mdi-balloon:before{content:"\FA25"}.mdi-ballot:before{content:"\F9C8"}.mdi-ballot-outline:before{content:"\F9C9"}.mdi-ballot-recount:before{content:"\FC15"}.mdi-ballot-recount-outline:before{content:"\FC16"}.mdi-bandage:before{content:"\FD8B"}.mdi-bandcamp:before{content:"\F674"}.mdi-bank:before{content:"\F070"}.mdi-bank-minus:before{content:"\FD8C"}.mdi-bank-plus:before{content:"\FD8D"}.mdi-bank-remove:before{content:"\FD8E"}.mdi-bank-transfer:before{content:"\FA26"}.mdi-bank-transfer-in:before{content:"\FA27"}.mdi-bank-transfer-out:before{content:"\FA28"}.mdi-barcode:before{content:"\F071"}.mdi-barcode-scan:before{content:"\F072"}.mdi-barley:before{content:"\F073"}.mdi-barley-off:before{content:"\FB39"}.mdi-barn:before{content:"\FB3A"}.mdi-barrel:before{content:"\F074"}.mdi-baseball:before{content:"\F851"}.mdi-baseball-bat:before{content:"\F852"}.mdi-basecamp:before{content:"\F075"}.mdi-basket:before{content:"\F076"}.mdi-basket-fill:before{content:"\F077"}.mdi-basket-unfill:before{content:"\F078"}.mdi-basketball:before{content:"\F805"}.mdi-basketball-hoop:before{content:"\FC17"}.mdi-basketball-hoop-outline:before{content:"\FC18"}.mdi-bat:before{content:"\FB3B"}.mdi-battery:before{content:"\F079"}.mdi-battery-10:before{content:"\F07A"}.mdi-battery-10-bluetooth:before{content:"\F93D"}.mdi-battery-20:before{content:"\F07B"}.mdi-battery-20-bluetooth:before{content:"\F93E"}.mdi-battery-30:before{content:"\F07C"}.mdi-battery-30-bluetooth:before{content:"\F93F"}.mdi-battery-40:before{content:"\F07D"}.mdi-battery-40-bluetooth:before{content:"\F940"}.mdi-battery-50:before{content:"\F07E"}.mdi-battery-50-bluetooth:before{content:"\F941"}.mdi-battery-60:before{content:"\F07F"}.mdi-battery-60-bluetooth:before{content:"\F942"}.mdi-battery-70:before{content:"\F080"}.mdi-battery-70-bluetooth:before{content:"\F943"}.mdi-battery-80:before{content:"\F081"}.mdi-battery-80-bluetooth:before{content:"\F944"}.mdi-battery-90:before{content:"\F082"}.mdi-battery-90-bluetooth:before{content:"\F945"}.mdi-battery-alert:before{content:"\F083"}.mdi-battery-alert-bluetooth:before{content:"\F946"}.mdi-battery-bluetooth:before{content:"\F947"}.mdi-battery-bluetooth-variant:before{content:"\F948"}.mdi-battery-charging:before{content:"\F084"}.mdi-battery-charging-10:before{content:"\F89B"}.mdi-battery-charging-100:before{content:"\F085"}.mdi-battery-charging-20:before{content:"\F086"}.mdi-battery-charging-30:before{content:"\F087"}.mdi-battery-charging-40:before{content:"\F088"}.mdi-battery-charging-50:before{content:"\F89C"}.mdi-battery-charging-60:before{content:"\F089"}.mdi-battery-charging-70:before{content:"\F89D"}.mdi-battery-charging-80:before{content:"\F08A"}.mdi-battery-charging-90:before{content:"\F08B"}.mdi-battery-charging-outline:before{content:"\F89E"}.mdi-battery-charging-wireless:before{content:"\F806"}.mdi-battery-charging-wireless-10:before{content:"\F807"}.mdi-battery-charging-wireless-20:before{content:"\F808"}.mdi-battery-charging-wireless-30:before{content:"\F809"}.mdi-battery-charging-wireless-40:before{content:"\F80A"}.mdi-battery-charging-wireless-50:before{content:"\F80B"}.mdi-battery-charging-wireless-60:before{content:"\F80C"}.mdi-battery-charging-wireless-70:before{content:"\F80D"}.mdi-battery-charging-wireless-80:before{content:"\F80E"}.mdi-battery-charging-wireless-90:before{content:"\F80F"}.mdi-battery-charging-wireless-alert:before{content:"\F810"}.mdi-battery-charging-wireless-outline:before{content:"\F811"}.mdi-battery-minus:before{content:"\F08C"}.mdi-battery-negative:before{content:"\F08D"}.mdi-battery-outline:before{content:"\F08E"}.mdi-battery-plus:before{content:"\F08F"}.mdi-battery-positive:before{content:"\F090"}.mdi-battery-unknown:before{content:"\F091"}.mdi-battery-unknown-bluetooth:before{content:"\F949"}.mdi-battlenet:before{content:"\FB3C"}.mdi-beach:before{content:"\F092"}.mdi-beaker:before{content:"\FCC6"}.mdi-beaker-outline:before{content:"\F68F"}.mdi-beats:before{content:"\F097"}.mdi-bed-empty:before{content:"\F89F"}.mdi-beer:before{content:"\F098"}.mdi-behance:before{content:"\F099"}.mdi-bell:before{content:"\F09A"}.mdi-bell-alert:before{content:"\FD35"}.mdi-bell-circle:before{content:"\FD36"}.mdi-bell-circle-outline:before{content:"\FD37"}.mdi-bell-off:before{content:"\F09B"}.mdi-bell-off-outline:before{content:"\FA90"}.mdi-bell-outline:before{content:"\F09C"}.mdi-bell-plus:before{content:"\F09D"}.mdi-bell-plus-outline:before{content:"\FA91"}.mdi-bell-ring:before{content:"\F09E"}.mdi-bell-ring-outline:before{content:"\F09F"}.mdi-bell-sleep:before{content:"\F0A0"}.mdi-bell-sleep-outline:before{content:"\FA92"}.mdi-beta:before{content:"\F0A1"}.mdi-betamax:before{content:"\F9CA"}.mdi-bible:before{content:"\F0A2"}.mdi-bike:before{content:"\F0A3"}.mdi-billiards:before{content:"\FB3D"}.mdi-billiards-rack:before{content:"\FB3E"}.mdi-bing:before{content:"\F0A4"}.mdi-binoculars:before{content:"\F0A5"}.mdi-bio:before{content:"\F0A6"}.mdi-biohazard:before{content:"\F0A7"}.mdi-bitbucket:before{content:"\F0A8"}.mdi-bitcoin:before{content:"\F812"}.mdi-black-mesa:before{content:"\F0A9"}.mdi-blackberry:before{content:"\F0AA"}.mdi-blender:before{content:"\FCC7"}.mdi-blender-software:before{content:"\F0AB"}.mdi-blinds:before{content:"\F0AC"}.mdi-block-helper:before{content:"\F0AD"}.mdi-blogger:before{content:"\F0AE"}.mdi-blood-bag:before{content:"\FCC8"}.mdi-bluetooth:before{content:"\F0AF"}.mdi-bluetooth-audio:before{content:"\F0B0"}.mdi-bluetooth-connect:before{content:"\F0B1"}.mdi-bluetooth-off:before{content:"\F0B2"}.mdi-bluetooth-settings:before{content:"\F0B3"}.mdi-bluetooth-transfer:before{content:"\F0B4"}.mdi-blur:before{content:"\F0B5"}.mdi-blur-linear:before{content:"\F0B6"}.mdi-blur-off:before{content:"\F0B7"}.mdi-blur-radial:before{content:"\F0B8"}.mdi-bolnisi-cross:before{content:"\FCC9"}.mdi-bolt:before{content:"\FD8F"}.mdi-bomb:before{content:"\F690"}.mdi-bomb-off:before{content:"\F6C4"}.mdi-bone:before{content:"\F0B9"}.mdi-book:before{content:"\F0BA"}.mdi-book-lock:before{content:"\F799"}.mdi-book-lock-open:before{content:"\F79A"}.mdi-book-minus:before{content:"\F5D9"}.mdi-book-multiple:before{content:"\F0BB"}.mdi-book-multiple-minus:before{content:"\FA93"}.mdi-book-multiple-plus:before{content:"\FA94"}.mdi-book-multiple-remove:before{content:"\FA95"}.mdi-book-multiple-variant:before{content:"\F0BC"}.mdi-book-open:before{content:"\F0BD"}.mdi-book-open-outline:before{content:"\FB3F"}.mdi-book-open-page-variant:before{content:"\F5DA"}.mdi-book-open-variant:before{content:"\F0BE"}.mdi-book-outline:before{content:"\FB40"}.mdi-book-plus:before{content:"\F5DB"}.mdi-book-remove:before{content:"\FA96"}.mdi-book-variant:before{content:"\F0BF"}.mdi-bookmark:before{content:"\F0C0"}.mdi-bookmark-check:before{content:"\F0C1"}.mdi-bookmark-minus:before{content:"\F9CB"}.mdi-bookmark-minus-outline:before{content:"\F9CC"}.mdi-bookmark-music:before{content:"\F0C2"}.mdi-bookmark-off:before{content:"\F9CD"}.mdi-bookmark-off-outline:before{content:"\F9CE"}.mdi-bookmark-outline:before{content:"\F0C3"}.mdi-bookmark-plus:before{content:"\F0C5"}.mdi-bookmark-plus-outline:before{content:"\F0C4"}.mdi-bookmark-remove:before{content:"\F0C6"}.mdi-boombox:before{content:"\F5DC"}.mdi-bootstrap:before{content:"\F6C5"}.mdi-border-all:before{content:"\F0C7"}.mdi-border-all-variant:before{content:"\F8A0"}.mdi-border-bottom:before{content:"\F0C8"}.mdi-border-bottom-variant:before{content:"\F8A1"}.mdi-border-color:before{content:"\F0C9"}.mdi-border-horizontal:before{content:"\F0CA"}.mdi-border-inside:before{content:"\F0CB"}.mdi-border-left:before{content:"\F0CC"}.mdi-border-left-variant:before{content:"\F8A2"}.mdi-border-none:before{content:"\F0CD"}.mdi-border-none-variant:before{content:"\F8A3"}.mdi-border-outside:before{content:"\F0CE"}.mdi-border-right:before{content:"\F0CF"}.mdi-border-right-variant:before{content:"\F8A4"}.mdi-border-style:before{content:"\F0D0"}.mdi-border-top:before{content:"\F0D1"}.mdi-border-top-variant:before{content:"\F8A5"}.mdi-border-vertical:before{content:"\F0D2"}.mdi-bottle-wine:before{content:"\F853"}.mdi-bow-tie:before{content:"\F677"}.mdi-bowl:before{content:"\F617"}.mdi-bowling:before{content:"\F0D3"}.mdi-box:before{content:"\F0D4"}.mdi-box-cutter:before{content:"\F0D5"}.mdi-box-shadow:before{content:"\F637"}.mdi-boxing-glove:before{content:"\FB41"}.mdi-braille:before{content:"\F9CF"}.mdi-brain:before{content:"\F9D0"}.mdi-bread-slice:before{content:"\FCCA"}.mdi-bread-slice-outline:before{content:"\FCCB"}.mdi-bridge:before{content:"\F618"}.mdi-briefcase:before{content:"\F0D6"}.mdi-briefcase-account:before{content:"\FCCC"}.mdi-briefcase-account-outline:before{content:"\FCCD"}.mdi-briefcase-check:before{content:"\F0D7"}.mdi-briefcase-download:before{content:"\F0D8"}.mdi-briefcase-download-outline:before{content:"\FC19"}.mdi-briefcase-edit:before{content:"\FA97"}.mdi-briefcase-edit-outline:before{content:"\FC1A"}.mdi-briefcase-minus:before{content:"\FA29"}.mdi-briefcase-minus-outline:before{content:"\FC1B"}.mdi-briefcase-outline:before{content:"\F813"}.mdi-briefcase-plus:before{content:"\FA2A"}.mdi-briefcase-plus-outline:before{content:"\FC1C"}.mdi-briefcase-remove:before{content:"\FA2B"}.mdi-briefcase-remove-outline:before{content:"\FC1D"}.mdi-briefcase-search:before{content:"\FA2C"}.mdi-briefcase-search-outline:before{content:"\FC1E"}.mdi-briefcase-upload:before{content:"\F0D9"}.mdi-briefcase-upload-outline:before{content:"\FC1F"}.mdi-brightness-1:before{content:"\F0DA"}.mdi-brightness-2:before{content:"\F0DB"}.mdi-brightness-3:before{content:"\F0DC"}.mdi-brightness-4:before{content:"\F0DD"}.mdi-brightness-5:before{content:"\F0DE"}.mdi-brightness-6:before{content:"\F0DF"}.mdi-brightness-7:before{content:"\F0E0"}.mdi-brightness-auto:before{content:"\F0E1"}.mdi-brightness-percent:before{content:"\FCCE"}.mdi-broom:before{content:"\F0E2"}.mdi-brush:before{content:"\F0E3"}.mdi-buddhism:before{content:"\F94A"}.mdi-buffer:before{content:"\F619"}.mdi-bug:before{content:"\F0E4"}.mdi-bug-check:before{content:"\FA2D"}.mdi-bug-check-outline:before{content:"\FA2E"}.mdi-bug-outline:before{content:"\FA2F"}.mdi-bugle:before{content:"\FD90"}.mdi-bulldozer:before{content:"\FB07"}.mdi-bullet:before{content:"\FCCF"}.mdi-bulletin-board:before{content:"\F0E5"}.mdi-bullhorn:before{content:"\F0E6"}.mdi-bullhorn-outline:before{content:"\FB08"}.mdi-bullseye:before{content:"\F5DD"}.mdi-bullseye-arrow:before{content:"\F8C8"}.mdi-bus:before{content:"\F0E7"}.mdi-bus-alert:before{content:"\FA98"}.mdi-bus-articulated-end:before{content:"\F79B"}.mdi-bus-articulated-front:before{content:"\F79C"}.mdi-bus-clock:before{content:"\F8C9"}.mdi-bus-double-decker:before{content:"\F79D"}.mdi-bus-school:before{content:"\F79E"}.mdi-bus-side:before{content:"\F79F"}.mdi-cached:before{content:"\F0E8"}.mdi-cactus:before{content:"\FD91"}.mdi-cake:before{content:"\F0E9"}.mdi-cake-layered:before{content:"\F0EA"}.mdi-cake-variant:before{content:"\F0EB"}.mdi-calculator:before{content:"\F0EC"}.mdi-calculator-variant:before{content:"\FA99"}.mdi-calendar:before{content:"\F0ED"}.mdi-calendar-alert:before{content:"\FA30"}.mdi-calendar-blank:before{content:"\F0EE"}.mdi-calendar-blank-outline:before{content:"\FB42"}.mdi-calendar-check:before{content:"\F0EF"}.mdi-calendar-check-outline:before{content:"\FC20"}.mdi-calendar-clock:before{content:"\F0F0"}.mdi-calendar-edit:before{content:"\F8A6"}.mdi-calendar-export:before{content:"\FB09"}.mdi-calendar-heart:before{content:"\F9D1"}.mdi-calendar-import:before{content:"\FB0A"}.mdi-calendar-minus:before{content:"\FD38"}.mdi-calendar-multiple:before{content:"\F0F1"}.mdi-calendar-multiple-check:before{content:"\F0F2"}.mdi-calendar-multiselect:before{content:"\FA31"}.mdi-calendar-outline:before{content:"\FB43"}.mdi-calendar-plus:before{content:"\F0F3"}.mdi-calendar-question:before{content:"\F691"}.mdi-calendar-range:before{content:"\F678"}.mdi-calendar-range-outline:before{content:"\FB44"}.mdi-calendar-remove:before{content:"\F0F4"}.mdi-calendar-remove-outline:before{content:"\FC21"}.mdi-calendar-search:before{content:"\F94B"}.mdi-calendar-star:before{content:"\F9D2"}.mdi-calendar-text:before{content:"\F0F5"}.mdi-calendar-text-outline:before{content:"\FC22"}.mdi-calendar-today:before{content:"\F0F6"}.mdi-calendar-week:before{content:"\FA32"}.mdi-calendar-week-begin:before{content:"\FA33"}.mdi-call-made:before{content:"\F0F7"}.mdi-call-merge:before{content:"\F0F8"}.mdi-call-missed:before{content:"\F0F9"}.mdi-call-received:before{content:"\F0FA"}.mdi-call-split:before{content:"\F0FB"}.mdi-camcorder:before{content:"\F0FC"}.mdi-camcorder-box:before{content:"\F0FD"}.mdi-camcorder-box-off:before{content:"\F0FE"}.mdi-camcorder-off:before{content:"\F0FF"}.mdi-camera:before{content:"\F100"}.mdi-camera-account:before{content:"\F8CA"}.mdi-camera-burst:before{content:"\F692"}.mdi-camera-control:before{content:"\FB45"}.mdi-camera-enhance:before{content:"\F101"}.mdi-camera-enhance-outline:before{content:"\FB46"}.mdi-camera-front:before{content:"\F102"}.mdi-camera-front-variant:before{content:"\F103"}.mdi-camera-gopro:before{content:"\F7A0"}.mdi-camera-image:before{content:"\F8CB"}.mdi-camera-iris:before{content:"\F104"}.mdi-camera-metering-center:before{content:"\F7A1"}.mdi-camera-metering-matrix:before{content:"\F7A2"}.mdi-camera-metering-partial:before{content:"\F7A3"}.mdi-camera-metering-spot:before{content:"\F7A4"}.mdi-camera-off:before{content:"\F5DF"}.mdi-camera-outline:before{content:"\FD39"}.mdi-camera-party-mode:before{content:"\F105"}.mdi-camera-rear:before{content:"\F106"}.mdi-camera-rear-variant:before{content:"\F107"}.mdi-camera-switch:before{content:"\F108"}.mdi-camera-timer:before{content:"\F109"}.mdi-camera-wireless:before{content:"\FD92"}.mdi-camera-wireless-outline:before{content:"\FD93"}.mdi-cancel:before{content:"\F739"}.mdi-candle:before{content:"\F5E2"}.mdi-candycane:before{content:"\F10A"}.mdi-cannabis:before{content:"\F7A5"}.mdi-caps-lock:before{content:"\FA9A"}.mdi-car:before{content:"\F10B"}.mdi-car-battery:before{content:"\F10C"}.mdi-car-brake-abs:before{content:"\FC23"}.mdi-car-brake-alert:before{content:"\FC24"}.mdi-car-brake-hold:before{content:"\FD3A"}.mdi-car-brake-parking:before{content:"\FD3B"}.mdi-car-connected:before{content:"\F10D"}.mdi-car-convertible:before{content:"\F7A6"}.mdi-car-cruise-control:before{content:"\FD3C"}.mdi-car-defrost-front:before{content:"\FD3D"}.mdi-car-defrost-rear:before{content:"\FD3E"}.mdi-car-door:before{content:"\FB47"}.mdi-car-electric:before{content:"\FB48"}.mdi-car-esp:before{content:"\FC25"}.mdi-car-estate:before{content:"\F7A7"}.mdi-car-hatchback:before{content:"\F7A8"}.mdi-car-key:before{content:"\FB49"}.mdi-car-light-dimmed:before{content:"\FC26"}.mdi-car-light-fog:before{content:"\FC27"}.mdi-car-light-high:before{content:"\FC28"}.mdi-car-limousine:before{content:"\F8CC"}.mdi-car-multiple:before{content:"\FB4A"}.mdi-car-parking-lights:before{content:"\FD3F"}.mdi-car-pickup:before{content:"\F7A9"}.mdi-car-side:before{content:"\F7AA"}.mdi-car-sports:before{content:"\F7AB"}.mdi-car-tire-alert:before{content:"\FC29"}.mdi-car-traction-control:before{content:"\FD40"}.mdi-car-wash:before{content:"\F10E"}.mdi-caravan:before{content:"\F7AC"}.mdi-card:before{content:"\FB4B"}.mdi-card-bulleted:before{content:"\FB4C"}.mdi-card-bulleted-off:before{content:"\FB4D"}.mdi-card-bulleted-off-outline:before{content:"\FB4E"}.mdi-card-bulleted-outline:before{content:"\FB4F"}.mdi-card-bulleted-settings:before{content:"\FB50"}.mdi-card-bulleted-settings-outline:before{content:"\FB51"}.mdi-card-outline:before{content:"\FB52"}.mdi-card-text:before{content:"\FB53"}.mdi-card-text-outline:before{content:"\FB54"}.mdi-cards:before{content:"\F638"}.mdi-cards-club:before{content:"\F8CD"}.mdi-cards-diamond:before{content:"\F8CE"}.mdi-cards-heart:before{content:"\F8CF"}.mdi-cards-outline:before{content:"\F639"}.mdi-cards-playing-outline:before{content:"\F63A"}.mdi-cards-spade:before{content:"\F8D0"}.mdi-cards-variant:before{content:"\F6C6"}.mdi-carrot:before{content:"\F10F"}.mdi-carry-on-bag-check:before{content:"\FD41"}.mdi-cart:before{content:"\F110"}.mdi-cart-arrow-down:before{content:"\FD42"}.mdi-cart-arrow-right:before{content:"\FC2A"}.mdi-cart-arrow-up:before{content:"\FD43"}.mdi-cart-minus:before{content:"\FD44"}.mdi-cart-off:before{content:"\F66B"}.mdi-cart-outline:before{content:"\F111"}.mdi-cart-plus:before{content:"\F112"}.mdi-cart-remove:before{content:"\FD45"}.mdi-case-sensitive-alt:before{content:"\F113"}.mdi-cash:before{content:"\F114"}.mdi-cash-100:before{content:"\F115"}.mdi-cash-marker:before{content:"\FD94"}.mdi-cash-multiple:before{content:"\F116"}.mdi-cash-refund:before{content:"\FA9B"}.mdi-cash-register:before{content:"\FCD0"}.mdi-cash-usd:before{content:"\F117"}.mdi-cassette:before{content:"\F9D3"}.mdi-cast:before{content:"\F118"}.mdi-cast-connected:before{content:"\F119"}.mdi-cast-off:before{content:"\F789"}.mdi-castle:before{content:"\F11A"}.mdi-cat:before{content:"\F11B"}.mdi-cctv:before{content:"\F7AD"}.mdi-ceiling-light:before{content:"\F768"}.mdi-cellphone:before{content:"\F11C"}.mdi-cellphone-android:before{content:"\F11D"}.mdi-cellphone-arrow-down:before{content:"\F9D4"}.mdi-cellphone-basic:before{content:"\F11E"}.mdi-cellphone-dock:before{content:"\F11F"}.mdi-cellphone-erase:before{content:"\F94C"}.mdi-cellphone-iphone:before{content:"\F120"}.mdi-cellphone-key:before{content:"\F94D"}.mdi-cellphone-link:before{content:"\F121"}.mdi-cellphone-link-off:before{content:"\F122"}.mdi-cellphone-lock:before{content:"\F94E"}.mdi-cellphone-message:before{content:"\F8D2"}.mdi-cellphone-off:before{content:"\F94F"}.mdi-cellphone-screenshot:before{content:"\FA34"}.mdi-cellphone-settings:before{content:"\F123"}.mdi-cellphone-settings-variant:before{content:"\F950"}.mdi-cellphone-sound:before{content:"\F951"}.mdi-cellphone-text:before{content:"\F8D1"}.mdi-cellphone-wireless:before{content:"\F814"}.mdi-celtic-cross:before{content:"\FCD1"}.mdi-certificate:before{content:"\F124"}.mdi-chair-school:before{content:"\F125"}.mdi-charity:before{content:"\FC2B"}.mdi-chart-arc:before{content:"\F126"}.mdi-chart-areaspline:before{content:"\F127"}.mdi-chart-bar:before{content:"\F128"}.mdi-chart-bar-stacked:before{content:"\F769"}.mdi-chart-bell-curve:before{content:"\FC2C"}.mdi-chart-bubble:before{content:"\F5E3"}.mdi-chart-donut:before{content:"\F7AE"}.mdi-chart-donut-variant:before{content:"\F7AF"}.mdi-chart-gantt:before{content:"\F66C"}.mdi-chart-histogram:before{content:"\F129"}.mdi-chart-line:before{content:"\F12A"}.mdi-chart-line-stacked:before{content:"\F76A"}.mdi-chart-line-variant:before{content:"\F7B0"}.mdi-chart-multiline:before{content:"\F8D3"}.mdi-chart-pie:before{content:"\F12B"}.mdi-chart-scatterplot-hexbin:before{content:"\F66D"}.mdi-chart-timeline:before{content:"\F66E"}.mdi-chat:before{content:"\FB55"}.mdi-chat-alert:before{content:"\FB56"}.mdi-chat-processing:before{content:"\FB57"}.mdi-check:before{content:"\F12C"}.mdi-check-all:before{content:"\F12D"}.mdi-check-box-multiple-outline:before{content:"\FC2D"}.mdi-check-box-outline:before{content:"\FC2E"}.mdi-check-circle:before{content:"\F5E0"}.mdi-check-circle-outline:before{content:"\F5E1"}.mdi-check-decagram:before{content:"\F790"}.mdi-check-network:before{content:"\FC2F"}.mdi-check-network-outline:before{content:"\FC30"}.mdi-check-outline:before{content:"\F854"}.mdi-checkbook:before{content:"\FA9C"}.mdi-checkbox-blank:before{content:"\F12E"}.mdi-checkbox-blank-circle:before{content:"\F12F"}.mdi-checkbox-blank-circle-outline:before{content:"\F130"}.mdi-checkbox-blank-outline:before{content:"\F131"}.mdi-checkbox-intermediate:before{content:"\F855"}.mdi-checkbox-marked:before{content:"\F132"}.mdi-checkbox-marked-circle:before{content:"\F133"}.mdi-checkbox-marked-circle-outline:before{content:"\F134"}.mdi-checkbox-marked-outline:before{content:"\F135"}.mdi-checkbox-multiple-blank:before{content:"\F136"}.mdi-checkbox-multiple-blank-circle:before{content:"\F63B"}.mdi-checkbox-multiple-blank-circle-outline:before{content:"\F63C"}.mdi-checkbox-multiple-blank-outline:before{content:"\F137"}.mdi-checkbox-multiple-marked:before{content:"\F138"}.mdi-checkbox-multiple-marked-circle:before{content:"\F63D"}.mdi-checkbox-multiple-marked-circle-outline:before{content:"\F63E"}.mdi-checkbox-multiple-marked-outline:before{content:"\F139"}.mdi-checkerboard:before{content:"\F13A"}.mdi-chef-hat:before{content:"\FB58"}.mdi-chemical-weapon:before{content:"\F13B"}.mdi-chess-bishop:before{content:"\F85B"}.mdi-chess-king:before{content:"\F856"}.mdi-chess-knight:before{content:"\F857"}.mdi-chess-pawn:before{content:"\F858"}.mdi-chess-queen:before{content:"\F859"}.mdi-chess-rook:before{content:"\F85A"}.mdi-chevron-double-down:before{content:"\F13C"}.mdi-chevron-double-left:before{content:"\F13D"}.mdi-chevron-double-right:before{content:"\F13E"}.mdi-chevron-double-up:before{content:"\F13F"}.mdi-chevron-down:before{content:"\F140"}.mdi-chevron-down-box:before{content:"\F9D5"}.mdi-chevron-down-box-outline:before{content:"\F9D6"}.mdi-chevron-down-circle:before{content:"\FB0B"}.mdi-chevron-down-circle-outline:before{content:"\FB0C"}.mdi-chevron-left:before{content:"\F141"}.mdi-chevron-left-box:before{content:"\F9D7"}.mdi-chevron-left-box-outline:before{content:"\F9D8"}.mdi-chevron-left-circle:before{content:"\FB0D"}.mdi-chevron-left-circle-outline:before{content:"\FB0E"}.mdi-chevron-right:before{content:"\F142"}.mdi-chevron-right-box:before{content:"\F9D9"}.mdi-chevron-right-box-outline:before{content:"\F9DA"}.mdi-chevron-right-circle:before{content:"\FB0F"}.mdi-chevron-right-circle-outline:before{content:"\FB10"}.mdi-chevron-triple-down:before{content:"\FD95"}.mdi-chevron-triple-left:before{content:"\FD96"}.mdi-chevron-triple-right:before{content:"\FD97"}.mdi-chevron-triple-up:before{content:"\FD98"}.mdi-chevron-up:before{content:"\F143"}.mdi-chevron-up-box:before{content:"\F9DB"}.mdi-chevron-up-box-outline:before{content:"\F9DC"}.mdi-chevron-up-circle:before{content:"\FB11"}.mdi-chevron-up-circle-outline:before{content:"\FB12"}.mdi-chili-hot:before{content:"\F7B1"}.mdi-chili-medium:before{content:"\F7B2"}.mdi-chili-mild:before{content:"\F7B3"}.mdi-chip:before{content:"\F61A"}.mdi-christianity:before{content:"\F952"}.mdi-christianity-outline:before{content:"\FCD2"}.mdi-church:before{content:"\F144"}.mdi-circle:before{content:"\F764"}.mdi-circle-edit-outline:before{content:"\F8D4"}.mdi-circle-medium:before{content:"\F9DD"}.mdi-circle-outline:before{content:"\F765"}.mdi-circle-slice-1:before{content:"\FA9D"}.mdi-circle-slice-2:before{content:"\FA9E"}.mdi-circle-slice-3:before{content:"\FA9F"}.mdi-circle-slice-4:before{content:"\FAA0"}.mdi-circle-slice-5:before{content:"\FAA1"}.mdi-circle-slice-6:before{content:"\FAA2"}.mdi-circle-slice-7:before{content:"\FAA3"}.mdi-circle-slice-8:before{content:"\FAA4"}.mdi-circle-small:before{content:"\F9DE"}.mdi-cisco-webex:before{content:"\F145"}.mdi-city:before{content:"\F146"}.mdi-city-variant:before{content:"\FA35"}.mdi-city-variant-outline:before{content:"\FA36"}.mdi-clipboard:before{content:"\F147"}.mdi-clipboard-account:before{content:"\F148"}.mdi-clipboard-account-outline:before{content:"\FC31"}.mdi-clipboard-alert:before{content:"\F149"}.mdi-clipboard-alert-outline:before{content:"\FCD3"}.mdi-clipboard-arrow-down:before{content:"\F14A"}.mdi-clipboard-arrow-down-outline:before{content:"\FC32"}.mdi-clipboard-arrow-left:before{content:"\F14B"}.mdi-clipboard-arrow-left-outline:before{content:"\FCD4"}.mdi-clipboard-arrow-right:before{content:"\FCD5"}.mdi-clipboard-arrow-right-outline:before{content:"\FCD6"}.mdi-clipboard-arrow-up:before{content:"\FC33"}.mdi-clipboard-arrow-up-outline:before{content:"\FC34"}.mdi-clipboard-check:before{content:"\F14C"}.mdi-clipboard-check-outline:before{content:"\F8A7"}.mdi-clipboard-flow:before{content:"\F6C7"}.mdi-clipboard-outline:before{content:"\F14D"}.mdi-clipboard-play:before{content:"\FC35"}.mdi-clipboard-play-outline:before{content:"\FC36"}.mdi-clipboard-plus:before{content:"\F750"}.mdi-clipboard-pulse:before{content:"\F85C"}.mdi-clipboard-pulse-outline:before{content:"\F85D"}.mdi-clipboard-text:before{content:"\F14E"}.mdi-clipboard-text-outline:before{content:"\FA37"}.mdi-clipboard-text-play:before{content:"\FC37"}.mdi-clipboard-text-play-outline:before{content:"\FC38"}.mdi-clippy:before{content:"\F14F"}.mdi-clock:before{content:"\F953"}.mdi-clock-alert:before{content:"\F954"}.mdi-clock-alert-outline:before{content:"\F5CE"}.mdi-clock-end:before{content:"\F151"}.mdi-clock-fast:before{content:"\F152"}.mdi-clock-in:before{content:"\F153"}.mdi-clock-out:before{content:"\F154"}.mdi-clock-outline:before{content:"\F150"}.mdi-clock-start:before{content:"\F155"}.mdi-close:before{content:"\F156"}.mdi-close-box:before{content:"\F157"}.mdi-close-box-multiple:before{content:"\FC39"}.mdi-close-box-multiple-outline:before{content:"\FC3A"}.mdi-close-box-outline:before{content:"\F158"}.mdi-close-circle:before{content:"\F159"}.mdi-close-circle-outline:before{content:"\F15A"}.mdi-close-network:before{content:"\F15B"}.mdi-close-network-outline:before{content:"\FC3B"}.mdi-close-octagon:before{content:"\F15C"}.mdi-close-octagon-outline:before{content:"\F15D"}.mdi-close-outline:before{content:"\F6C8"}.mdi-closed-caption:before{content:"\F15E"}.mdi-closed-caption-outline:before{content:"\FD99"}.mdi-cloud:before{content:"\F15F"}.mdi-cloud-alert:before{content:"\F9DF"}.mdi-cloud-braces:before{content:"\F7B4"}.mdi-cloud-check:before{content:"\F160"}.mdi-cloud-circle:before{content:"\F161"}.mdi-cloud-download:before{content:"\F162"}.mdi-cloud-download-outline:before{content:"\FB59"}.mdi-cloud-off-outline:before{content:"\F164"}.mdi-cloud-outline:before{content:"\F163"}.mdi-cloud-print:before{content:"\F165"}.mdi-cloud-print-outline:before{content:"\F166"}.mdi-cloud-question:before{content:"\FA38"}.mdi-cloud-search:before{content:"\F955"}.mdi-cloud-search-outline:before{content:"\F956"}.mdi-cloud-sync:before{content:"\F63F"}.mdi-cloud-tags:before{content:"\F7B5"}.mdi-cloud-upload:before{content:"\F167"}.mdi-cloud-upload-outline:before{content:"\FB5A"}.mdi-clover:before{content:"\F815"}.mdi-code-array:before{content:"\F168"}.mdi-code-braces:before{content:"\F169"}.mdi-code-brackets:before{content:"\F16A"}.mdi-code-equal:before{content:"\F16B"}.mdi-code-greater-than:before{content:"\F16C"}.mdi-code-greater-than-or-equal:before{content:"\F16D"}.mdi-code-less-than:before{content:"\F16E"}.mdi-code-less-than-or-equal:before{content:"\F16F"}.mdi-code-not-equal:before{content:"\F170"}.mdi-code-not-equal-variant:before{content:"\F171"}.mdi-code-parentheses:before{content:"\F172"}.mdi-code-string:before{content:"\F173"}.mdi-code-tags:before{content:"\F174"}.mdi-code-tags-check:before{content:"\F693"}.mdi-codepen:before{content:"\F175"}.mdi-coffee:before{content:"\F176"}.mdi-coffee-outline:before{content:"\F6C9"}.mdi-coffee-to-go:before{content:"\F177"}.mdi-coffin:before{content:"\FB5B"}.mdi-cogs:before{content:"\F8D5"}.mdi-coin:before{content:"\F178"}.mdi-coins:before{content:"\F694"}.mdi-collage:before{content:"\F640"}.mdi-collapse-all:before{content:"\FAA5"}.mdi-collapse-all-outline:before{content:"\FAA6"}.mdi-color-helper:before{content:"\F179"}.mdi-comment:before{content:"\F17A"}.mdi-comment-account:before{content:"\F17B"}.mdi-comment-account-outline:before{content:"\F17C"}.mdi-comment-alert:before{content:"\F17D"}.mdi-comment-alert-outline:before{content:"\F17E"}.mdi-comment-arrow-left:before{content:"\F9E0"}.mdi-comment-arrow-left-outline:before{content:"\F9E1"}.mdi-comment-arrow-right:before{content:"\F9E2"}.mdi-comment-arrow-right-outline:before{content:"\F9E3"}.mdi-comment-check:before{content:"\F17F"}.mdi-comment-check-outline:before{content:"\F180"}.mdi-comment-eye:before{content:"\FA39"}.mdi-comment-eye-outline:before{content:"\FA3A"}.mdi-comment-multiple:before{content:"\F85E"}.mdi-comment-multiple-outline:before{content:"\F181"}.mdi-comment-outline:before{content:"\F182"}.mdi-comment-plus:before{content:"\F9E4"}.mdi-comment-plus-outline:before{content:"\F183"}.mdi-comment-processing:before{content:"\F184"}.mdi-comment-processing-outline:before{content:"\F185"}.mdi-comment-question:before{content:"\F816"}.mdi-comment-question-outline:before{content:"\F186"}.mdi-comment-remove:before{content:"\F5DE"}.mdi-comment-remove-outline:before{content:"\F187"}.mdi-comment-search:before{content:"\FA3B"}.mdi-comment-search-outline:before{content:"\FA3C"}.mdi-comment-text:before{content:"\F188"}.mdi-comment-text-multiple:before{content:"\F85F"}.mdi-comment-text-multiple-outline:before{content:"\F860"}.mdi-comment-text-outline:before{content:"\F189"}.mdi-compare:before{content:"\F18A"}.mdi-compass:before{content:"\F18B"}.mdi-compass-off:before{content:"\FB5C"}.mdi-compass-off-outline:before{content:"\FB5D"}.mdi-compass-outline:before{content:"\F18C"}.mdi-console:before{content:"\F18D"}.mdi-console-line:before{content:"\F7B6"}.mdi-console-network:before{content:"\F8A8"}.mdi-console-network-outline:before{content:"\FC3C"}.mdi-contact-mail:before{content:"\F18E"}.mdi-contactless-payment:before{content:"\FD46"}.mdi-contacts:before{content:"\F6CA"}.mdi-contain:before{content:"\FA3D"}.mdi-contain-end:before{content:"\FA3E"}.mdi-contain-start:before{content:"\FA3F"}.mdi-content-copy:before{content:"\F18F"}.mdi-content-cut:before{content:"\F190"}.mdi-content-duplicate:before{content:"\F191"}.mdi-content-paste:before{content:"\F192"}.mdi-content-save:before{content:"\F193"}.mdi-content-save-all:before{content:"\F194"}.mdi-content-save-edit:before{content:"\FCD7"}.mdi-content-save-edit-outline:before{content:"\FCD8"}.mdi-content-save-outline:before{content:"\F817"}.mdi-content-save-settings:before{content:"\F61B"}.mdi-content-save-settings-outline:before{content:"\FB13"}.mdi-contrast:before{content:"\F195"}.mdi-contrast-box:before{content:"\F196"}.mdi-contrast-circle:before{content:"\F197"}.mdi-controller-classic:before{content:"\FB5E"}.mdi-controller-classic-outline:before{content:"\FB5F"}.mdi-cookie:before{content:"\F198"}.mdi-copyright:before{content:"\F5E6"}.mdi-cordova:before{content:"\F957"}.mdi-corn:before{content:"\F7B7"}.mdi-counter:before{content:"\F199"}.mdi-cow:before{content:"\F19A"}.mdi-crane:before{content:"\F861"}.mdi-creation:before{content:"\F1C9"}.mdi-creative-commons:before{content:"\FD47"}.mdi-credit-card:before{content:"\F19B"}.mdi-credit-card-marker:before{content:"\FD9A"}.mdi-credit-card-multiple:before{content:"\F19C"}.mdi-credit-card-off:before{content:"\F5E4"}.mdi-credit-card-plus:before{content:"\F675"}.mdi-credit-card-refund:before{content:"\FAA7"}.mdi-credit-card-scan:before{content:"\F19D"}.mdi-credit-card-settings:before{content:"\F8D6"}.mdi-credit-card-wireless:before{content:"\FD48"}.mdi-cricket:before{content:"\FD49"}.mdi-crop:before{content:"\F19E"}.mdi-crop-free:before{content:"\F19F"}.mdi-crop-landscape:before{content:"\F1A0"}.mdi-crop-portrait:before{content:"\F1A1"}.mdi-crop-rotate:before{content:"\F695"}.mdi-crop-square:before{content:"\F1A2"}.mdi-crosshairs:before{content:"\F1A3"}.mdi-crosshairs-gps:before{content:"\F1A4"}.mdi-crown:before{content:"\F1A5"}.mdi-cryengine:before{content:"\F958"}.mdi-crystal-ball:before{content:"\FB14"}.mdi-cube:before{content:"\F1A6"}.mdi-cube-outline:before{content:"\F1A7"}.mdi-cube-scan:before{content:"\FB60"}.mdi-cube-send:before{content:"\F1A8"}.mdi-cube-unfolded:before{content:"\F1A9"}.mdi-cup:before{content:"\F1AA"}.mdi-cup-off:before{content:"\F5E5"}.mdi-cup-water:before{content:"\F1AB"}.mdi-cupcake:before{content:"\F959"}.mdi-curling:before{content:"\F862"}.mdi-currency-bdt:before{content:"\F863"}.mdi-currency-brl:before{content:"\FB61"}.mdi-currency-btc:before{content:"\F1AC"}.mdi-currency-chf:before{content:"\F7B8"}.mdi-currency-cny:before{content:"\F7B9"}.mdi-currency-eth:before{content:"\F7BA"}.mdi-currency-eur:before{content:"\F1AD"}.mdi-currency-gbp:before{content:"\F1AE"}.mdi-currency-ils:before{content:"\FC3D"}.mdi-currency-inr:before{content:"\F1AF"}.mdi-currency-jpy:before{content:"\F7BB"}.mdi-currency-krw:before{content:"\F7BC"}.mdi-currency-kzt:before{content:"\F864"}.mdi-currency-ngn:before{content:"\F1B0"}.mdi-currency-php:before{content:"\F9E5"}.mdi-currency-rub:before{content:"\F1B1"}.mdi-currency-sign:before{content:"\F7BD"}.mdi-currency-try:before{content:"\F1B2"}.mdi-currency-twd:before{content:"\F7BE"}.mdi-currency-usd:before{content:"\F1B3"}.mdi-currency-usd-off:before{content:"\F679"}.mdi-current-ac:before{content:"\F95A"}.mdi-current-dc:before{content:"\F95B"}.mdi-cursor-default:before{content:"\F1B4"}.mdi-cursor-default-click:before{content:"\FCD9"}.mdi-cursor-default-click-outline:before{content:"\FCDA"}.mdi-cursor-default-outline:before{content:"\F1B5"}.mdi-cursor-move:before{content:"\F1B6"}.mdi-cursor-pointer:before{content:"\F1B7"}.mdi-cursor-text:before{content:"\F5E7"}.mdi-database:before{content:"\F1B8"}.mdi-database-check:before{content:"\FAA8"}.mdi-database-edit:before{content:"\FB62"}.mdi-database-export:before{content:"\F95D"}.mdi-database-import:before{content:"\F95C"}.mdi-database-lock:before{content:"\FAA9"}.mdi-database-minus:before{content:"\F1B9"}.mdi-database-plus:before{content:"\F1BA"}.mdi-database-refresh:before{content:"\FCDB"}.mdi-database-remove:before{content:"\FCDC"}.mdi-database-search:before{content:"\F865"}.mdi-database-settings:before{content:"\FCDD"}.mdi-death-star:before{content:"\F8D7"}.mdi-death-star-variant:before{content:"\F8D8"}.mdi-deathly-hallows:before{content:"\FB63"}.mdi-debian:before{content:"\F8D9"}.mdi-debug-step-into:before{content:"\F1BB"}.mdi-debug-step-out:before{content:"\F1BC"}.mdi-debug-step-over:before{content:"\F1BD"}.mdi-decagram:before{content:"\F76B"}.mdi-decagram-outline:before{content:"\F76C"}.mdi-decimal-decrease:before{content:"\F1BE"}.mdi-decimal-increase:before{content:"\F1BF"}.mdi-delete:before{content:"\F1C0"}.mdi-delete-circle:before{content:"\F682"}.mdi-delete-circle-outline:before{content:"\FB64"}.mdi-delete-empty:before{content:"\F6CB"}.mdi-delete-forever:before{content:"\F5E8"}.mdi-delete-forever-outline:before{content:"\FB65"}.mdi-delete-outline:before{content:"\F9E6"}.mdi-delete-restore:before{content:"\F818"}.mdi-delete-sweep:before{content:"\F5E9"}.mdi-delete-sweep-outline:before{content:"\FC3E"}.mdi-delete-variant:before{content:"\F1C1"}.mdi-delta:before{content:"\F1C2"}.mdi-desk-lamp:before{content:"\F95E"}.mdi-deskphone:before{content:"\F1C3"}.mdi-desktop-classic:before{content:"\F7BF"}.mdi-desktop-mac:before{content:"\F1C4"}.mdi-desktop-mac-dashboard:before{content:"\F9E7"}.mdi-desktop-tower:before{content:"\F1C5"}.mdi-desktop-tower-monitor:before{content:"\FAAA"}.mdi-details:before{content:"\F1C6"}.mdi-dev-to:before{content:"\FD4A"}.mdi-developer-board:before{content:"\F696"}.mdi-deviantart:before{content:"\F1C7"}.mdi-dialpad:before{content:"\F61C"}.mdi-diameter:before{content:"\FC3F"}.mdi-diameter-outline:before{content:"\FC40"}.mdi-diameter-variant:before{content:"\FC41"}.mdi-diamond:before{content:"\FB66"}.mdi-diamond-outline:before{content:"\FB67"}.mdi-diamond-stone:before{content:"\F1C8"}.mdi-dice-1:before{content:"\F1CA"}.mdi-dice-2:before{content:"\F1CB"}.mdi-dice-3:before{content:"\F1CC"}.mdi-dice-4:before{content:"\F1CD"}.mdi-dice-5:before{content:"\F1CE"}.mdi-dice-6:before{content:"\F1CF"}.mdi-dice-d10:before{content:"\F76E"}.mdi-dice-d12:before{content:"\F866"}.mdi-dice-d20:before{content:"\F5EA"}.mdi-dice-d4:before{content:"\F5EB"}.mdi-dice-d6:before{content:"\F5EC"}.mdi-dice-d8:before{content:"\F5ED"}.mdi-dice-multiple:before{content:"\F76D"}.mdi-dictionary:before{content:"\F61D"}.mdi-dip-switch:before{content:"\F7C0"}.mdi-directions:before{content:"\F1D0"}.mdi-directions-fork:before{content:"\F641"}.mdi-disc:before{content:"\F5EE"}.mdi-disc-alert:before{content:"\F1D1"}.mdi-disc-player:before{content:"\F95F"}.mdi-discord:before{content:"\F66F"}.mdi-dishwasher:before{content:"\FAAB"}.mdi-disqus:before{content:"\F1D2"}.mdi-disqus-outline:before{content:"\F1D3"}.mdi-diving-flippers:before{content:"\FD9B"}.mdi-diving-helmet:before{content:"\FD9C"}.mdi-diving-scuba:before{content:"\FD9D"}.mdi-diving-scuba-flag:before{content:"\FD9E"}.mdi-diving-scuba-tank:before{content:"\FD9F"}.mdi-diving-scuba-tank-multiple:before{content:"\FDA0"}.mdi-diving-snorkel:before{content:"\FDA1"}.mdi-division:before{content:"\F1D4"}.mdi-division-box:before{content:"\F1D5"}.mdi-dlna:before{content:"\FA40"}.mdi-dna:before{content:"\F683"}.mdi-dns:before{content:"\F1D6"}.mdi-dns-outline:before{content:"\FB68"}.mdi-do-not-disturb:before{content:"\F697"}.mdi-do-not-disturb-off:before{content:"\F698"}.mdi-docker:before{content:"\F867"}.mdi-doctor:before{content:"\FA41"}.mdi-dog:before{content:"\FA42"}.mdi-dog-service:before{content:"\FAAC"}.mdi-dog-side:before{content:"\FA43"}.mdi-dolby:before{content:"\F6B2"}.mdi-domain:before{content:"\F1D7"}.mdi-domain-off:before{content:"\FD4B"}.mdi-donkey:before{content:"\F7C1"}.mdi-door:before{content:"\F819"}.mdi-door-closed:before{content:"\F81A"}.mdi-door-open:before{content:"\F81B"}.mdi-doorbell-video:before{content:"\F868"}.mdi-dot-net:before{content:"\FAAD"}.mdi-dots-horizontal:before{content:"\F1D8"}.mdi-dots-horizontal-circle:before{content:"\F7C2"}.mdi-dots-horizontal-circle-outline:before{content:"\FB69"}.mdi-dots-vertical:before{content:"\F1D9"}.mdi-dots-vertical-circle:before{content:"\F7C3"}.mdi-dots-vertical-circle-outline:before{content:"\FB6A"}.mdi-douban:before{content:"\F699"}.mdi-download:before{content:"\F1DA"}.mdi-download-multiple:before{content:"\F9E8"}.mdi-download-network:before{content:"\F6F3"}.mdi-download-network-outline:before{content:"\FC42"}.mdi-download-outline:before{content:"\FB6B"}.mdi-drag:before{content:"\F1DB"}.mdi-drag-horizontal:before{content:"\F1DC"}.mdi-drag-variant:before{content:"\FB6C"}.mdi-drag-vertical:before{content:"\F1DD"}.mdi-drama-masks:before{content:"\FCDE"}.mdi-drawing:before{content:"\F1DE"}.mdi-drawing-box:before{content:"\F1DF"}.mdi-dribbble:before{content:"\F1E0"}.mdi-dribbble-box:before{content:"\F1E1"}.mdi-drone:before{content:"\F1E2"}.mdi-dropbox:before{content:"\F1E3"}.mdi-drupal:before{content:"\F1E4"}.mdi-duck:before{content:"\F1E5"}.mdi-dumbbell:before{content:"\F1E6"}.mdi-dump-truck:before{content:"\FC43"}.mdi-ear-hearing:before{content:"\F7C4"}.mdi-ear-hearing-off:before{content:"\FA44"}.mdi-earth:before{content:"\F1E7"}.mdi-earth-box:before{content:"\F6CC"}.mdi-earth-box-off:before{content:"\F6CD"}.mdi-earth-off:before{content:"\F1E8"}.mdi-edge:before{content:"\F1E9"}.mdi-egg:before{content:"\FAAE"}.mdi-egg-easter:before{content:"\FAAF"}.mdi-eight-track:before{content:"\F9E9"}.mdi-eject:before{content:"\F1EA"}.mdi-eject-outline:before{content:"\FB6D"}.mdi-elephant:before{content:"\F7C5"}.mdi-elevation-decline:before{content:"\F1EB"}.mdi-elevation-rise:before{content:"\F1EC"}.mdi-elevator:before{content:"\F1ED"}.mdi-email:before{content:"\F1EE"}.mdi-email-alert:before{content:"\F6CE"}.mdi-email-box:before{content:"\FCDF"}.mdi-email-check:before{content:"\FAB0"}.mdi-email-check-outline:before{content:"\FAB1"}.mdi-email-lock:before{content:"\F1F1"}.mdi-email-mark-as-unread:before{content:"\FB6E"}.mdi-email-open:before{content:"\F1EF"}.mdi-email-open-outline:before{content:"\F5EF"}.mdi-email-outline:before{content:"\F1F0"}.mdi-email-plus:before{content:"\F9EA"}.mdi-email-plus-outline:before{content:"\F9EB"}.mdi-email-search:before{content:"\F960"}.mdi-email-search-outline:before{content:"\F961"}.mdi-email-variant:before{content:"\F5F0"}.mdi-ember:before{content:"\FB15"}.mdi-emby:before{content:"\F6B3"}.mdi-emoticon:before{content:"\FC44"}.mdi-emoticon-angry:before{content:"\FC45"}.mdi-emoticon-angry-outline:before{content:"\FC46"}.mdi-emoticon-cool:before{content:"\FC47"}.mdi-emoticon-cool-outline:before{content:"\F1F3"}.mdi-emoticon-cry:before{content:"\FC48"}.mdi-emoticon-cry-outline:before{content:"\FC49"}.mdi-emoticon-dead:before{content:"\FC4A"}.mdi-emoticon-dead-outline:before{content:"\F69A"}.mdi-emoticon-devil:before{content:"\FC4B"}.mdi-emoticon-devil-outline:before{content:"\F1F4"}.mdi-emoticon-excited:before{content:"\FC4C"}.mdi-emoticon-excited-outline:before{content:"\F69B"}.mdi-emoticon-happy:before{content:"\FC4D"}.mdi-emoticon-happy-outline:before{content:"\F1F5"}.mdi-emoticon-kiss:before{content:"\FC4E"}.mdi-emoticon-kiss-outline:before{content:"\FC4F"}.mdi-emoticon-neutral:before{content:"\FC50"}.mdi-emoticon-neutral-outline:before{content:"\F1F6"}.mdi-emoticon-outline:before{content:"\F1F2"}.mdi-emoticon-poop:before{content:"\F1F7"}.mdi-emoticon-poop-outline:before{content:"\FC51"}.mdi-emoticon-sad:before{content:"\FC52"}.mdi-emoticon-sad-outline:before{content:"\F1F8"}.mdi-emoticon-tongue:before{content:"\F1F9"}.mdi-emoticon-tongue-outline:before{content:"\FC53"}.mdi-emoticon-wink:before{content:"\FC54"}.mdi-emoticon-wink-outline:before{content:"\FC55"}.mdi-engine:before{content:"\F1FA"}.mdi-engine-off:before{content:"\FA45"}.mdi-engine-off-outline:before{content:"\FA46"}.mdi-engine-outline:before{content:"\F1FB"}.mdi-equal:before{content:"\F1FC"}.mdi-equal-box:before{content:"\F1FD"}.mdi-eraser:before{content:"\F1FE"}.mdi-eraser-variant:before{content:"\F642"}.mdi-escalator:before{content:"\F1FF"}.mdi-eslint:before{content:"\FC56"}.mdi-et:before{content:"\FAB2"}.mdi-ethereum:before{content:"\F869"}.mdi-ethernet:before{content:"\F200"}.mdi-ethernet-cable:before{content:"\F201"}.mdi-ethernet-cable-off:before{content:"\F202"}.mdi-etsy:before{content:"\F203"}.mdi-ev-station:before{content:"\F5F1"}.mdi-eventbrite:before{content:"\F7C6"}.mdi-evernote:before{content:"\F204"}.mdi-exclamation:before{content:"\F205"}.mdi-exit-run:before{content:"\FA47"}.mdi-exit-to-app:before{content:"\F206"}.mdi-expand-all:before{content:"\FAB3"}.mdi-expand-all-outline:before{content:"\FAB4"}.mdi-exponent:before{content:"\F962"}.mdi-exponent-box:before{content:"\F963"}.mdi-export:before{content:"\F207"}.mdi-export-variant:before{content:"\FB6F"}.mdi-eye:before{content:"\F208"}.mdi-eye-check:before{content:"\FCE0"}.mdi-eye-check-outline:before{content:"\FCE1"}.mdi-eye-circle:before{content:"\FB70"}.mdi-eye-circle-outline:before{content:"\FB71"}.mdi-eye-off:before{content:"\F209"}.mdi-eye-off-outline:before{content:"\F6D0"}.mdi-eye-outline:before{content:"\F6CF"}.mdi-eye-plus:before{content:"\F86A"}.mdi-eye-plus-outline:before{content:"\F86B"}.mdi-eye-settings:before{content:"\F86C"}.mdi-eye-settings-outline:before{content:"\F86D"}.mdi-eyedropper:before{content:"\F20A"}.mdi-eyedropper-variant:before{content:"\F20B"}.mdi-face:before{content:"\F643"}.mdi-face-agent:before{content:"\FD4C"}.mdi-face-outline:before{content:"\FB72"}.mdi-face-profile:before{content:"\F644"}.mdi-face-recognition:before{content:"\FC57"}.mdi-facebook:before{content:"\F20C"}.mdi-facebook-box:before{content:"\F20D"}.mdi-facebook-messenger:before{content:"\F20E"}.mdi-facebook-workplace:before{content:"\FB16"}.mdi-factory:before{content:"\F20F"}.mdi-fan:before{content:"\F210"}.mdi-fan-off:before{content:"\F81C"}.mdi-fast-forward:before{content:"\F211"}.mdi-fast-forward-10:before{content:"\FD4D"}.mdi-fast-forward-30:before{content:"\FCE2"}.mdi-fast-forward-outline:before{content:"\F6D1"}.mdi-fax:before{content:"\F212"}.mdi-feather:before{content:"\F6D2"}.mdi-feature-search:before{content:"\FA48"}.mdi-feature-search-outline:before{content:"\FA49"}.mdi-fedora:before{content:"\F8DA"}.mdi-ferry:before{content:"\F213"}.mdi-file:before{content:"\F214"}.mdi-file-account:before{content:"\F73A"}.mdi-file-alert:before{content:"\FA4A"}.mdi-file-alert-outline:before{content:"\FA4B"}.mdi-file-cabinet:before{content:"\FAB5"}.mdi-file-cancel:before{content:"\FDA2"}.mdi-file-cancel-outline:before{content:"\FDA3"}.mdi-file-chart:before{content:"\F215"}.mdi-file-check:before{content:"\F216"}.mdi-file-cloud:before{content:"\F217"}.mdi-file-compare:before{content:"\F8A9"}.mdi-file-delimited:before{content:"\F218"}.mdi-file-document:before{content:"\F219"}.mdi-file-document-box:before{content:"\F21A"}.mdi-file-document-box-multiple:before{content:"\FAB6"}.mdi-file-document-box-multiple-outline:before{content:"\FAB7"}.mdi-file-document-box-outline:before{content:"\F9EC"}.mdi-file-document-edit:before{content:"\FDA4"}.mdi-file-document-edit-outline:before{content:"\FDA5"}.mdi-file-document-outline:before{content:"\F9ED"}.mdi-file-download:before{content:"\F964"}.mdi-file-download-outline:before{content:"\F965"}.mdi-file-excel:before{content:"\F21B"}.mdi-file-excel-box:before{content:"\F21C"}.mdi-file-export:before{content:"\F21D"}.mdi-file-eye:before{content:"\FDA6"}.mdi-file-eye-outline:before{content:"\FDA7"}.mdi-file-find:before{content:"\F21E"}.mdi-file-find-outline:before{content:"\FB73"}.mdi-file-hidden:before{content:"\F613"}.mdi-file-image:before{content:"\F21F"}.mdi-file-import:before{content:"\F220"}.mdi-file-lock:before{content:"\F221"}.mdi-file-move:before{content:"\FAB8"}.mdi-file-multiple:before{content:"\F222"}.mdi-file-music:before{content:"\F223"}.mdi-file-outline:before{content:"\F224"}.mdi-file-pdf:before{content:"\F225"}.mdi-file-pdf-box:before{content:"\F226"}.mdi-file-percent:before{content:"\F81D"}.mdi-file-plus:before{content:"\F751"}.mdi-file-powerpoint:before{content:"\F227"}.mdi-file-powerpoint-box:before{content:"\F228"}.mdi-file-presentation-box:before{content:"\F229"}.mdi-file-question:before{content:"\F86E"}.mdi-file-remove:before{content:"\FB74"}.mdi-file-replace:before{content:"\FB17"}.mdi-file-replace-outline:before{content:"\FB18"}.mdi-file-restore:before{content:"\F670"}.mdi-file-search:before{content:"\FC58"}.mdi-file-search-outline:before{content:"\FC59"}.mdi-file-send:before{content:"\F22A"}.mdi-file-table:before{content:"\FC5A"}.mdi-file-table-outline:before{content:"\FC5B"}.mdi-file-tree:before{content:"\F645"}.mdi-file-undo:before{content:"\F8DB"}.mdi-file-upload:before{content:"\FA4C"}.mdi-file-upload-outline:before{content:"\FA4D"}.mdi-file-video:before{content:"\F22B"}.mdi-file-word:before{content:"\F22C"}.mdi-file-word-box:before{content:"\F22D"}.mdi-file-xml:before{content:"\F22E"}.mdi-film:before{content:"\F22F"}.mdi-filmstrip:before{content:"\F230"}.mdi-filmstrip-off:before{content:"\F231"}.mdi-filter:before{content:"\F232"}.mdi-filter-outline:before{content:"\F233"}.mdi-filter-remove:before{content:"\F234"}.mdi-filter-remove-outline:before{content:"\F235"}.mdi-filter-variant:before{content:"\F236"}.mdi-finance:before{content:"\F81E"}.mdi-find-replace:before{content:"\F6D3"}.mdi-fingerprint:before{content:"\F237"}.mdi-fire:before{content:"\F238"}.mdi-fire-truck:before{content:"\F8AA"}.mdi-firebase:before{content:"\F966"}.mdi-firefox:before{content:"\F239"}.mdi-fish:before{content:"\F23A"}.mdi-flag:before{content:"\F23B"}.mdi-flag-checkered:before{content:"\F23C"}.mdi-flag-minus:before{content:"\FB75"}.mdi-flag-outline:before{content:"\F23D"}.mdi-flag-plus:before{content:"\FB76"}.mdi-flag-remove:before{content:"\FB77"}.mdi-flag-triangle:before{content:"\F23F"}.mdi-flag-variant:before{content:"\F240"}.mdi-flag-variant-outline:before{content:"\F23E"}.mdi-flare:before{content:"\FD4E"}.mdi-flash:before{content:"\F241"}.mdi-flash-auto:before{content:"\F242"}.mdi-flash-circle:before{content:"\F81F"}.mdi-flash-off:before{content:"\F243"}.mdi-flash-outline:before{content:"\F6D4"}.mdi-flash-red-eye:before{content:"\F67A"}.mdi-flashlight:before{content:"\F244"}.mdi-flashlight-off:before{content:"\F245"}.mdi-flask:before{content:"\F093"}.mdi-flask-empty:before{content:"\F094"}.mdi-flask-empty-outline:before{content:"\F095"}.mdi-flask-outline:before{content:"\F096"}.mdi-flattr:before{content:"\F246"}.mdi-flickr:before{content:"\FCE3"}.mdi-flip-to-back:before{content:"\F247"}.mdi-flip-to-front:before{content:"\F248"}.mdi-floor-lamp:before{content:"\F8DC"}.mdi-floor-plan:before{content:"\F820"}.mdi-floppy:before{content:"\F249"}.mdi-floppy-variant:before{content:"\F9EE"}.mdi-flower:before{content:"\F24A"}.mdi-flower-outline:before{content:"\F9EF"}.mdi-flower-poppy:before{content:"\FCE4"}.mdi-flower-tulip:before{content:"\F9F0"}.mdi-flower-tulip-outline:before{content:"\F9F1"}.mdi-folder:before{content:"\F24B"}.mdi-folder-account:before{content:"\F24C"}.mdi-folder-account-outline:before{content:"\FB78"}.mdi-folder-alert:before{content:"\FDA8"}.mdi-folder-alert-outline:before{content:"\FDA9"}.mdi-folder-clock:before{content:"\FAB9"}.mdi-folder-clock-outline:before{content:"\FABA"}.mdi-folder-download:before{content:"\F24D"}.mdi-folder-edit:before{content:"\F8DD"}.mdi-folder-edit-outline:before{content:"\FDAA"}.mdi-folder-google-drive:before{content:"\F24E"}.mdi-folder-image:before{content:"\F24F"}.mdi-folder-key:before{content:"\F8AB"}.mdi-folder-key-network:before{content:"\F8AC"}.mdi-folder-key-network-outline:before{content:"\FC5C"}.mdi-folder-lock:before{content:"\F250"}.mdi-folder-lock-open:before{content:"\F251"}.mdi-folder-move:before{content:"\F252"}.mdi-folder-multiple:before{content:"\F253"}.mdi-folder-multiple-image:before{content:"\F254"}.mdi-folder-multiple-outline:before{content:"\F255"}.mdi-folder-network:before{content:"\F86F"}.mdi-folder-network-outline:before{content:"\FC5D"}.mdi-folder-open:before{content:"\F76F"}.mdi-folder-open-outline:before{content:"\FDAB"}.mdi-folder-outline:before{content:"\F256"}.mdi-folder-plus:before{content:"\F257"}.mdi-folder-plus-outline:before{content:"\FB79"}.mdi-folder-pound:before{content:"\FCE5"}.mdi-folder-pound-outline:before{content:"\FCE6"}.mdi-folder-remove:before{content:"\F258"}.mdi-folder-remove-outline:before{content:"\FB7A"}.mdi-folder-search:before{content:"\F967"}.mdi-folder-search-outline:before{content:"\F968"}.mdi-folder-star:before{content:"\F69C"}.mdi-folder-star-outline:before{content:"\FB7B"}.mdi-folder-sync:before{content:"\FCE7"}.mdi-folder-sync-outline:before{content:"\FCE8"}.mdi-folder-text:before{content:"\FC5E"}.mdi-folder-text-outline:before{content:"\FC5F"}.mdi-folder-upload:before{content:"\F259"}.mdi-font-awesome:before{content:"\F03A"}.mdi-food:before{content:"\F25A"}.mdi-food-apple:before{content:"\F25B"}.mdi-food-apple-outline:before{content:"\FC60"}.mdi-food-croissant:before{content:"\F7C7"}.mdi-food-fork-drink:before{content:"\F5F2"}.mdi-food-off:before{content:"\F5F3"}.mdi-food-variant:before{content:"\F25C"}.mdi-football:before{content:"\F25D"}.mdi-football-australian:before{content:"\F25E"}.mdi-football-helmet:before{content:"\F25F"}.mdi-forklift:before{content:"\F7C8"}.mdi-format-align-bottom:before{content:"\F752"}.mdi-format-align-center:before{content:"\F260"}.mdi-format-align-justify:before{content:"\F261"}.mdi-format-align-left:before{content:"\F262"}.mdi-format-align-middle:before{content:"\F753"}.mdi-format-align-right:before{content:"\F263"}.mdi-format-align-top:before{content:"\F754"}.mdi-format-annotation-minus:before{content:"\FABB"}.mdi-format-annotation-plus:before{content:"\F646"}.mdi-format-bold:before{content:"\F264"}.mdi-format-clear:before{content:"\F265"}.mdi-format-color-fill:before{content:"\F266"}.mdi-format-color-text:before{content:"\F69D"}.mdi-format-columns:before{content:"\F8DE"}.mdi-format-float-center:before{content:"\F267"}.mdi-format-float-left:before{content:"\F268"}.mdi-format-float-none:before{content:"\F269"}.mdi-format-float-right:before{content:"\F26A"}.mdi-format-font:before{content:"\F6D5"}.mdi-format-font-size-decrease:before{content:"\F9F2"}.mdi-format-font-size-increase:before{content:"\F9F3"}.mdi-format-header-1:before{content:"\F26B"}.mdi-format-header-2:before{content:"\F26C"}.mdi-format-header-3:before{content:"\F26D"}.mdi-format-header-4:before{content:"\F26E"}.mdi-format-header-5:before{content:"\F26F"}.mdi-format-header-6:before{content:"\F270"}.mdi-format-header-decrease:before{content:"\F271"}.mdi-format-header-equal:before{content:"\F272"}.mdi-format-header-increase:before{content:"\F273"}.mdi-format-header-pound:before{content:"\F274"}.mdi-format-horizontal-align-center:before{content:"\F61E"}.mdi-format-horizontal-align-left:before{content:"\F61F"}.mdi-format-horizontal-align-right:before{content:"\F620"}.mdi-format-indent-decrease:before{content:"\F275"}.mdi-format-indent-increase:before{content:"\F276"}.mdi-format-italic:before{content:"\F277"}.mdi-format-letter-case:before{content:"\FB19"}.mdi-format-letter-case-lower:before{content:"\FB1A"}.mdi-format-letter-case-upper:before{content:"\FB1B"}.mdi-format-line-spacing:before{content:"\F278"}.mdi-format-line-style:before{content:"\F5C8"}.mdi-format-line-weight:before{content:"\F5C9"}.mdi-format-list-bulleted:before{content:"\F279"}.mdi-format-list-bulleted-square:before{content:"\FDAC"}.mdi-format-list-bulleted-type:before{content:"\F27A"}.mdi-format-list-checkbox:before{content:"\F969"}.mdi-format-list-checks:before{content:"\F755"}.mdi-format-list-numbered:before{content:"\F27B"}.mdi-format-list-numbered-rtl:before{content:"\FCE9"}.mdi-format-page-break:before{content:"\F6D6"}.mdi-format-paint:before{content:"\F27C"}.mdi-format-paragraph:before{content:"\F27D"}.mdi-format-pilcrow:before{content:"\F6D7"}.mdi-format-quote-close:before{content:"\F27E"}.mdi-format-quote-open:before{content:"\F756"}.mdi-format-rotate-90:before{content:"\F6A9"}.mdi-format-section:before{content:"\F69E"}.mdi-format-size:before{content:"\F27F"}.mdi-format-strikethrough:before{content:"\F280"}.mdi-format-strikethrough-variant:before{content:"\F281"}.mdi-format-subscript:before{content:"\F282"}.mdi-format-superscript:before{content:"\F283"}.mdi-format-text:before{content:"\F284"}.mdi-format-text-rotation-down:before{content:"\FD4F"}.mdi-format-text-rotation-none:before{content:"\FD50"}.mdi-format-text-wrapping-clip:before{content:"\FCEA"}.mdi-format-text-wrapping-overflow:before{content:"\FCEB"}.mdi-format-text-wrapping-wrap:before{content:"\FCEC"}.mdi-format-textbox:before{content:"\FCED"}.mdi-format-textdirection-l-to-r:before{content:"\F285"}.mdi-format-textdirection-r-to-l:before{content:"\F286"}.mdi-format-title:before{content:"\F5F4"}.mdi-format-underline:before{content:"\F287"}.mdi-format-vertical-align-bottom:before{content:"\F621"}.mdi-format-vertical-align-center:before{content:"\F622"}.mdi-format-vertical-align-top:before{content:"\F623"}.mdi-format-wrap-inline:before{content:"\F288"}.mdi-format-wrap-square:before{content:"\F289"}.mdi-format-wrap-tight:before{content:"\F28A"}.mdi-format-wrap-top-bottom:before{content:"\F28B"}.mdi-forum:before{content:"\F28C"}.mdi-forum-outline:before{content:"\F821"}.mdi-forward:before{content:"\F28D"}.mdi-forwardburger:before{content:"\FD51"}.mdi-fountain:before{content:"\F96A"}.mdi-fountain-pen:before{content:"\FCEE"}.mdi-fountain-pen-tip:before{content:"\FCEF"}.mdi-foursquare:before{content:"\F28E"}.mdi-freebsd:before{content:"\F8DF"}.mdi-fridge:before{content:"\F290"}.mdi-fridge-bottom:before{content:"\F292"}.mdi-fridge-outline:before{content:"\F28F"}.mdi-fridge-top:before{content:"\F291"}.mdi-fuel:before{content:"\F7C9"}.mdi-fullscreen:before{content:"\F293"}.mdi-fullscreen-exit:before{content:"\F294"}.mdi-function:before{content:"\F295"}.mdi-function-variant:before{content:"\F870"}.mdi-fuse:before{content:"\FC61"}.mdi-fuse-blade:before{content:"\FC62"}.mdi-gamepad:before{content:"\F296"}.mdi-gamepad-variant:before{content:"\F297"}.mdi-gantry-crane:before{content:"\FDAD"}.mdi-garage:before{content:"\F6D8"}.mdi-garage-alert:before{content:"\F871"}.mdi-garage-open:before{content:"\F6D9"}.mdi-gas-cylinder:before{content:"\F647"}.mdi-gas-station:before{content:"\F298"}.mdi-gate:before{content:"\F299"}.mdi-gate-and:before{content:"\F8E0"}.mdi-gate-nand:before{content:"\F8E1"}.mdi-gate-nor:before{content:"\F8E2"}.mdi-gate-not:before{content:"\F8E3"}.mdi-gate-or:before{content:"\F8E4"}.mdi-gate-xnor:before{content:"\F8E5"}.mdi-gate-xor:before{content:"\F8E6"}.mdi-gauge:before{content:"\F29A"}.mdi-gauge-empty:before{content:"\F872"}.mdi-gauge-full:before{content:"\F873"}.mdi-gauge-low:before{content:"\F874"}.mdi-gavel:before{content:"\F29B"}.mdi-gender-female:before{content:"\F29C"}.mdi-gender-male:before{content:"\F29D"}.mdi-gender-male-female:before{content:"\F29E"}.mdi-gender-transgender:before{content:"\F29F"}.mdi-gentoo:before{content:"\F8E7"}.mdi-gesture:before{content:"\F7CA"}.mdi-gesture-double-tap:before{content:"\F73B"}.mdi-gesture-pinch:before{content:"\FABC"}.mdi-gesture-spread:before{content:"\FABD"}.mdi-gesture-swipe:before{content:"\FD52"}.mdi-gesture-swipe-down:before{content:"\F73C"}.mdi-gesture-swipe-horizontal:before{content:"\FABE"}.mdi-gesture-swipe-left:before{content:"\F73D"}.mdi-gesture-swipe-right:before{content:"\F73E"}.mdi-gesture-swipe-up:before{content:"\F73F"}.mdi-gesture-swipe-vertical:before{content:"\FABF"}.mdi-gesture-tap:before{content:"\F740"}.mdi-gesture-tap-hold:before{content:"\FD53"}.mdi-gesture-two-double-tap:before{content:"\F741"}.mdi-gesture-two-tap:before{content:"\F742"}.mdi-ghost:before{content:"\F2A0"}.mdi-ghost-off:before{content:"\F9F4"}.mdi-gif:before{content:"\FD54"}.mdi-gift:before{content:"\F2A1"}.mdi-git:before{content:"\F2A2"}.mdi-github-box:before{content:"\F2A3"}.mdi-github-circle:before{content:"\F2A4"}.mdi-github-face:before{content:"\F6DA"}.mdi-gitlab:before{content:"\FB7C"}.mdi-glass-cocktail:before{content:"\F356"}.mdi-glass-flute:before{content:"\F2A5"}.mdi-glass-mug:before{content:"\F2A6"}.mdi-glass-stange:before{content:"\F2A7"}.mdi-glass-tulip:before{content:"\F2A8"}.mdi-glass-wine:before{content:"\F875"}.mdi-glassdoor:before{content:"\F2A9"}.mdi-glasses:before{content:"\F2AA"}.mdi-globe-model:before{content:"\F8E8"}.mdi-gmail:before{content:"\F2AB"}.mdi-gnome:before{content:"\F2AC"}.mdi-go-kart:before{content:"\FD55"}.mdi-go-kart-track:before{content:"\FD56"}.mdi-gog:before{content:"\FB7D"}.mdi-golf:before{content:"\F822"}.mdi-gondola:before{content:"\F685"}.mdi-goodreads:before{content:"\FD57"}.mdi-google:before{content:"\F2AD"}.mdi-google-adwords:before{content:"\FC63"}.mdi-google-allo:before{content:"\F801"}.mdi-google-analytics:before{content:"\F7CB"}.mdi-google-assistant:before{content:"\F7CC"}.mdi-google-cardboard:before{content:"\F2AE"}.mdi-google-chrome:before{content:"\F2AF"}.mdi-google-circles:before{content:"\F2B0"}.mdi-google-circles-communities:before{content:"\F2B1"}.mdi-google-circles-extended:before{content:"\F2B2"}.mdi-google-circles-group:before{content:"\F2B3"}.mdi-google-classroom:before{content:"\F2C0"}.mdi-google-controller:before{content:"\F2B4"}.mdi-google-controller-off:before{content:"\F2B5"}.mdi-google-drive:before{content:"\F2B6"}.mdi-google-earth:before{content:"\F2B7"}.mdi-google-fit:before{content:"\F96B"}.mdi-google-glass:before{content:"\F2B8"}.mdi-google-hangouts:before{content:"\F2C9"}.mdi-google-home:before{content:"\F823"}.mdi-google-keep:before{content:"\F6DB"}.mdi-google-lens:before{content:"\F9F5"}.mdi-google-maps:before{content:"\F5F5"}.mdi-google-nearby:before{content:"\F2B9"}.mdi-google-pages:before{content:"\F2BA"}.mdi-google-photos:before{content:"\F6DC"}.mdi-google-physical-web:before{content:"\F2BB"}.mdi-google-play:before{content:"\F2BC"}.mdi-google-plus:before{content:"\F2BD"}.mdi-google-plus-box:before{content:"\F2BE"}.mdi-google-spreadsheet:before{content:"\F9F6"}.mdi-google-street-view:before{content:"\FC64"}.mdi-google-translate:before{content:"\F2BF"}.mdi-gpu:before{content:"\F8AD"}.mdi-gradient:before{content:"\F69F"}.mdi-grain:before{content:"\FD58"}.mdi-graphql:before{content:"\F876"}.mdi-grave-stone:before{content:"\FB7E"}.mdi-grease-pencil:before{content:"\F648"}.mdi-greater-than:before{content:"\F96C"}.mdi-greater-than-or-equal:before{content:"\F96D"}.mdi-grid:before{content:"\F2C1"}.mdi-grid-large:before{content:"\F757"}.mdi-grid-off:before{content:"\F2C2"}.mdi-group:before{content:"\F2C3"}.mdi-guitar-acoustic:before{content:"\F770"}.mdi-guitar-electric:before{content:"\F2C4"}.mdi-guitar-pick:before{content:"\F2C5"}.mdi-guitar-pick-outline:before{content:"\F2C6"}.mdi-guy-fawkes-mask:before{content:"\F824"}.mdi-hackernews:before{content:"\F624"}.mdi-hail:before{content:"\FAC0"}.mdi-halloween:before{content:"\FB7F"}.mdi-hamburger:before{content:"\F684"}.mdi-hammer:before{content:"\F8E9"}.mdi-hand:before{content:"\FA4E"}.mdi-hand-okay:before{content:"\FA4F"}.mdi-hand-peace:before{content:"\FA50"}.mdi-hand-peace-variant:before{content:"\FA51"}.mdi-hand-pointing-down:before{content:"\FA52"}.mdi-hand-pointing-left:before{content:"\FA53"}.mdi-hand-pointing-right:before{content:"\F2C7"}.mdi-hand-pointing-up:before{content:"\FA54"}.mdi-hanger:before{content:"\F2C8"}.mdi-hard-hat:before{content:"\F96E"}.mdi-harddisk:before{content:"\F2CA"}.mdi-hat-fedora:before{content:"\FB80"}.mdi-hazard-lights:before{content:"\FC65"}.mdi-hdr:before{content:"\FD59"}.mdi-hdr-off:before{content:"\FD5A"}.mdi-headphones:before{content:"\F2CB"}.mdi-headphones-bluetooth:before{content:"\F96F"}.mdi-headphones-box:before{content:"\F2CC"}.mdi-headphones-off:before{content:"\F7CD"}.mdi-headphones-settings:before{content:"\F2CD"}.mdi-headset:before{content:"\F2CE"}.mdi-headset-dock:before{content:"\F2CF"}.mdi-headset-off:before{content:"\F2D0"}.mdi-heart:before{content:"\F2D1"}.mdi-heart-box:before{content:"\F2D2"}.mdi-heart-box-outline:before{content:"\F2D3"}.mdi-heart-broken:before{content:"\F2D4"}.mdi-heart-broken-outline:before{content:"\FCF0"}.mdi-heart-circle:before{content:"\F970"}.mdi-heart-circle-outline:before{content:"\F971"}.mdi-heart-half:before{content:"\F6DE"}.mdi-heart-half-full:before{content:"\F6DD"}.mdi-heart-half-outline:before{content:"\F6DF"}.mdi-heart-multiple:before{content:"\FA55"}.mdi-heart-multiple-outline:before{content:"\FA56"}.mdi-heart-off:before{content:"\F758"}.mdi-heart-outline:before{content:"\F2D5"}.mdi-heart-pulse:before{content:"\F5F6"}.mdi-helicopter:before{content:"\FAC1"}.mdi-help:before{content:"\F2D6"}.mdi-help-box:before{content:"\F78A"}.mdi-help-circle:before{content:"\F2D7"}.mdi-help-circle-outline:before{content:"\F625"}.mdi-help-network:before{content:"\F6F4"}.mdi-help-network-outline:before{content:"\FC66"}.mdi-help-rhombus:before{content:"\FB81"}.mdi-help-rhombus-outline:before{content:"\FB82"}.mdi-hexagon:before{content:"\F2D8"}.mdi-hexagon-multiple:before{content:"\F6E0"}.mdi-hexagon-outline:before{content:"\F2D9"}.mdi-hexagon-slice-1:before{content:"\FAC2"}.mdi-hexagon-slice-2:before{content:"\FAC3"}.mdi-hexagon-slice-3:before{content:"\FAC4"}.mdi-hexagon-slice-4:before{content:"\FAC5"}.mdi-hexagon-slice-5:before{content:"\FAC6"}.mdi-hexagon-slice-6:before{content:"\FAC7"}.mdi-hexagram:before{content:"\FAC8"}.mdi-hexagram-outline:before{content:"\FAC9"}.mdi-high-definition:before{content:"\F7CE"}.mdi-high-definition-box:before{content:"\F877"}.mdi-highway:before{content:"\F5F7"}.mdi-hiking:before{content:"\FD5B"}.mdi-hinduism:before{content:"\F972"}.mdi-history:before{content:"\F2DA"}.mdi-hockey-puck:before{content:"\F878"}.mdi-hockey-sticks:before{content:"\F879"}.mdi-hololens:before{content:"\F2DB"}.mdi-home:before{content:"\F2DC"}.mdi-home-account:before{content:"\F825"}.mdi-home-alert:before{content:"\F87A"}.mdi-home-assistant:before{content:"\F7CF"}.mdi-home-automation:before{content:"\F7D0"}.mdi-home-circle:before{content:"\F7D1"}.mdi-home-city:before{content:"\FCF1"}.mdi-home-city-outline:before{content:"\FCF2"}.mdi-home-currency-usd:before{content:"\F8AE"}.mdi-home-floor-0:before{content:"\FDAE"}.mdi-home-floor-1:before{content:"\FD5C"}.mdi-home-floor-2:before{content:"\FD5D"}.mdi-home-floor-3:before{content:"\FD5E"}.mdi-home-floor-a:before{content:"\FD5F"}.mdi-home-floor-b:before{content:"\FD60"}.mdi-home-floor-g:before{content:"\FD61"}.mdi-home-floor-l:before{content:"\FD62"}.mdi-home-floor-negative-1:before{content:"\FDAF"}.mdi-home-group:before{content:"\FDB0"}.mdi-home-heart:before{content:"\F826"}.mdi-home-lock:before{content:"\F8EA"}.mdi-home-lock-open:before{content:"\F8EB"}.mdi-home-map-marker:before{content:"\F5F8"}.mdi-home-minus:before{content:"\F973"}.mdi-home-modern:before{content:"\F2DD"}.mdi-home-outline:before{content:"\F6A0"}.mdi-home-plus:before{content:"\F974"}.mdi-home-variant:before{content:"\F2DE"}.mdi-home-variant-outline:before{content:"\FB83"}.mdi-hook:before{content:"\F6E1"}.mdi-hook-off:before{content:"\F6E2"}.mdi-hops:before{content:"\F2DF"}.mdi-horseshoe:before{content:"\FA57"}.mdi-hospital:before{content:"\F2E0"}.mdi-hospital-building:before{content:"\F2E1"}.mdi-hospital-marker:before{content:"\F2E2"}.mdi-hot-tub:before{content:"\F827"}.mdi-hotel:before{content:"\F2E3"}.mdi-houzz:before{content:"\F2E4"}.mdi-houzz-box:before{content:"\F2E5"}.mdi-hubspot:before{content:"\FCF3"}.mdi-hulu:before{content:"\F828"}.mdi-human:before{content:"\F2E6"}.mdi-human-child:before{content:"\F2E7"}.mdi-human-female:before{content:"\F649"}.mdi-human-female-boy:before{content:"\FA58"}.mdi-human-female-female:before{content:"\FA59"}.mdi-human-female-girl:before{content:"\FA5A"}.mdi-human-greeting:before{content:"\F64A"}.mdi-human-handsdown:before{content:"\F64B"}.mdi-human-handsup:before{content:"\F64C"}.mdi-human-male:before{content:"\F64D"}.mdi-human-male-boy:before{content:"\FA5B"}.mdi-human-male-female:before{content:"\F2E8"}.mdi-human-male-girl:before{content:"\FA5C"}.mdi-human-male-male:before{content:"\FA5D"}.mdi-human-pregnant:before{content:"\F5CF"}.mdi-humble-bundle:before{content:"\F743"}.mdi-ice-cream:before{content:"\F829"}.mdi-iframe:before{content:"\FC67"}.mdi-iframe-outline:before{content:"\FC68"}.mdi-image:before{content:"\F2E9"}.mdi-image-album:before{content:"\F2EA"}.mdi-image-area:before{content:"\F2EB"}.mdi-image-area-close:before{content:"\F2EC"}.mdi-image-broken:before{content:"\F2ED"}.mdi-image-broken-variant:before{content:"\F2EE"}.mdi-image-filter:before{content:"\F2EF"}.mdi-image-filter-black-white:before{content:"\F2F0"}.mdi-image-filter-center-focus:before{content:"\F2F1"}.mdi-image-filter-center-focus-weak:before{content:"\F2F2"}.mdi-image-filter-drama:before{content:"\F2F3"}.mdi-image-filter-frames:before{content:"\F2F4"}.mdi-image-filter-hdr:before{content:"\F2F5"}.mdi-image-filter-none:before{content:"\F2F6"}.mdi-image-filter-tilt-shift:before{content:"\F2F7"}.mdi-image-filter-vintage:before{content:"\F2F8"}.mdi-image-move:before{content:"\F9F7"}.mdi-image-multiple:before{content:"\F2F9"}.mdi-image-off:before{content:"\F82A"}.mdi-image-outline:before{content:"\F975"}.mdi-image-plus:before{content:"\F87B"}.mdi-image-search:before{content:"\F976"}.mdi-image-search-outline:before{content:"\F977"}.mdi-image-size-select-actual:before{content:"\FC69"}.mdi-image-size-select-large:before{content:"\FC6A"}.mdi-image-size-select-small:before{content:"\FC6B"}.mdi-import:before{content:"\F2FA"}.mdi-inbox:before{content:"\F686"}.mdi-inbox-arrow-down:before{content:"\F2FB"}.mdi-inbox-arrow-up:before{content:"\F3D1"}.mdi-inbox-multiple:before{content:"\F8AF"}.mdi-inbox-multiple-outline:before{content:"\FB84"}.mdi-incognito:before{content:"\F5F9"}.mdi-infinity:before{content:"\F6E3"}.mdi-information:before{content:"\F2FC"}.mdi-information-outline:before{content:"\F2FD"}.mdi-information-variant:before{content:"\F64E"}.mdi-instagram:before{content:"\F2FE"}.mdi-instapaper:before{content:"\F2FF"}.mdi-internet-explorer:before{content:"\F300"}.mdi-invert-colors:before{content:"\F301"}.mdi-ip:before{content:"\FA5E"}.mdi-ip-network:before{content:"\FA5F"}.mdi-ip-network-outline:before{content:"\FC6C"}.mdi-ipod:before{content:"\FC6D"}.mdi-islam:before{content:"\F978"}.mdi-itunes:before{content:"\F676"}.mdi-jabber:before{content:"\FDB1"}.mdi-jeepney:before{content:"\F302"}.mdi-jira:before{content:"\F303"}.mdi-jquery:before{content:"\F87C"}.mdi-jsfiddle:before{content:"\F304"}.mdi-json:before{content:"\F626"}.mdi-judaism:before{content:"\F979"}.mdi-kabaddi:before{content:"\FD63"}.mdi-karate:before{content:"\F82B"}.mdi-keg:before{content:"\F305"}.mdi-kettle:before{content:"\F5FA"}.mdi-key:before{content:"\F306"}.mdi-key-change:before{content:"\F307"}.mdi-key-minus:before{content:"\F308"}.mdi-key-outline:before{content:"\FDB2"}.mdi-key-plus:before{content:"\F309"}.mdi-key-remove:before{content:"\F30A"}.mdi-key-variant:before{content:"\F30B"}.mdi-keyboard:before{content:"\F30C"}.mdi-keyboard-backspace:before{content:"\F30D"}.mdi-keyboard-caps:before{content:"\F30E"}.mdi-keyboard-close:before{content:"\F30F"}.mdi-keyboard-off:before{content:"\F310"}.mdi-keyboard-outline:before{content:"\F97A"}.mdi-keyboard-return:before{content:"\F311"}.mdi-keyboard-settings:before{content:"\F9F8"}.mdi-keyboard-settings-outline:before{content:"\F9F9"}.mdi-keyboard-tab:before{content:"\F312"}.mdi-keyboard-variant:before{content:"\F313"}.mdi-kickstarter:before{content:"\F744"}.mdi-knife:before{content:"\F9FA"}.mdi-knife-military:before{content:"\F9FB"}.mdi-kodi:before{content:"\F314"}.mdi-label:before{content:"\F315"}.mdi-label-off:before{content:"\FACA"}.mdi-label-off-outline:before{content:"\FACB"}.mdi-label-outline:before{content:"\F316"}.mdi-label-variant:before{content:"\FACC"}.mdi-label-variant-outline:before{content:"\FACD"}.mdi-ladybug:before{content:"\F82C"}.mdi-lambda:before{content:"\F627"}.mdi-lamp:before{content:"\F6B4"}.mdi-lan:before{content:"\F317"}.mdi-lan-connect:before{content:"\F318"}.mdi-lan-disconnect:before{content:"\F319"}.mdi-lan-pending:before{content:"\F31A"}.mdi-language-c:before{content:"\F671"}.mdi-language-cpp:before{content:"\F672"}.mdi-language-csharp:before{content:"\F31B"}.mdi-language-css3:before{content:"\F31C"}.mdi-language-go:before{content:"\F7D2"}.mdi-language-haskell:before{content:"\FC6E"}.mdi-language-html5:before{content:"\F31D"}.mdi-language-java:before{content:"\FB1C"}.mdi-language-javascript:before{content:"\F31E"}.mdi-language-lua:before{content:"\F8B0"}.mdi-language-php:before{content:"\F31F"}.mdi-language-python:before{content:"\F320"}.mdi-language-python-text:before{content:"\F321"}.mdi-language-r:before{content:"\F7D3"}.mdi-language-ruby-on-rails:before{content:"\FACE"}.mdi-language-swift:before{content:"\F6E4"}.mdi-language-typescript:before{content:"\F6E5"}.mdi-laptop:before{content:"\F322"}.mdi-laptop-chromebook:before{content:"\F323"}.mdi-laptop-mac:before{content:"\F324"}.mdi-laptop-off:before{content:"\F6E6"}.mdi-laptop-windows:before{content:"\F325"}.mdi-laravel:before{content:"\FACF"}.mdi-lastfm:before{content:"\F326"}.mdi-lastpass:before{content:"\F446"}.mdi-launch:before{content:"\F327"}.mdi-lava-lamp:before{content:"\F7D4"}.mdi-layers:before{content:"\F328"}.mdi-layers-off:before{content:"\F329"}.mdi-layers-off-outline:before{content:"\F9FC"}.mdi-layers-outline:before{content:"\F9FD"}.mdi-lead-pencil:before{content:"\F64F"}.mdi-leaf:before{content:"\F32A"}.mdi-leaf-maple:before{content:"\FC6F"}.mdi-leak:before{content:"\FDB3"}.mdi-leak-off:before{content:"\FDB4"}.mdi-led-off:before{content:"\F32B"}.mdi-led-on:before{content:"\F32C"}.mdi-led-outline:before{content:"\F32D"}.mdi-led-strip:before{content:"\F7D5"}.mdi-led-variant-off:before{content:"\F32E"}.mdi-led-variant-on:before{content:"\F32F"}.mdi-led-variant-outline:before{content:"\F330"}.mdi-less-than:before{content:"\F97B"}.mdi-less-than-or-equal:before{content:"\F97C"}.mdi-library:before{content:"\F331"}.mdi-library-books:before{content:"\F332"}.mdi-library-movie:before{content:"\FCF4"}.mdi-library-music:before{content:"\F333"}.mdi-library-plus:before{content:"\F334"}.mdi-library-shelves:before{content:"\FB85"}.mdi-library-video:before{content:"\FCF5"}.mdi-lifebuoy:before{content:"\F87D"}.mdi-light-switch:before{content:"\F97D"}.mdi-lightbulb:before{content:"\F335"}.mdi-lightbulb-on:before{content:"\F6E7"}.mdi-lightbulb-on-outline:before{content:"\F6E8"}.mdi-lightbulb-outline:before{content:"\F336"}.mdi-lighthouse:before{content:"\F9FE"}.mdi-lighthouse-on:before{content:"\F9FF"}.mdi-link:before{content:"\F337"}.mdi-link-box:before{content:"\FCF6"}.mdi-link-box-outline:before{content:"\FCF7"}.mdi-link-box-variant:before{content:"\FCF8"}.mdi-link-box-variant-outline:before{content:"\FCF9"}.mdi-link-off:before{content:"\F338"}.mdi-link-plus:before{content:"\FC70"}.mdi-link-variant:before{content:"\F339"}.mdi-link-variant-off:before{content:"\F33A"}.mdi-linkedin:before{content:"\F33B"}.mdi-linkedin-box:before{content:"\F33C"}.mdi-linux:before{content:"\F33D"}.mdi-linux-mint:before{content:"\F8EC"}.mdi-litecoin:before{content:"\FA60"}.mdi-loading:before{content:"\F771"}.mdi-lock:before{content:"\F33E"}.mdi-lock-alert:before{content:"\F8ED"}.mdi-lock-clock:before{content:"\F97E"}.mdi-lock-open:before{content:"\F33F"}.mdi-lock-open-outline:before{content:"\F340"}.mdi-lock-outline:before{content:"\F341"}.mdi-lock-pattern:before{content:"\F6E9"}.mdi-lock-plus:before{content:"\F5FB"}.mdi-lock-question:before{content:"\F8EE"}.mdi-lock-reset:before{content:"\F772"}.mdi-lock-smart:before{content:"\F8B1"}.mdi-locker:before{content:"\F7D6"}.mdi-locker-multiple:before{content:"\F7D7"}.mdi-login:before{content:"\F342"}.mdi-login-variant:before{content:"\F5FC"}.mdi-logout:before{content:"\F343"}.mdi-logout-variant:before{content:"\F5FD"}.mdi-looks:before{content:"\F344"}.mdi-loop:before{content:"\F6EA"}.mdi-loupe:before{content:"\F345"}.mdi-lumx:before{content:"\F346"}.mdi-lyft:before{content:"\FB1D"}.mdi-magnet:before{content:"\F347"}.mdi-magnet-on:before{content:"\F348"}.mdi-magnify:before{content:"\F349"}.mdi-magnify-close:before{content:"\F97F"}.mdi-magnify-minus:before{content:"\F34A"}.mdi-magnify-minus-cursor:before{content:"\FA61"}.mdi-magnify-minus-outline:before{content:"\F6EB"}.mdi-magnify-plus:before{content:"\F34B"}.mdi-magnify-plus-cursor:before{content:"\FA62"}.mdi-magnify-plus-outline:before{content:"\F6EC"}.mdi-mail-ru:before{content:"\F34C"}.mdi-mailbox:before{content:"\F6ED"}.mdi-mailbox-open:before{content:"\FD64"}.mdi-mailbox-open-outline:before{content:"\FD65"}.mdi-mailbox-open-up:before{content:"\FD66"}.mdi-mailbox-open-up-outline:before{content:"\FD67"}.mdi-mailbox-outline:before{content:"\FD68"}.mdi-mailbox-up:before{content:"\FD69"}.mdi-mailbox-up-outline:before{content:"\FD6A"}.mdi-map:before{content:"\F34D"}.mdi-map-clock:before{content:"\FCFA"}.mdi-map-clock-outline:before{content:"\FCFB"}.mdi-map-legend:before{content:"\FA00"}.mdi-map-marker:before{content:"\F34E"}.mdi-map-marker-check:before{content:"\FC71"}.mdi-map-marker-circle:before{content:"\F34F"}.mdi-map-marker-distance:before{content:"\F8EF"}.mdi-map-marker-minus:before{content:"\F650"}.mdi-map-marker-multiple:before{content:"\F350"}.mdi-map-marker-off:before{content:"\F351"}.mdi-map-marker-outline:before{content:"\F7D8"}.mdi-map-marker-path:before{content:"\FCFC"}.mdi-map-marker-plus:before{content:"\F651"}.mdi-map-marker-radius:before{content:"\F352"}.mdi-map-minus:before{content:"\F980"}.mdi-map-outline:before{content:"\F981"}.mdi-map-plus:before{content:"\F982"}.mdi-map-search:before{content:"\F983"}.mdi-map-search-outline:before{content:"\F984"}.mdi-mapbox:before{content:"\FB86"}.mdi-margin:before{content:"\F353"}.mdi-markdown:before{content:"\F354"}.mdi-marker:before{content:"\F652"}.mdi-marker-cancel:before{content:"\FDB5"}.mdi-marker-check:before{content:"\F355"}.mdi-mastodon:before{content:"\FAD0"}.mdi-mastodon-variant:before{content:"\FAD1"}.mdi-material-design:before{content:"\F985"}.mdi-material-ui:before{content:"\F357"}.mdi-math-compass:before{content:"\F358"}.mdi-math-cos:before{content:"\FC72"}.mdi-math-sin:before{content:"\FC73"}.mdi-math-tan:before{content:"\FC74"}.mdi-matrix:before{content:"\F628"}.mdi-maxcdn:before{content:"\F359"}.mdi-medal:before{content:"\F986"}.mdi-medical-bag:before{content:"\F6EE"}.mdi-medium:before{content:"\F35A"}.mdi-meetup:before{content:"\FAD2"}.mdi-memory:before{content:"\F35B"}.mdi-menu:before{content:"\F35C"}.mdi-menu-down:before{content:"\F35D"}.mdi-menu-down-outline:before{content:"\F6B5"}.mdi-menu-left:before{content:"\F35E"}.mdi-menu-left-outline:before{content:"\FA01"}.mdi-menu-open:before{content:"\FB87"}.mdi-menu-right:before{content:"\F35F"}.mdi-menu-right-outline:before{content:"\FA02"}.mdi-menu-swap:before{content:"\FA63"}.mdi-menu-swap-outline:before{content:"\FA64"}.mdi-menu-up:before{content:"\F360"}.mdi-menu-up-outline:before{content:"\F6B6"}.mdi-message:before{content:"\F361"}.mdi-message-alert:before{content:"\F362"}.mdi-message-alert-outline:before{content:"\FA03"}.mdi-message-bulleted:before{content:"\F6A1"}.mdi-message-bulleted-off:before{content:"\F6A2"}.mdi-message-draw:before{content:"\F363"}.mdi-message-image:before{content:"\F364"}.mdi-message-outline:before{content:"\F365"}.mdi-message-plus:before{content:"\F653"}.mdi-message-processing:before{content:"\F366"}.mdi-message-reply:before{content:"\F367"}.mdi-message-reply-text:before{content:"\F368"}.mdi-message-settings:before{content:"\F6EF"}.mdi-message-settings-variant:before{content:"\F6F0"}.mdi-message-text:before{content:"\F369"}.mdi-message-text-outline:before{content:"\F36A"}.mdi-message-video:before{content:"\F36B"}.mdi-meteor:before{content:"\F629"}.mdi-metronome:before{content:"\F7D9"}.mdi-metronome-tick:before{content:"\F7DA"}.mdi-micro-sd:before{content:"\F7DB"}.mdi-microphone:before{content:"\F36C"}.mdi-microphone-minus:before{content:"\F8B2"}.mdi-microphone-off:before{content:"\F36D"}.mdi-microphone-outline:before{content:"\F36E"}.mdi-microphone-plus:before{content:"\F8B3"}.mdi-microphone-settings:before{content:"\F36F"}.mdi-microphone-variant:before{content:"\F370"}.mdi-microphone-variant-off:before{content:"\F371"}.mdi-microscope:before{content:"\F654"}.mdi-microsoft:before{content:"\F372"}.mdi-microsoft-dynamics:before{content:"\F987"}.mdi-microwave:before{content:"\FC75"}.mdi-midi:before{content:"\F8F0"}.mdi-midi-port:before{content:"\F8F1"}.mdi-mine:before{content:"\FDB6"}.mdi-minecraft:before{content:"\F373"}.mdi-mini-sd:before{content:"\FA04"}.mdi-minidisc:before{content:"\FA05"}.mdi-minus:before{content:"\F374"}.mdi-minus-box:before{content:"\F375"}.mdi-minus-box-outline:before{content:"\F6F1"}.mdi-minus-circle:before{content:"\F376"}.mdi-minus-circle-outline:before{content:"\F377"}.mdi-minus-network:before{content:"\F378"}.mdi-minus-network-outline:before{content:"\FC76"}.mdi-mixcloud:before{content:"\F62A"}.mdi-mixed-martial-arts:before{content:"\FD6B"}.mdi-mixed-reality:before{content:"\F87E"}.mdi-mixer:before{content:"\F7DC"}.mdi-molecule:before{content:"\FB88"}.mdi-monitor:before{content:"\F379"}.mdi-monitor-cellphone:before{content:"\F988"}.mdi-monitor-cellphone-star:before{content:"\F989"}.mdi-monitor-dashboard:before{content:"\FA06"}.mdi-monitor-lock:before{content:"\FDB7"}.mdi-monitor-multiple:before{content:"\F37A"}.mdi-monitor-off:before{content:"\FD6C"}.mdi-monitor-star:before{content:"\FDB8"}.mdi-more:before{content:"\F37B"}.mdi-mother-nurse:before{content:"\FCFD"}.mdi-motion-sensor:before{content:"\FD6D"}.mdi-motorbike:before{content:"\F37C"}.mdi-mouse:before{content:"\F37D"}.mdi-mouse-bluetooth:before{content:"\F98A"}.mdi-mouse-off:before{content:"\F37E"}.mdi-mouse-variant:before{content:"\F37F"}.mdi-mouse-variant-off:before{content:"\F380"}.mdi-move-resize:before{content:"\F655"}.mdi-move-resize-variant:before{content:"\F656"}.mdi-movie:before{content:"\F381"}.mdi-movie-outline:before{content:"\FDB9"}.mdi-movie-roll:before{content:"\F7DD"}.mdi-muffin:before{content:"\F98B"}.mdi-multiplication:before{content:"\F382"}.mdi-multiplication-box:before{content:"\F383"}.mdi-mushroom:before{content:"\F7DE"}.mdi-mushroom-outline:before{content:"\F7DF"}.mdi-music:before{content:"\F759"}.mdi-music-box:before{content:"\F384"}.mdi-music-box-outline:before{content:"\F385"}.mdi-music-circle:before{content:"\F386"}.mdi-music-circle-outline:before{content:"\FAD3"}.mdi-music-note:before{content:"\F387"}.mdi-music-note-bluetooth:before{content:"\F5FE"}.mdi-music-note-bluetooth-off:before{content:"\F5FF"}.mdi-music-note-eighth:before{content:"\F388"}.mdi-music-note-half:before{content:"\F389"}.mdi-music-note-off:before{content:"\F38A"}.mdi-music-note-plus:before{content:"\FDBA"}.mdi-music-note-quarter:before{content:"\F38B"}.mdi-music-note-sixteenth:before{content:"\F38C"}.mdi-music-note-whole:before{content:"\F38D"}.mdi-music-off:before{content:"\F75A"}.mdi-nail:before{content:"\FDBB"}.mdi-nas:before{content:"\F8F2"}.mdi-nativescript:before{content:"\F87F"}.mdi-nature:before{content:"\F38E"}.mdi-nature-people:before{content:"\F38F"}.mdi-navigation:before{content:"\F390"}.mdi-near-me:before{content:"\F5CD"}.mdi-needle:before{content:"\F391"}.mdi-netflix:before{content:"\F745"}.mdi-network:before{content:"\F6F2"}.mdi-network-off:before{content:"\FC77"}.mdi-network-off-outline:before{content:"\FC78"}.mdi-network-outline:before{content:"\FC79"}.mdi-network-strength-1:before{content:"\F8F3"}.mdi-network-strength-1-alert:before{content:"\F8F4"}.mdi-network-strength-2:before{content:"\F8F5"}.mdi-network-strength-2-alert:before{content:"\F8F6"}.mdi-network-strength-3:before{content:"\F8F7"}.mdi-network-strength-3-alert:before{content:"\F8F8"}.mdi-network-strength-4:before{content:"\F8F9"}.mdi-network-strength-4-alert:before{content:"\F8FA"}.mdi-network-strength-off:before{content:"\F8FB"}.mdi-network-strength-off-outline:before{content:"\F8FC"}.mdi-network-strength-outline:before{content:"\F8FD"}.mdi-new-box:before{content:"\F394"}.mdi-newspaper:before{content:"\F395"}.mdi-nfc:before{content:"\F396"}.mdi-nfc-tap:before{content:"\F397"}.mdi-nfc-variant:before{content:"\F398"}.mdi-ninja:before{content:"\F773"}.mdi-nintendo-switch:before{content:"\F7E0"}.mdi-nodejs:before{content:"\F399"}.mdi-not-equal:before{content:"\F98C"}.mdi-not-equal-variant:before{content:"\F98D"}.mdi-note:before{content:"\F39A"}.mdi-note-multiple:before{content:"\F6B7"}.mdi-note-multiple-outline:before{content:"\F6B8"}.mdi-note-outline:before{content:"\F39B"}.mdi-note-plus:before{content:"\F39C"}.mdi-note-plus-outline:before{content:"\F39D"}.mdi-note-text:before{content:"\F39E"}.mdi-notebook:before{content:"\F82D"}.mdi-notification-clear-all:before{content:"\F39F"}.mdi-npm:before{content:"\F6F6"}.mdi-npm-variant:before{content:"\F98E"}.mdi-npm-variant-outline:before{content:"\F98F"}.mdi-nuke:before{content:"\F6A3"}.mdi-null:before{content:"\F7E1"}.mdi-numeric:before{content:"\F3A0"}.mdi-numeric-0:before{content:"\30"}.mdi-numeric-0-box:before{content:"\F3A1"}.mdi-numeric-0-box-multiple-outline:before{content:"\F3A2"}.mdi-numeric-0-box-outline:before{content:"\F3A3"}.mdi-numeric-0-circle:before{content:"\FC7A"}.mdi-numeric-0-circle-outline:before{content:"\FC7B"}.mdi-numeric-1:before{content:"\31"}.mdi-numeric-1-box:before{content:"\F3A4"}.mdi-numeric-1-box-multiple-outline:before{content:"\F3A5"}.mdi-numeric-1-box-outline:before{content:"\F3A6"}.mdi-numeric-1-circle:before{content:"\FC7C"}.mdi-numeric-1-circle-outline:before{content:"\FC7D"}.mdi-numeric-2:before{content:"\32"}.mdi-numeric-2-box:before{content:"\F3A7"}.mdi-numeric-2-box-multiple-outline:before{content:"\F3A8"}.mdi-numeric-2-box-outline:before{content:"\F3A9"}.mdi-numeric-2-circle:before{content:"\FC7E"}.mdi-numeric-2-circle-outline:before{content:"\FC7F"}.mdi-numeric-3:before{content:"\33"}.mdi-numeric-3-box:before{content:"\F3AA"}.mdi-numeric-3-box-multiple-outline:before{content:"\F3AB"}.mdi-numeric-3-box-outline:before{content:"\F3AC"}.mdi-numeric-3-circle:before{content:"\FC80"}.mdi-numeric-3-circle-outline:before{content:"\FC81"}.mdi-numeric-4:before{content:"\34"}.mdi-numeric-4-box:before{content:"\F3AD"}.mdi-numeric-4-box-multiple-outline:before{content:"\F3AE"}.mdi-numeric-4-box-outline:before{content:"\F3AF"}.mdi-numeric-4-circle:before{content:"\FC82"}.mdi-numeric-4-circle-outline:before{content:"\FC83"}.mdi-numeric-5:before{content:"\35"}.mdi-numeric-5-box:before{content:"\F3B0"}.mdi-numeric-5-box-multiple-outline:before{content:"\F3B1"}.mdi-numeric-5-box-outline:before{content:"\F3B2"}.mdi-numeric-5-circle:before{content:"\FC84"}.mdi-numeric-5-circle-outline:before{content:"\FC85"}.mdi-numeric-6:before{content:"\36"}.mdi-numeric-6-box:before{content:"\F3B3"}.mdi-numeric-6-box-multiple-outline:before{content:"\F3B4"}.mdi-numeric-6-box-outline:before{content:"\F3B5"}.mdi-numeric-6-circle:before{content:"\FC86"}.mdi-numeric-6-circle-outline:before{content:"\FC87"}.mdi-numeric-7:before{content:"\37"}.mdi-numeric-7-box:before{content:"\F3B6"}.mdi-numeric-7-box-multiple-outline:before{content:"\F3B7"}.mdi-numeric-7-box-outline:before{content:"\F3B8"}.mdi-numeric-7-circle:before{content:"\FC88"}.mdi-numeric-7-circle-outline:before{content:"\FC89"}.mdi-numeric-8:before{content:"\38"}.mdi-numeric-8-box:before{content:"\F3B9"}.mdi-numeric-8-box-multiple-outline:before{content:"\F3BA"}.mdi-numeric-8-box-outline:before{content:"\F3BB"}.mdi-numeric-8-circle:before{content:"\FC8A"}.mdi-numeric-8-circle-outline:before{content:"\FC8B"}.mdi-numeric-9:before{content:"\39"}.mdi-numeric-9-box:before{content:"\F3BC"}.mdi-numeric-9-box-multiple-outline:before{content:"\F3BD"}.mdi-numeric-9-box-outline:before{content:"\F3BE"}.mdi-numeric-9-circle:before{content:"\FC8C"}.mdi-numeric-9-circle-outline:before{content:"\FC8D"}.mdi-numeric-9-plus-box:before{content:"\F3BF"}.mdi-numeric-9-plus-box-multiple-outline:before{content:"\F3C0"}.mdi-numeric-9-plus-box-outline:before{content:"\F3C1"}.mdi-numeric-9-plus-circle:before{content:"\FC8E"}.mdi-numeric-9-plus-circle-outline:before{content:"\FC8F"}.mdi-nut:before{content:"\F6F7"}.mdi-nutrition:before{content:"\F3C2"}.mdi-oar:before{content:"\F67B"}.mdi-ocarina:before{content:"\FDBC"}.mdi-octagon:before{content:"\F3C3"}.mdi-octagon-outline:before{content:"\F3C4"}.mdi-octagram:before{content:"\F6F8"}.mdi-octagram-outline:before{content:"\F774"}.mdi-odnoklassniki:before{content:"\F3C5"}.mdi-office:before{content:"\F3C6"}.mdi-office-building:before{content:"\F990"}.mdi-oil:before{content:"\F3C7"}.mdi-oil-temperature:before{content:"\F3C8"}.mdi-omega:before{content:"\F3C9"}.mdi-one-up:before{content:"\FB89"}.mdi-onedrive:before{content:"\F3CA"}.mdi-onenote:before{content:"\F746"}.mdi-onepassword:before{content:"\F880"}.mdi-opacity:before{content:"\F5CC"}.mdi-open-in-app:before{content:"\F3CB"}.mdi-open-in-new:before{content:"\F3CC"}.mdi-open-source-initiative:before{content:"\FB8A"}.mdi-openid:before{content:"\F3CD"}.mdi-opera:before{content:"\F3CE"}.mdi-orbit:before{content:"\F018"}.mdi-origin:before{content:"\FB2B"}.mdi-ornament:before{content:"\F3CF"}.mdi-ornament-variant:before{content:"\F3D0"}.mdi-outlook:before{content:"\FCFE"}.mdi-owl:before{content:"\F3D2"}.mdi-pac-man:before{content:"\FB8B"}.mdi-package:before{content:"\F3D3"}.mdi-package-down:before{content:"\F3D4"}.mdi-package-up:before{content:"\F3D5"}.mdi-package-variant:before{content:"\F3D6"}.mdi-package-variant-closed:before{content:"\F3D7"}.mdi-page-first:before{content:"\F600"}.mdi-page-last:before{content:"\F601"}.mdi-page-layout-body:before{content:"\F6F9"}.mdi-page-layout-footer:before{content:"\F6FA"}.mdi-page-layout-header:before{content:"\F6FB"}.mdi-page-layout-sidebar-left:before{content:"\F6FC"}.mdi-page-layout-sidebar-right:before{content:"\F6FD"}.mdi-page-next:before{content:"\FB8C"}.mdi-page-next-outline:before{content:"\FB8D"}.mdi-page-previous:before{content:"\FB8E"}.mdi-page-previous-outline:before{content:"\FB8F"}.mdi-palette:before{content:"\F3D8"}.mdi-palette-advanced:before{content:"\F3D9"}.mdi-palette-outline:before{content:"\FDE8"}.mdi-palette-swatch:before{content:"\F8B4"}.mdi-pan:before{content:"\FB90"}.mdi-pan-bottom-left:before{content:"\FB91"}.mdi-pan-bottom-right:before{content:"\FB92"}.mdi-pan-down:before{content:"\FB93"}.mdi-pan-horizontal:before{content:"\FB94"}.mdi-pan-left:before{content:"\FB95"}.mdi-pan-right:before{content:"\FB96"}.mdi-pan-top-left:before{content:"\FB97"}.mdi-pan-top-right:before{content:"\FB98"}.mdi-pan-up:before{content:"\FB99"}.mdi-pan-vertical:before{content:"\FB9A"}.mdi-panda:before{content:"\F3DA"}.mdi-pandora:before{content:"\F3DB"}.mdi-panorama:before{content:"\F3DC"}.mdi-panorama-fisheye:before{content:"\F3DD"}.mdi-panorama-horizontal:before{content:"\F3DE"}.mdi-panorama-vertical:before{content:"\F3DF"}.mdi-panorama-wide-angle:before{content:"\F3E0"}.mdi-paper-cut-vertical:before{content:"\F3E1"}.mdi-paperclip:before{content:"\F3E2"}.mdi-parachute:before{content:"\FC90"}.mdi-parachute-outline:before{content:"\FC91"}.mdi-parking:before{content:"\F3E3"}.mdi-passport:before{content:"\F7E2"}.mdi-passport-biometric:before{content:"\FDBD"}.mdi-patreon:before{content:"\F881"}.mdi-pause:before{content:"\F3E4"}.mdi-pause-circle:before{content:"\F3E5"}.mdi-pause-circle-outline:before{content:"\F3E6"}.mdi-pause-octagon:before{content:"\F3E7"}.mdi-pause-octagon-outline:before{content:"\F3E8"}.mdi-paw:before{content:"\F3E9"}.mdi-paw-off:before{content:"\F657"}.mdi-paypal:before{content:"\F882"}.mdi-peace:before{content:"\F883"}.mdi-pen:before{content:"\F3EA"}.mdi-pen-lock:before{content:"\FDBE"}.mdi-pen-minus:before{content:"\FDBF"}.mdi-pen-off:before{content:"\FDC0"}.mdi-pen-plus:before{content:"\FDC1"}.mdi-pen-remove:before{content:"\FDC2"}.mdi-pencil:before{content:"\F3EB"}.mdi-pencil-box:before{content:"\F3EC"}.mdi-pencil-box-outline:before{content:"\F3ED"}.mdi-pencil-circle:before{content:"\F6FE"}.mdi-pencil-circle-outline:before{content:"\F775"}.mdi-pencil-lock:before{content:"\F3EE"}.mdi-pencil-lock-outline:before{content:"\FDC3"}.mdi-pencil-minus:before{content:"\FDC4"}.mdi-pencil-minus-outline:before{content:"\FDC5"}.mdi-pencil-off:before{content:"\F3EF"}.mdi-pencil-off-outline:before{content:"\FDC6"}.mdi-pencil-outline:before{content:"\FC92"}.mdi-pencil-plus:before{content:"\FDC7"}.mdi-pencil-plus-outline:before{content:"\FDC8"}.mdi-pencil-remove:before{content:"\FDC9"}.mdi-pencil-remove-outline:before{content:"\FDCA"}.mdi-pentagon:before{content:"\F6FF"}.mdi-pentagon-outline:before{content:"\F700"}.mdi-percent:before{content:"\F3F0"}.mdi-periodic-table:before{content:"\F8B5"}.mdi-periodic-table-co2:before{content:"\F7E3"}.mdi-periscope:before{content:"\F747"}.mdi-perspective-less:before{content:"\FCFF"}.mdi-perspective-more:before{content:"\FD00"}.mdi-pharmacy:before{content:"\F3F1"}.mdi-phone:before{content:"\F3F2"}.mdi-phone-bluetooth:before{content:"\F3F3"}.mdi-phone-classic:before{content:"\F602"}.mdi-phone-forward:before{content:"\F3F4"}.mdi-phone-hangup:before{content:"\F3F5"}.mdi-phone-in-talk:before{content:"\F3F6"}.mdi-phone-incoming:before{content:"\F3F7"}.mdi-phone-lock:before{content:"\F3F8"}.mdi-phone-log:before{content:"\F3F9"}.mdi-phone-minus:before{content:"\F658"}.mdi-phone-missed:before{content:"\F3FA"}.mdi-phone-off:before{content:"\FDCB"}.mdi-phone-outgoing:before{content:"\F3FB"}.mdi-phone-outline:before{content:"\FDCC"}.mdi-phone-paused:before{content:"\F3FC"}.mdi-phone-plus:before{content:"\F659"}.mdi-phone-return:before{content:"\F82E"}.mdi-phone-rotate-landscape:before{content:"\F884"}.mdi-phone-rotate-portrait:before{content:"\F885"}.mdi-phone-settings:before{content:"\F3FD"}.mdi-phone-voip:before{content:"\F3FE"}.mdi-pi:before{content:"\F3FF"}.mdi-pi-box:before{content:"\F400"}.mdi-pi-hole:before{content:"\FDCD"}.mdi-piano:before{content:"\F67C"}.mdi-pickaxe:before{content:"\F8B6"}.mdi-pier:before{content:"\F886"}.mdi-pier-crane:before{content:"\F887"}.mdi-pig:before{content:"\F401"}.mdi-pill:before{content:"\F402"}.mdi-pillar:before{content:"\F701"}.mdi-pin:before{content:"\F403"}.mdi-pin-off:before{content:"\F404"}.mdi-pin-off-outline:before{content:"\F92F"}.mdi-pin-outline:before{content:"\F930"}.mdi-pine-tree:before{content:"\F405"}.mdi-pine-tree-box:before{content:"\F406"}.mdi-pinterest:before{content:"\F407"}.mdi-pinterest-box:before{content:"\F408"}.mdi-pinwheel:before{content:"\FAD4"}.mdi-pinwheel-outline:before{content:"\FAD5"}.mdi-pipe:before{content:"\F7E4"}.mdi-pipe-disconnected:before{content:"\F7E5"}.mdi-pipe-leak:before{content:"\F888"}.mdi-pirate:before{content:"\FA07"}.mdi-pistol:before{content:"\F702"}.mdi-piston:before{content:"\F889"}.mdi-pizza:before{content:"\F409"}.mdi-play:before{content:"\F40A"}.mdi-play-box-outline:before{content:"\F40B"}.mdi-play-circle:before{content:"\F40C"}.mdi-play-circle-outline:before{content:"\F40D"}.mdi-play-network:before{content:"\F88A"}.mdi-play-network-outline:before{content:"\FC93"}.mdi-play-pause:before{content:"\F40E"}.mdi-play-protected-content:before{content:"\F40F"}.mdi-play-speed:before{content:"\F8FE"}.mdi-playlist-check:before{content:"\F5C7"}.mdi-playlist-edit:before{content:"\F8FF"}.mdi-playlist-minus:before{content:"\F410"}.mdi-playlist-music:before{content:"\FC94"}.mdi-playlist-music-outline:before{content:"\FC95"}.mdi-playlist-play:before{content:"\F411"}.mdi-playlist-plus:before{content:"\F412"}.mdi-playlist-remove:before{content:"\F413"}.mdi-playlist-star:before{content:"\FDCE"}.mdi-playstation:before{content:"\F414"}.mdi-plex:before{content:"\F6B9"}.mdi-plus:before{content:"\F415"}.mdi-plus-box:before{content:"\F416"}.mdi-plus-box-outline:before{content:"\F703"}.mdi-plus-circle:before{content:"\F417"}.mdi-plus-circle-multiple-outline:before{content:"\F418"}.mdi-plus-circle-outline:before{content:"\F419"}.mdi-plus-minus:before{content:"\F991"}.mdi-plus-minus-box:before{content:"\F992"}.mdi-plus-network:before{content:"\F41A"}.mdi-plus-network-outline:before{content:"\FC96"}.mdi-plus-one:before{content:"\F41B"}.mdi-plus-outline:before{content:"\F704"}.mdi-pocket:before{content:"\F41C"}.mdi-podcast:before{content:"\F993"}.mdi-podium:before{content:"\FD01"}.mdi-podium-bronze:before{content:"\FD02"}.mdi-podium-gold:before{content:"\FD03"}.mdi-podium-silver:before{content:"\FD04"}.mdi-point-of-sale:before{content:"\FD6E"}.mdi-pokeball:before{content:"\F41D"}.mdi-pokemon-go:before{content:"\FA08"}.mdi-poker-chip:before{content:"\F82F"}.mdi-polaroid:before{content:"\F41E"}.mdi-poll:before{content:"\F41F"}.mdi-poll-box:before{content:"\F420"}.mdi-polymer:before{content:"\F421"}.mdi-pool:before{content:"\F606"}.mdi-popcorn:before{content:"\F422"}.mdi-postage-stamp:before{content:"\FC97"}.mdi-pot:before{content:"\F65A"}.mdi-pot-mix:before{content:"\F65B"}.mdi-pound:before{content:"\F423"}.mdi-pound-box:before{content:"\F424"}.mdi-power:before{content:"\F425"}.mdi-power-cycle:before{content:"\F900"}.mdi-power-off:before{content:"\F901"}.mdi-power-on:before{content:"\F902"}.mdi-power-plug:before{content:"\F6A4"}.mdi-power-plug-off:before{content:"\F6A5"}.mdi-power-settings:before{content:"\F426"}.mdi-power-sleep:before{content:"\F903"}.mdi-power-socket:before{content:"\F427"}.mdi-power-socket-au:before{content:"\F904"}.mdi-power-socket-eu:before{content:"\F7E6"}.mdi-power-socket-uk:before{content:"\F7E7"}.mdi-power-socket-us:before{content:"\F7E8"}.mdi-power-standby:before{content:"\F905"}.mdi-powershell:before{content:"\FA09"}.mdi-prescription:before{content:"\F705"}.mdi-presentation:before{content:"\F428"}.mdi-presentation-play:before{content:"\F429"}.mdi-printer:before{content:"\F42A"}.mdi-printer-3d:before{content:"\F42B"}.mdi-printer-alert:before{content:"\F42C"}.mdi-printer-settings:before{content:"\F706"}.mdi-printer-wireless:before{content:"\FA0A"}.mdi-priority-high:before{content:"\F603"}.mdi-priority-low:before{content:"\F604"}.mdi-professional-hexagon:before{content:"\F42D"}.mdi-progress-alert:before{content:"\FC98"}.mdi-progress-check:before{content:"\F994"}.mdi-progress-clock:before{content:"\F995"}.mdi-progress-download:before{content:"\F996"}.mdi-progress-upload:before{content:"\F997"}.mdi-progress-wrench:before{content:"\FC99"}.mdi-projector:before{content:"\F42E"}.mdi-projector-screen:before{content:"\F42F"}.mdi-publish:before{content:"\F6A6"}.mdi-pulse:before{content:"\F430"}.mdi-pumpkin:before{content:"\FB9B"}.mdi-puzzle:before{content:"\F431"}.mdi-puzzle-outline:before{content:"\FA65"}.mdi-qi:before{content:"\F998"}.mdi-qqchat:before{content:"\F605"}.mdi-qrcode:before{content:"\F432"}.mdi-qrcode-edit:before{content:"\F8B7"}.mdi-qrcode-scan:before{content:"\F433"}.mdi-quadcopter:before{content:"\F434"}.mdi-quality-high:before{content:"\F435"}.mdi-quality-low:before{content:"\FA0B"}.mdi-quality-medium:before{content:"\FA0C"}.mdi-quicktime:before{content:"\F436"}.mdi-quora:before{content:"\FD05"}.mdi-rabbit:before{content:"\F906"}.mdi-racing-helmet:before{content:"\FD6F"}.mdi-racquetball:before{content:"\FD70"}.mdi-radar:before{content:"\F437"}.mdi-radiator:before{content:"\F438"}.mdi-radiator-disabled:before{content:"\FAD6"}.mdi-radiator-off:before{content:"\FAD7"}.mdi-radio:before{content:"\F439"}.mdi-radio-am:before{content:"\FC9A"}.mdi-radio-fm:before{content:"\FC9B"}.mdi-radio-handheld:before{content:"\F43A"}.mdi-radio-tower:before{content:"\F43B"}.mdi-radioactive:before{content:"\F43C"}.mdi-radiobox-blank:before{content:"\F43D"}.mdi-radiobox-marked:before{content:"\F43E"}.mdi-radius:before{content:"\FC9C"}.mdi-radius-outline:before{content:"\FC9D"}.mdi-raspberry-pi:before{content:"\F43F"}.mdi-ray-end:before{content:"\F440"}.mdi-ray-end-arrow:before{content:"\F441"}.mdi-ray-start:before{content:"\F442"}.mdi-ray-start-arrow:before{content:"\F443"}.mdi-ray-start-end:before{content:"\F444"}.mdi-ray-vertex:before{content:"\F445"}.mdi-react:before{content:"\F707"}.mdi-read:before{content:"\F447"}.mdi-receipt:before{content:"\F449"}.mdi-record:before{content:"\F44A"}.mdi-record-player:before{content:"\F999"}.mdi-record-rec:before{content:"\F44B"}.mdi-recycle:before{content:"\F44C"}.mdi-reddit:before{content:"\F44D"}.mdi-redo:before{content:"\F44E"}.mdi-redo-variant:before{content:"\F44F"}.mdi-reflect-horizontal:before{content:"\FA0D"}.mdi-reflect-vertical:before{content:"\FA0E"}.mdi-refresh:before{content:"\F450"}.mdi-regex:before{content:"\F451"}.mdi-registered-trademark:before{content:"\FA66"}.mdi-relative-scale:before{content:"\F452"}.mdi-reload:before{content:"\F453"}.mdi-reminder:before{content:"\F88B"}.mdi-remote:before{content:"\F454"}.mdi-remote-desktop:before{content:"\F8B8"}.mdi-rename-box:before{content:"\F455"}.mdi-reorder-horizontal:before{content:"\F687"}.mdi-reorder-vertical:before{content:"\F688"}.mdi-repeat:before{content:"\F456"}.mdi-repeat-off:before{content:"\F457"}.mdi-repeat-once:before{content:"\F458"}.mdi-replay:before{content:"\F459"}.mdi-reply:before{content:"\F45A"}.mdi-reply-all:before{content:"\F45B"}.mdi-reproduction:before{content:"\F45C"}.mdi-resistor:before{content:"\FB1F"}.mdi-resistor-nodes:before{content:"\FB20"}.mdi-resize:before{content:"\FA67"}.mdi-resize-bottom-right:before{content:"\F45D"}.mdi-responsive:before{content:"\F45E"}.mdi-restart:before{content:"\F708"}.mdi-restart-off:before{content:"\FD71"}.mdi-restore:before{content:"\F99A"}.mdi-restore-clock:before{content:"\F6A7"}.mdi-rewind:before{content:"\F45F"}.mdi-rewind-10:before{content:"\FD06"}.mdi-rewind-30:before{content:"\FD72"}.mdi-rewind-outline:before{content:"\F709"}.mdi-rhombus:before{content:"\F70A"}.mdi-rhombus-medium:before{content:"\FA0F"}.mdi-rhombus-outline:before{content:"\F70B"}.mdi-rhombus-split:before{content:"\FA10"}.mdi-ribbon:before{content:"\F460"}.mdi-rice:before{content:"\F7E9"}.mdi-ring:before{content:"\F7EA"}.mdi-road:before{content:"\F461"}.mdi-road-variant:before{content:"\F462"}.mdi-robot:before{content:"\F6A8"}.mdi-robot-industrial:before{content:"\FB21"}.mdi-robot-vacuum:before{content:"\F70C"}.mdi-robot-vacuum-variant:before{content:"\F907"}.mdi-rocket:before{content:"\F463"}.mdi-roller-skate:before{content:"\FD07"}.mdi-rollerblade:before{content:"\FD08"}.mdi-rollupjs:before{content:"\FB9C"}.mdi-room-service:before{content:"\F88C"}.mdi-room-service-outline:before{content:"\FD73"}.mdi-rotate-3d:before{content:"\F464"}.mdi-rotate-left:before{content:"\F465"}.mdi-rotate-left-variant:before{content:"\F466"}.mdi-rotate-orbit:before{content:"\FD74"}.mdi-rotate-right:before{content:"\F467"}.mdi-rotate-right-variant:before{content:"\F468"}.mdi-rounded-corner:before{content:"\F607"}.mdi-router-wireless:before{content:"\F469"}.mdi-router-wireless-settings:before{content:"\FA68"}.mdi-routes:before{content:"\F46A"}.mdi-rowing:before{content:"\F608"}.mdi-rss:before{content:"\F46B"}.mdi-rss-box:before{content:"\F46C"}.mdi-ruby:before{content:"\FD09"}.mdi-rugby:before{content:"\FD75"}.mdi-ruler:before{content:"\F46D"}.mdi-ruler-square:before{content:"\FC9E"}.mdi-run:before{content:"\F70D"}.mdi-run-fast:before{content:"\F46E"}.mdi-sack:before{content:"\FD0A"}.mdi-sack-percent:before{content:"\FD0B"}.mdi-safe:before{content:"\FA69"}.mdi-safety-goggles:before{content:"\FD0C"}.mdi-sale:before{content:"\F46F"}.mdi-salesforce:before{content:"\F88D"}.mdi-sass:before{content:"\F7EB"}.mdi-satellite:before{content:"\F470"}.mdi-satellite-uplink:before{content:"\F908"}.mdi-satellite-variant:before{content:"\F471"}.mdi-sausage:before{content:"\F8B9"}.mdi-saxophone:before{content:"\F609"}.mdi-scale:before{content:"\F472"}.mdi-scale-balance:before{content:"\F5D1"}.mdi-scale-bathroom:before{content:"\F473"}.mdi-scanner:before{content:"\F6AA"}.mdi-scanner-off:before{content:"\F909"}.mdi-school:before{content:"\F474"}.mdi-scissors-cutting:before{content:"\FA6A"}.mdi-screen-rotation:before{content:"\F475"}.mdi-screen-rotation-lock:before{content:"\F476"}.mdi-screw-flat-top:before{content:"\FDCF"}.mdi-screw-lag:before{content:"\FDD0"}.mdi-screw-machine-flat-top:before{content:"\FDD1"}.mdi-screw-machine-round-top:before{content:"\FDD2"}.mdi-screw-round-top:before{content:"\FDD3"}.mdi-screwdriver:before{content:"\F477"}.mdi-script:before{content:"\FB9D"}.mdi-script-outline:before{content:"\F478"}.mdi-script-text:before{content:"\FB9E"}.mdi-script-text-outline:before{content:"\FB9F"}.mdi-sd:before{content:"\F479"}.mdi-seal:before{content:"\F47A"}.mdi-search-web:before{content:"\F70E"}.mdi-seat:before{content:"\FC9F"}.mdi-seat-flat:before{content:"\F47B"}.mdi-seat-flat-angled:before{content:"\F47C"}.mdi-seat-individual-suite:before{content:"\F47D"}.mdi-seat-legroom-extra:before{content:"\F47E"}.mdi-seat-legroom-normal:before{content:"\F47F"}.mdi-seat-legroom-reduced:before{content:"\F480"}.mdi-seat-outline:before{content:"\FCA0"}.mdi-seat-recline-extra:before{content:"\F481"}.mdi-seat-recline-normal:before{content:"\F482"}.mdi-seatbelt:before{content:"\FCA1"}.mdi-security:before{content:"\F483"}.mdi-security-network:before{content:"\F484"}.mdi-select:before{content:"\F485"}.mdi-select-all:before{content:"\F486"}.mdi-select-color:before{content:"\FD0D"}.mdi-select-compare:before{content:"\FAD8"}.mdi-select-drag:before{content:"\FA6B"}.mdi-select-inverse:before{content:"\F487"}.mdi-select-off:before{content:"\F488"}.mdi-selection:before{content:"\F489"}.mdi-selection-drag:before{content:"\FA6C"}.mdi-selection-ellipse:before{content:"\FD0E"}.mdi-selection-off:before{content:"\F776"}.mdi-send:before{content:"\F48A"}.mdi-send-circle:before{content:"\FDD4"}.mdi-send-circle-outline:before{content:"\FDD5"}.mdi-send-lock:before{content:"\F7EC"}.mdi-serial-port:before{content:"\F65C"}.mdi-server:before{content:"\F48B"}.mdi-server-minus:before{content:"\F48C"}.mdi-server-network:before{content:"\F48D"}.mdi-server-network-off:before{content:"\F48E"}.mdi-server-off:before{content:"\F48F"}.mdi-server-plus:before{content:"\F490"}.mdi-server-remove:before{content:"\F491"}.mdi-server-security:before{content:"\F492"}.mdi-set-all:before{content:"\F777"}.mdi-set-center:before{content:"\F778"}.mdi-set-center-right:before{content:"\F779"}.mdi-set-left:before{content:"\F77A"}.mdi-set-left-center:before{content:"\F77B"}.mdi-set-left-right:before{content:"\F77C"}.mdi-set-none:before{content:"\F77D"}.mdi-set-right:before{content:"\F77E"}.mdi-set-top-box:before{content:"\F99E"}.mdi-settings:before{content:"\F493"}.mdi-settings-box:before{content:"\F494"}.mdi-settings-helper:before{content:"\FA6D"}.mdi-settings-outline:before{content:"\F8BA"}.mdi-shape:before{content:"\F830"}.mdi-shape-circle-plus:before{content:"\F65D"}.mdi-shape-outline:before{content:"\F831"}.mdi-shape-plus:before{content:"\F495"}.mdi-shape-polygon-plus:before{content:"\F65E"}.mdi-shape-rectangle-plus:before{content:"\F65F"}.mdi-shape-square-plus:before{content:"\F660"}.mdi-share:before{content:"\F496"}.mdi-share-outline:before{content:"\F931"}.mdi-share-variant:before{content:"\F497"}.mdi-sheep:before{content:"\FCA2"}.mdi-shield:before{content:"\F498"}.mdi-shield-account:before{content:"\F88E"}.mdi-shield-account-outline:before{content:"\FA11"}.mdi-shield-airplane:before{content:"\F6BA"}.mdi-shield-airplane-outline:before{content:"\FCA3"}.mdi-shield-check:before{content:"\F565"}.mdi-shield-check-outline:before{content:"\FCA4"}.mdi-shield-cross:before{content:"\FCA5"}.mdi-shield-cross-outline:before{content:"\FCA6"}.mdi-shield-half-full:before{content:"\F77F"}.mdi-shield-home:before{content:"\F689"}.mdi-shield-home-outline:before{content:"\FCA7"}.mdi-shield-key:before{content:"\FBA0"}.mdi-shield-key-outline:before{content:"\FBA1"}.mdi-shield-link-variant:before{content:"\FD0F"}.mdi-shield-link-variant-outline:before{content:"\FD10"}.mdi-shield-lock:before{content:"\F99C"}.mdi-shield-lock-outline:before{content:"\FCA8"}.mdi-shield-off:before{content:"\F99D"}.mdi-shield-off-outline:before{content:"\F99B"}.mdi-shield-outline:before{content:"\F499"}.mdi-shield-plus:before{content:"\FAD9"}.mdi-shield-plus-outline:before{content:"\FADA"}.mdi-shield-remove:before{content:"\FADB"}.mdi-shield-remove-outline:before{content:"\FADC"}.mdi-shield-search:before{content:"\FD76"}.mdi-ship-wheel:before{content:"\F832"}.mdi-shoe-formal:before{content:"\FB22"}.mdi-shoe-heel:before{content:"\FB23"}.mdi-shoe-print:before{content:"\FDD6"}.mdi-shopify:before{content:"\FADD"}.mdi-shopping:before{content:"\F49A"}.mdi-shopping-music:before{content:"\F49B"}.mdi-shovel:before{content:"\F70F"}.mdi-shovel-off:before{content:"\F710"}.mdi-shower:before{content:"\F99F"}.mdi-shower-head:before{content:"\F9A0"}.mdi-shredder:before{content:"\F49C"}.mdi-shuffle:before{content:"\F49D"}.mdi-shuffle-disabled:before{content:"\F49E"}.mdi-shuffle-variant:before{content:"\F49F"}.mdi-sigma:before{content:"\F4A0"}.mdi-sigma-lower:before{content:"\F62B"}.mdi-sign-caution:before{content:"\F4A1"}.mdi-sign-direction:before{content:"\F780"}.mdi-sign-text:before{content:"\F781"}.mdi-signal:before{content:"\F4A2"}.mdi-signal-2g:before{content:"\F711"}.mdi-signal-3g:before{content:"\F712"}.mdi-signal-4g:before{content:"\F713"}.mdi-signal-5g:before{content:"\FA6E"}.mdi-signal-cellular-1:before{content:"\F8BB"}.mdi-signal-cellular-2:before{content:"\F8BC"}.mdi-signal-cellular-3:before{content:"\F8BD"}.mdi-signal-cellular-outline:before{content:"\F8BE"}.mdi-signal-hspa:before{content:"\F714"}.mdi-signal-hspa-plus:before{content:"\F715"}.mdi-signal-off:before{content:"\F782"}.mdi-signal-variant:before{content:"\F60A"}.mdi-signature:before{content:"\FDD7"}.mdi-signature-freehand:before{content:"\FDD8"}.mdi-signature-image:before{content:"\FDD9"}.mdi-signature-text:before{content:"\FDDA"}.mdi-silo:before{content:"\FB24"}.mdi-silverware:before{content:"\F4A3"}.mdi-silverware-fork:before{content:"\F4A4"}.mdi-silverware-fork-knife:before{content:"\FA6F"}.mdi-silverware-spoon:before{content:"\F4A5"}.mdi-silverware-variant:before{content:"\F4A6"}.mdi-sim:before{content:"\F4A7"}.mdi-sim-alert:before{content:"\F4A8"}.mdi-sim-off:before{content:"\F4A9"}.mdi-sina-weibo:before{content:"\FADE"}.mdi-sitemap:before{content:"\F4AA"}.mdi-skate:before{content:"\FD11"}.mdi-skew-less:before{content:"\FD12"}.mdi-skew-more:before{content:"\FD13"}.mdi-skip-backward:before{content:"\F4AB"}.mdi-skip-forward:before{content:"\F4AC"}.mdi-skip-next:before{content:"\F4AD"}.mdi-skip-next-circle:before{content:"\F661"}.mdi-skip-next-circle-outline:before{content:"\F662"}.mdi-skip-previous:before{content:"\F4AE"}.mdi-skip-previous-circle:before{content:"\F663"}.mdi-skip-previous-circle-outline:before{content:"\F664"}.mdi-skull:before{content:"\F68B"}.mdi-skull-crossbones:before{content:"\FBA2"}.mdi-skull-crossbones-outline:before{content:"\FBA3"}.mdi-skull-outline:before{content:"\FBA4"}.mdi-skype:before{content:"\F4AF"}.mdi-skype-business:before{content:"\F4B0"}.mdi-slack:before{content:"\F4B1"}.mdi-slackware:before{content:"\F90A"}.mdi-sleep:before{content:"\F4B2"}.mdi-sleep-off:before{content:"\F4B3"}.mdi-slope-downhill:before{content:"\FDDB"}.mdi-slope-uphill:before{content:"\FDDC"}.mdi-smog:before{content:"\FA70"}.mdi-smoke-detector:before{content:"\F392"}.mdi-smoking:before{content:"\F4B4"}.mdi-smoking-off:before{content:"\F4B5"}.mdi-snapchat:before{content:"\F4B6"}.mdi-snowflake:before{content:"\F716"}.mdi-snowman:before{content:"\F4B7"}.mdi-soccer:before{content:"\F4B8"}.mdi-soccer-field:before{content:"\F833"}.mdi-sofa:before{content:"\F4B9"}.mdi-solar-panel:before{content:"\FD77"}.mdi-solar-panel-large:before{content:"\FD78"}.mdi-solar-power:before{content:"\FA71"}.mdi-solid:before{content:"\F68C"}.mdi-sort:before{content:"\F4BA"}.mdi-sort-alphabetical:before{content:"\F4BB"}.mdi-sort-ascending:before{content:"\F4BC"}.mdi-sort-descending:before{content:"\F4BD"}.mdi-sort-numeric:before{content:"\F4BE"}.mdi-sort-variant:before{content:"\F4BF"}.mdi-sort-variant-lock:before{content:"\FCA9"}.mdi-sort-variant-lock-open:before{content:"\FCAA"}.mdi-soundcloud:before{content:"\F4C0"}.mdi-source-branch:before{content:"\F62C"}.mdi-source-commit:before{content:"\F717"}.mdi-source-commit-end:before{content:"\F718"}.mdi-source-commit-end-local:before{content:"\F719"}.mdi-source-commit-local:before{content:"\F71A"}.mdi-source-commit-next-local:before{content:"\F71B"}.mdi-source-commit-start:before{content:"\F71C"}.mdi-source-commit-start-next-local:before{content:"\F71D"}.mdi-source-fork:before{content:"\F4C1"}.mdi-source-merge:before{content:"\F62D"}.mdi-source-pull:before{content:"\F4C2"}.mdi-source-repository:before{content:"\FCAB"}.mdi-source-repository-multiple:before{content:"\FCAC"}.mdi-soy-sauce:before{content:"\F7ED"}.mdi-spa:before{content:"\FCAD"}.mdi-spa-outline:before{content:"\FCAE"}.mdi-space-invaders:before{content:"\FBA5"}.mdi-speaker:before{content:"\F4C3"}.mdi-speaker-bluetooth:before{content:"\F9A1"}.mdi-speaker-multiple:before{content:"\FD14"}.mdi-speaker-off:before{content:"\F4C4"}.mdi-speaker-wireless:before{content:"\F71E"}.mdi-speedometer:before{content:"\F4C5"}.mdi-spellcheck:before{content:"\F4C6"}.mdi-spider-web:before{content:"\FBA6"}.mdi-spotify:before{content:"\F4C7"}.mdi-spotlight:before{content:"\F4C8"}.mdi-spotlight-beam:before{content:"\F4C9"}.mdi-spray:before{content:"\F665"}.mdi-spray-bottle:before{content:"\FADF"}.mdi-square:before{content:"\F763"}.mdi-square-edit-outline:before{content:"\F90B"}.mdi-square-inc:before{content:"\F4CA"}.mdi-square-inc-cash:before{content:"\F4CB"}.mdi-square-medium:before{content:"\FA12"}.mdi-square-medium-outline:before{content:"\FA13"}.mdi-square-outline:before{content:"\F762"}.mdi-square-root:before{content:"\F783"}.mdi-square-root-box:before{content:"\F9A2"}.mdi-square-small:before{content:"\FA14"}.mdi-squeegee:before{content:"\FAE0"}.mdi-ssh:before{content:"\F8BF"}.mdi-stack-exchange:before{content:"\F60B"}.mdi-stack-overflow:before{content:"\F4CC"}.mdi-stadium:before{content:"\F71F"}.mdi-stairs:before{content:"\F4CD"}.mdi-stamper:before{content:"\FD15"}.mdi-standard-definition:before{content:"\F7EE"}.mdi-star:before{content:"\F4CE"}.mdi-star-box:before{content:"\FA72"}.mdi-star-box-outline:before{content:"\FA73"}.mdi-star-circle:before{content:"\F4CF"}.mdi-star-circle-outline:before{content:"\F9A3"}.mdi-star-face:before{content:"\F9A4"}.mdi-star-four-points:before{content:"\FAE1"}.mdi-star-four-points-outline:before{content:"\FAE2"}.mdi-star-half:before{content:"\F4D0"}.mdi-star-off:before{content:"\F4D1"}.mdi-star-outline:before{content:"\F4D2"}.mdi-star-three-points:before{content:"\FAE3"}.mdi-star-three-points-outline:before{content:"\FAE4"}.mdi-steam:before{content:"\F4D3"}.mdi-steam-box:before{content:"\F90C"}.mdi-steering:before{content:"\F4D4"}.mdi-steering-off:before{content:"\F90D"}.mdi-step-backward:before{content:"\F4D5"}.mdi-step-backward-2:before{content:"\F4D6"}.mdi-step-forward:before{content:"\F4D7"}.mdi-step-forward-2:before{content:"\F4D8"}.mdi-stethoscope:before{content:"\F4D9"}.mdi-sticker:before{content:"\F5D0"}.mdi-sticker-emoji:before{content:"\F784"}.mdi-stocking:before{content:"\F4DA"}.mdi-stop:before{content:"\F4DB"}.mdi-stop-circle:before{content:"\F666"}.mdi-stop-circle-outline:before{content:"\F667"}.mdi-store:before{content:"\F4DC"}.mdi-store-24-hour:before{content:"\F4DD"}.mdi-stove:before{content:"\F4DE"}.mdi-strava:before{content:"\FB25"}.mdi-subdirectory-arrow-left:before{content:"\F60C"}.mdi-subdirectory-arrow-right:before{content:"\F60D"}.mdi-subtitles:before{content:"\FA15"}.mdi-subtitles-outline:before{content:"\FA16"}.mdi-subway:before{content:"\F6AB"}.mdi-subway-alert-variant:before{content:"\FD79"}.mdi-subway-variant:before{content:"\F4DF"}.mdi-summit:before{content:"\F785"}.mdi-sunglasses:before{content:"\F4E0"}.mdi-surround-sound:before{content:"\F5C5"}.mdi-surround-sound-2-0:before{content:"\F7EF"}.mdi-surround-sound-3-1:before{content:"\F7F0"}.mdi-surround-sound-5-1:before{content:"\F7F1"}.mdi-surround-sound-7-1:before{content:"\F7F2"}.mdi-svg:before{content:"\F720"}.mdi-swap-horizontal:before{content:"\F4E1"}.mdi-swap-horizontal-bold:before{content:"\FBA9"}.mdi-swap-horizontal-variant:before{content:"\F8C0"}.mdi-swap-vertical:before{content:"\F4E2"}.mdi-swap-vertical-bold:before{content:"\FBAA"}.mdi-swap-vertical-variant:before{content:"\F8C1"}.mdi-swim:before{content:"\F4E3"}.mdi-switch:before{content:"\F4E4"}.mdi-sword:before{content:"\F4E5"}.mdi-sword-cross:before{content:"\F786"}.mdi-symfony:before{content:"\FAE5"}.mdi-sync:before{content:"\F4E6"}.mdi-sync-alert:before{content:"\F4E7"}.mdi-sync-off:before{content:"\F4E8"}.mdi-tab:before{content:"\F4E9"}.mdi-tab-minus:before{content:"\FB26"}.mdi-tab-plus:before{content:"\F75B"}.mdi-tab-remove:before{content:"\FB27"}.mdi-tab-unselected:before{content:"\F4EA"}.mdi-table:before{content:"\F4EB"}.mdi-table-border:before{content:"\FA17"}.mdi-table-column:before{content:"\F834"}.mdi-table-column-plus-after:before{content:"\F4EC"}.mdi-table-column-plus-before:before{content:"\F4ED"}.mdi-table-column-remove:before{content:"\F4EE"}.mdi-table-column-width:before{content:"\F4EF"}.mdi-table-edit:before{content:"\F4F0"}.mdi-table-large:before{content:"\F4F1"}.mdi-table-merge-cells:before{content:"\F9A5"}.mdi-table-of-contents:before{content:"\F835"}.mdi-table-plus:before{content:"\FA74"}.mdi-table-remove:before{content:"\FA75"}.mdi-table-row:before{content:"\F836"}.mdi-table-row-height:before{content:"\F4F2"}.mdi-table-row-plus-after:before{content:"\F4F3"}.mdi-table-row-plus-before:before{content:"\F4F4"}.mdi-table-row-remove:before{content:"\F4F5"}.mdi-table-search:before{content:"\F90E"}.mdi-table-settings:before{content:"\F837"}.mdi-tablet:before{content:"\F4F6"}.mdi-tablet-android:before{content:"\F4F7"}.mdi-tablet-cellphone:before{content:"\F9A6"}.mdi-tablet-ipad:before{content:"\F4F8"}.mdi-taco:before{content:"\F761"}.mdi-tag:before{content:"\F4F9"}.mdi-tag-faces:before{content:"\F4FA"}.mdi-tag-heart:before{content:"\F68A"}.mdi-tag-heart-outline:before{content:"\FBAB"}.mdi-tag-minus:before{content:"\F90F"}.mdi-tag-multiple:before{content:"\F4FB"}.mdi-tag-outline:before{content:"\F4FC"}.mdi-tag-plus:before{content:"\F721"}.mdi-tag-remove:before{content:"\F722"}.mdi-tag-text-outline:before{content:"\F4FD"}.mdi-tank:before{content:"\FD16"}.mdi-tape-measure:before{content:"\FB28"}.mdi-target:before{content:"\F4FE"}.mdi-target-account:before{content:"\FBAC"}.mdi-target-variant:before{content:"\FA76"}.mdi-taxi:before{content:"\F4FF"}.mdi-tea:before{content:"\FD7A"}.mdi-tea-outline:before{content:"\FD7B"}.mdi-teach:before{content:"\F88F"}.mdi-teamviewer:before{content:"\F500"}.mdi-telegram:before{content:"\F501"}.mdi-telescope:before{content:"\FB29"}.mdi-television:before{content:"\F502"}.mdi-television-box:before{content:"\F838"}.mdi-television-classic:before{content:"\F7F3"}.mdi-television-classic-off:before{content:"\F839"}.mdi-television-guide:before{content:"\F503"}.mdi-television-off:before{content:"\F83A"}.mdi-temperature-celsius:before{content:"\F504"}.mdi-temperature-fahrenheit:before{content:"\F505"}.mdi-temperature-kelvin:before{content:"\F506"}.mdi-tennis:before{content:"\FD7C"}.mdi-tennis-ball:before{content:"\F507"}.mdi-tent:before{content:"\F508"}.mdi-terrain:before{content:"\F509"}.mdi-test-tube:before{content:"\F668"}.mdi-test-tube-empty:before{content:"\F910"}.mdi-test-tube-off:before{content:"\F911"}.mdi-text:before{content:"\F9A7"}.mdi-text-shadow:before{content:"\F669"}.mdi-text-short:before{content:"\F9A8"}.mdi-text-subject:before{content:"\F9A9"}.mdi-text-to-speech:before{content:"\F50A"}.mdi-text-to-speech-off:before{content:"\F50B"}.mdi-textbox:before{content:"\F60E"}.mdi-textbox-password:before{content:"\F7F4"}.mdi-texture:before{content:"\F50C"}.mdi-theater:before{content:"\F50D"}.mdi-theme-light-dark:before{content:"\F50E"}.mdi-thermometer:before{content:"\F50F"}.mdi-thermometer-alert:before{content:"\FDDD"}.mdi-thermometer-chevron-down:before{content:"\FDDE"}.mdi-thermometer-chevron-up:before{content:"\FDDF"}.mdi-thermometer-lines:before{content:"\F510"}.mdi-thermometer-minus:before{content:"\FDE0"}.mdi-thermometer-plus:before{content:"\FDE1"}.mdi-thermostat:before{content:"\F393"}.mdi-thermostat-box:before{content:"\F890"}.mdi-thought-bubble:before{content:"\F7F5"}.mdi-thought-bubble-outline:before{content:"\F7F6"}.mdi-thumb-down:before{content:"\F511"}.mdi-thumb-down-outline:before{content:"\F512"}.mdi-thumb-up:before{content:"\F513"}.mdi-thumb-up-outline:before{content:"\F514"}.mdi-thumbs-up-down:before{content:"\F515"}.mdi-ticket:before{content:"\F516"}.mdi-ticket-account:before{content:"\F517"}.mdi-ticket-confirmation:before{content:"\F518"}.mdi-ticket-outline:before{content:"\F912"}.mdi-ticket-percent:before{content:"\F723"}.mdi-tie:before{content:"\F519"}.mdi-tilde:before{content:"\F724"}.mdi-timelapse:before{content:"\F51A"}.mdi-timeline:before{content:"\FBAD"}.mdi-timeline-outline:before{content:"\FBAE"}.mdi-timeline-text:before{content:"\FBAF"}.mdi-timeline-text-outline:before{content:"\FBB0"}.mdi-timer:before{content:"\F51B"}.mdi-timer-10:before{content:"\F51C"}.mdi-timer-3:before{content:"\F51D"}.mdi-timer-off:before{content:"\F51E"}.mdi-timer-sand:before{content:"\F51F"}.mdi-timer-sand-empty:before{content:"\F6AC"}.mdi-timer-sand-full:before{content:"\F78B"}.mdi-timetable:before{content:"\F520"}.mdi-toaster-oven:before{content:"\FCAF"}.mdi-toggle-switch:before{content:"\F521"}.mdi-toggle-switch-off:before{content:"\F522"}.mdi-toggle-switch-off-outline:before{content:"\FA18"}.mdi-toggle-switch-outline:before{content:"\FA19"}.mdi-toilet:before{content:"\F9AA"}.mdi-toolbox:before{content:"\F9AB"}.mdi-toolbox-outline:before{content:"\F9AC"}.mdi-tooltip:before{content:"\F523"}.mdi-tooltip-account:before{content:"\F00C"}.mdi-tooltip-edit:before{content:"\F524"}.mdi-tooltip-image:before{content:"\F525"}.mdi-tooltip-image-outline:before{content:"\FBB1"}.mdi-tooltip-outline:before{content:"\F526"}.mdi-tooltip-plus:before{content:"\FBB2"}.mdi-tooltip-plus-outline:before{content:"\F527"}.mdi-tooltip-text:before{content:"\F528"}.mdi-tooltip-text-outline:before{content:"\FBB3"}.mdi-tooth:before{content:"\F8C2"}.mdi-tooth-outline:before{content:"\F529"}.mdi-tor:before{content:"\F52A"}.mdi-tortoise:before{content:"\FD17"}.mdi-tournament:before{content:"\F9AD"}.mdi-tower-beach:before{content:"\F680"}.mdi-tower-fire:before{content:"\F681"}.mdi-towing:before{content:"\F83B"}.mdi-track-light:before{content:"\F913"}.mdi-trackpad:before{content:"\F7F7"}.mdi-trackpad-lock:before{content:"\F932"}.mdi-tractor:before{content:"\F891"}.mdi-trademark:before{content:"\FA77"}.mdi-traffic-light:before{content:"\F52B"}.mdi-train:before{content:"\F52C"}.mdi-train-car:before{content:"\FBB4"}.mdi-train-variant:before{content:"\F8C3"}.mdi-tram:before{content:"\F52D"}.mdi-transcribe:before{content:"\F52E"}.mdi-transcribe-close:before{content:"\F52F"}.mdi-transfer-down:before{content:"\FD7D"}.mdi-transfer-left:before{content:"\FD7E"}.mdi-transfer-right:before{content:"\F530"}.mdi-transfer-up:before{content:"\FD7F"}.mdi-transit-connection:before{content:"\FD18"}.mdi-transit-connection-variant:before{content:"\FD19"}.mdi-transit-transfer:before{content:"\F6AD"}.mdi-transition:before{content:"\F914"}.mdi-transition-masked:before{content:"\F915"}.mdi-translate:before{content:"\F5CA"}.mdi-translate-off:before{content:"\FDE2"}.mdi-transmission-tower:before{content:"\FD1A"}.mdi-trash-can:before{content:"\FA78"}.mdi-trash-can-outline:before{content:"\FA79"}.mdi-treasure-chest:before{content:"\F725"}.mdi-tree:before{content:"\F531"}.mdi-trello:before{content:"\F532"}.mdi-trending-down:before{content:"\F533"}.mdi-trending-neutral:before{content:"\F534"}.mdi-trending-up:before{content:"\F535"}.mdi-triangle:before{content:"\F536"}.mdi-triangle-outline:before{content:"\F537"}.mdi-triforce:before{content:"\FBB5"}.mdi-trophy:before{content:"\F538"}.mdi-trophy-award:before{content:"\F539"}.mdi-trophy-broken:before{content:"\FD80"}.mdi-trophy-outline:before{content:"\F53A"}.mdi-trophy-variant:before{content:"\F53B"}.mdi-trophy-variant-outline:before{content:"\F53C"}.mdi-truck:before{content:"\F53D"}.mdi-truck-check:before{content:"\FCB0"}.mdi-truck-delivery:before{content:"\F53E"}.mdi-truck-fast:before{content:"\F787"}.mdi-truck-trailer:before{content:"\F726"}.mdi-tshirt-crew:before{content:"\FA7A"}.mdi-tshirt-crew-outline:before{content:"\F53F"}.mdi-tshirt-v:before{content:"\FA7B"}.mdi-tshirt-v-outline:before{content:"\F540"}.mdi-tumble-dryer:before{content:"\F916"}.mdi-tumblr:before{content:"\F541"}.mdi-tumblr-box:before{content:"\F917"}.mdi-tumblr-reblog:before{content:"\F542"}.mdi-tune:before{content:"\F62E"}.mdi-tune-vertical:before{content:"\F66A"}.mdi-turnstile:before{content:"\FCB1"}.mdi-turnstile-outline:before{content:"\FCB2"}.mdi-turtle:before{content:"\FCB3"}.mdi-twitch:before{content:"\F543"}.mdi-twitter:before{content:"\F544"}.mdi-twitter-box:before{content:"\F545"}.mdi-twitter-circle:before{content:"\F546"}.mdi-twitter-retweet:before{content:"\F547"}.mdi-two-factor-authentication:before{content:"\F9AE"}.mdi-uber:before{content:"\F748"}.mdi-ubisoft:before{content:"\FBB6"}.mdi-ubuntu:before{content:"\F548"}.mdi-ultra-high-definition:before{content:"\F7F8"}.mdi-umbraco:before{content:"\F549"}.mdi-umbrella:before{content:"\F54A"}.mdi-umbrella-closed:before{content:"\F9AF"}.mdi-umbrella-outline:before{content:"\F54B"}.mdi-undo:before{content:"\F54C"}.mdi-undo-variant:before{content:"\F54D"}.mdi-unfold-less-horizontal:before{content:"\F54E"}.mdi-unfold-less-vertical:before{content:"\F75F"}.mdi-unfold-more-horizontal:before{content:"\F54F"}.mdi-unfold-more-vertical:before{content:"\F760"}.mdi-ungroup:before{content:"\F550"}.mdi-unity:before{content:"\F6AE"}.mdi-unreal:before{content:"\F9B0"}.mdi-untappd:before{content:"\F551"}.mdi-update:before{content:"\F6AF"}.mdi-upload:before{content:"\F552"}.mdi-upload-multiple:before{content:"\F83C"}.mdi-upload-network:before{content:"\F6F5"}.mdi-upload-network-outline:before{content:"\FCB4"}.mdi-upload-outline:before{content:"\FDE3"}.mdi-usb:before{content:"\F553"}.mdi-van-passenger:before{content:"\F7F9"}.mdi-van-utility:before{content:"\F7FA"}.mdi-vanish:before{content:"\F7FB"}.mdi-variable:before{content:"\FAE6"}.mdi-vector-arrange-above:before{content:"\F554"}.mdi-vector-arrange-below:before{content:"\F555"}.mdi-vector-bezier:before{content:"\FAE7"}.mdi-vector-circle:before{content:"\F556"}.mdi-vector-circle-variant:before{content:"\F557"}.mdi-vector-combine:before{content:"\F558"}.mdi-vector-curve:before{content:"\F559"}.mdi-vector-difference:before{content:"\F55A"}.mdi-vector-difference-ab:before{content:"\F55B"}.mdi-vector-difference-ba:before{content:"\F55C"}.mdi-vector-ellipse:before{content:"\F892"}.mdi-vector-intersection:before{content:"\F55D"}.mdi-vector-line:before{content:"\F55E"}.mdi-vector-point:before{content:"\F55F"}.mdi-vector-polygon:before{content:"\F560"}.mdi-vector-polyline:before{content:"\F561"}.mdi-vector-radius:before{content:"\F749"}.mdi-vector-rectangle:before{content:"\F5C6"}.mdi-vector-selection:before{content:"\F562"}.mdi-vector-square:before{content:"\F001"}.mdi-vector-triangle:before{content:"\F563"}.mdi-vector-union:before{content:"\F564"}.mdi-venmo:before{content:"\F578"}.mdi-vhs:before{content:"\FA1A"}.mdi-vibrate:before{content:"\F566"}.mdi-vibrate-off:before{content:"\FCB5"}.mdi-video:before{content:"\F567"}.mdi-video-3d:before{content:"\F7FC"}.mdi-video-4k-box:before{content:"\F83D"}.mdi-video-account:before{content:"\F918"}.mdi-video-image:before{content:"\F919"}.mdi-video-input-antenna:before{content:"\F83E"}.mdi-video-input-component:before{content:"\F83F"}.mdi-video-input-hdmi:before{content:"\F840"}.mdi-video-input-svideo:before{content:"\F841"}.mdi-video-minus:before{content:"\F9B1"}.mdi-video-off:before{content:"\F568"}.mdi-video-off-outline:before{content:"\FBB7"}.mdi-video-outline:before{content:"\FBB8"}.mdi-video-plus:before{content:"\F9B2"}.mdi-video-stabilization:before{content:"\F91A"}.mdi-video-switch:before{content:"\F569"}.mdi-video-vintage:before{content:"\FA1B"}.mdi-view-agenda:before{content:"\F56A"}.mdi-view-array:before{content:"\F56B"}.mdi-view-carousel:before{content:"\F56C"}.mdi-view-column:before{content:"\F56D"}.mdi-view-dashboard:before{content:"\F56E"}.mdi-view-dashboard-outline:before{content:"\FA1C"}.mdi-view-dashboard-variant:before{content:"\F842"}.mdi-view-day:before{content:"\F56F"}.mdi-view-grid:before{content:"\F570"}.mdi-view-headline:before{content:"\F571"}.mdi-view-list:before{content:"\F572"}.mdi-view-module:before{content:"\F573"}.mdi-view-parallel:before{content:"\F727"}.mdi-view-quilt:before{content:"\F574"}.mdi-view-sequential:before{content:"\F728"}.mdi-view-split-horizontal:before{content:"\FBA7"}.mdi-view-split-vertical:before{content:"\FBA8"}.mdi-view-stream:before{content:"\F575"}.mdi-view-week:before{content:"\F576"}.mdi-vimeo:before{content:"\F577"}.mdi-violin:before{content:"\F60F"}.mdi-virtual-reality:before{content:"\F893"}.mdi-visual-studio:before{content:"\F610"}.mdi-visual-studio-code:before{content:"\FA1D"}.mdi-vk:before{content:"\F579"}.mdi-vk-box:before{content:"\F57A"}.mdi-vk-circle:before{content:"\F57B"}.mdi-vlc:before{content:"\F57C"}.mdi-voice:before{content:"\F5CB"}.mdi-voicemail:before{content:"\F57D"}.mdi-volleyball:before{content:"\F9B3"}.mdi-volume-high:before{content:"\F57E"}.mdi-volume-low:before{content:"\F57F"}.mdi-volume-medium:before{content:"\F580"}.mdi-volume-minus:before{content:"\F75D"}.mdi-volume-mute:before{content:"\F75E"}.mdi-volume-off:before{content:"\F581"}.mdi-volume-plus:before{content:"\F75C"}.mdi-volume-variant-off:before{content:"\FDE4"}.mdi-vote:before{content:"\FA1E"}.mdi-vote-outline:before{content:"\FA1F"}.mdi-vpn:before{content:"\F582"}.mdi-vuejs:before{content:"\F843"}.mdi-walk:before{content:"\F583"}.mdi-wall:before{content:"\F7FD"}.mdi-wall-sconce:before{content:"\F91B"}.mdi-wall-sconce-flat:before{content:"\F91C"}.mdi-wall-sconce-variant:before{content:"\F91D"}.mdi-wallet:before{content:"\F584"}.mdi-wallet-giftcard:before{content:"\F585"}.mdi-wallet-membership:before{content:"\F586"}.mdi-wallet-outline:before{content:"\FBB9"}.mdi-wallet-travel:before{content:"\F587"}.mdi-wallpaper:before{content:"\FDE5"}.mdi-wan:before{content:"\F588"}.mdi-washing-machine:before{content:"\F729"}.mdi-watch:before{content:"\F589"}.mdi-watch-export:before{content:"\F58A"}.mdi-watch-export-variant:before{content:"\F894"}.mdi-watch-import:before{content:"\F58B"}.mdi-watch-import-variant:before{content:"\F895"}.mdi-watch-variant:before{content:"\F896"}.mdi-watch-vibrate:before{content:"\F6B0"}.mdi-watch-vibrate-off:before{content:"\FCB6"}.mdi-water:before{content:"\F58C"}.mdi-water-off:before{content:"\F58D"}.mdi-water-outline:before{content:"\FDE6"}.mdi-water-percent:before{content:"\F58E"}.mdi-water-pump:before{content:"\F58F"}.mdi-watermark:before{content:"\F612"}.mdi-waves:before{content:"\F78C"}.mdi-waze:before{content:"\FBBA"}.mdi-weather-cloudy:before{content:"\F590"}.mdi-weather-fog:before{content:"\F591"}.mdi-weather-hail:before{content:"\F592"}.mdi-weather-hurricane:before{content:"\F897"}.mdi-weather-lightning:before{content:"\F593"}.mdi-weather-lightning-rainy:before{content:"\F67D"}.mdi-weather-night:before{content:"\F594"}.mdi-weather-partlycloudy:before{content:"\F595"}.mdi-weather-pouring:before{content:"\F596"}.mdi-weather-rainy:before{content:"\F597"}.mdi-weather-snowy:before{content:"\F598"}.mdi-weather-snowy-rainy:before{content:"\F67E"}.mdi-weather-sunny:before{content:"\F599"}.mdi-weather-sunset:before{content:"\F59A"}.mdi-weather-sunset-down:before{content:"\F59B"}.mdi-weather-sunset-up:before{content:"\F59C"}.mdi-weather-windy:before{content:"\F59D"}.mdi-weather-windy-variant:before{content:"\F59E"}.mdi-web:before{content:"\F59F"}.mdi-webcam:before{content:"\F5A0"}.mdi-webhook:before{content:"\F62F"}.mdi-webpack:before{content:"\F72A"}.mdi-wechat:before{content:"\F611"}.mdi-weight:before{content:"\F5A1"}.mdi-weight-gram:before{content:"\FD1B"}.mdi-weight-kilogram:before{content:"\F5A2"}.mdi-weight-pound:before{content:"\F9B4"}.mdi-whatsapp:before{content:"\F5A3"}.mdi-wheelchair-accessibility:before{content:"\F5A4"}.mdi-whistle:before{content:"\F9B5"}.mdi-white-balance-auto:before{content:"\F5A5"}.mdi-white-balance-incandescent:before{content:"\F5A6"}.mdi-white-balance-iridescent:before{content:"\F5A7"}.mdi-white-balance-sunny:before{content:"\F5A8"}.mdi-widgets:before{content:"\F72B"}.mdi-wifi:before{content:"\F5A9"}.mdi-wifi-off:before{content:"\F5AA"}.mdi-wifi-star:before{content:"\FDE7"}.mdi-wifi-strength-1:before{content:"\F91E"}.mdi-wifi-strength-1-alert:before{content:"\F91F"}.mdi-wifi-strength-1-lock:before{content:"\F920"}.mdi-wifi-strength-2:before{content:"\F921"}.mdi-wifi-strength-2-alert:before{content:"\F922"}.mdi-wifi-strength-2-lock:before{content:"\F923"}.mdi-wifi-strength-3:before{content:"\F924"}.mdi-wifi-strength-3-alert:before{content:"\F925"}.mdi-wifi-strength-3-lock:before{content:"\F926"}.mdi-wifi-strength-4:before{content:"\F927"}.mdi-wifi-strength-4-alert:before{content:"\F928"}.mdi-wifi-strength-4-lock:before{content:"\F929"}.mdi-wifi-strength-alert-outline:before{content:"\F92A"}.mdi-wifi-strength-lock-outline:before{content:"\F92B"}.mdi-wifi-strength-off:before{content:"\F92C"}.mdi-wifi-strength-off-outline:before{content:"\F92D"}.mdi-wifi-strength-outline:before{content:"\F92E"}.mdi-wii:before{content:"\F5AB"}.mdi-wiiu:before{content:"\F72C"}.mdi-wikipedia:before{content:"\F5AC"}.mdi-wind-turbine:before{content:"\FD81"}.mdi-window-close:before{content:"\F5AD"}.mdi-window-closed:before{content:"\F5AE"}.mdi-window-maximize:before{content:"\F5AF"}.mdi-window-minimize:before{content:"\F5B0"}.mdi-window-open:before{content:"\F5B1"}.mdi-window-restore:before{content:"\F5B2"}.mdi-windows:before{content:"\F5B3"}.mdi-windows-classic:before{content:"\FA20"}.mdi-wiper:before{content:"\FAE8"}.mdi-wiper-wash:before{content:"\FD82"}.mdi-wordpress:before{content:"\F5B4"}.mdi-worker:before{content:"\F5B5"}.mdi-wrap:before{content:"\F5B6"}.mdi-wrap-disabled:before{content:"\FBBB"}.mdi-wrench:before{content:"\F5B7"}.mdi-wrench-outline:before{content:"\FBBC"}.mdi-wunderlist:before{content:"\F5B8"}.mdi-xamarin:before{content:"\F844"}.mdi-xamarin-outline:before{content:"\F845"}.mdi-xaml:before{content:"\F673"}.mdi-xbox:before{content:"\F5B9"}.mdi-xbox-controller:before{content:"\F5BA"}.mdi-xbox-controller-battery-alert:before{content:"\F74A"}.mdi-xbox-controller-battery-charging:before{content:"\FA21"}.mdi-xbox-controller-battery-empty:before{content:"\F74B"}.mdi-xbox-controller-battery-full:before{content:"\F74C"}.mdi-xbox-controller-battery-low:before{content:"\F74D"}.mdi-xbox-controller-battery-medium:before{content:"\F74E"}.mdi-xbox-controller-battery-unknown:before{content:"\F74F"}.mdi-xbox-controller-off:before{content:"\F5BB"}.mdi-xda:before{content:"\F5BC"}.mdi-xing:before{content:"\F5BD"}.mdi-xing-box:before{content:"\F5BE"}.mdi-xing-circle:before{content:"\F5BF"}.mdi-xml:before{content:"\F5C0"}.mdi-xmpp:before{content:"\F7FE"}.mdi-yahoo:before{content:"\FB2A"}.mdi-yammer:before{content:"\F788"}.mdi-yeast:before{content:"\F5C1"}.mdi-yelp:before{content:"\F5C2"}.mdi-yin-yang:before{content:"\F67F"}.mdi-youtube:before{content:"\F5C3"}.mdi-youtube-creator-studio:before{content:"\F846"}.mdi-youtube-gaming:before{content:"\F847"}.mdi-youtube-subscription:before{content:"\FD1C"}.mdi-youtube-tv:before{content:"\F448"}.mdi-z-wave:before{content:"\FAE9"}.mdi-zend:before{content:"\FAEA"}.mdi-zigbee:before{content:"\FD1D"}.mdi-zip-box:before{content:"\F5C4"}.mdi-zip-disk:before{content:"\FA22"}.mdi-zodiac-aquarius:before{content:"\FA7C"}.mdi-zodiac-aries:before{content:"\FA7D"}.mdi-zodiac-cancer:before{content:"\FA7E"}.mdi-zodiac-capricorn:before{content:"\FA7F"}.mdi-zodiac-gemini:before{content:"\FA80"}.mdi-zodiac-leo:before{content:"\FA81"}.mdi-zodiac-libra:before{content:"\FA82"}.mdi-zodiac-pisces:before{content:"\FA83"}.mdi-zodiac-sagittarius:before{content:"\FA84"}.mdi-zodiac-scorpio:before{content:"\FA85"}.mdi-zodiac-taurus:before{content:"\FA86"}.mdi-zodiac-virgo:before{content:"\FA87"}.mdi-blank:before{content:"\F68C";visibility:hidden}.mdi-18px.mdi-set,.mdi-18px.mdi:before{font-size:18px}.mdi-24px.mdi-set,.mdi-24px.mdi:before{font-size:24px}.mdi-36px.mdi-set,.mdi-36px.mdi:before{font-size:36px}.mdi-48px.mdi-set,.mdi-48px.mdi:before{font-size:48px}.mdi-dark:before{color:rgba(0,0,0,0.54)}.mdi-dark.mdi-inactive:before{color:rgba(0,0,0,0.26)}.mdi-light:before{color:#fff}.mdi-light.mdi-inactive:before{color:rgba(255,255,255,0.3)}.mdi-rotate-45:before{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.mdi-rotate-90:before{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.mdi-rotate-135:before{-webkit-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg)}.mdi-rotate-180:before{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.mdi-rotate-225:before{-webkit-transform:rotate(225deg);-ms-transform:rotate(225deg);transform:rotate(225deg)}.mdi-rotate-270:before{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.mdi-rotate-315:before{-webkit-transform:rotate(315deg);-ms-transform:rotate(315deg);transform:rotate(315deg)}.mdi-flip-h:before{-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.mdi-flip-v:before{-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.mdi-spin:before{-webkit-animation:mdi-spin 2s infinite linear;animation:mdi-spin 2s infinite linear}@-webkit-keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}} +/*# sourceMappingURL=materialdesignicons.min.css.map */ diff --git a/community_server/webroot/js/ensurePassphrase.js b/community_server/webroot/js/ensurePassphrase.js index 485946050..e2f7a98bb 100644 --- a/community_server/webroot/js/ensurePassphrase.js +++ b/community_server/webroot/js/ensurePassphrase.js @@ -265,1165 +265,1165 @@ exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : }).call(this,require("timers").setImmediate,require("timers").clearImmediate) },{"process/browser.js":1,"timers":2}],3:[function(require,module,exports){ (function (global,setImmediate){ -new function() { - -function Vnode(tag, key, attrs0, children, text, dom) { - return {tag: tag, key: key, attrs: attrs0, children: children, text: text, dom: dom, domSize: undefined, state: {}, events: undefined, instance: undefined, skip: false} -} -Vnode.normalize = function(node) { - if (Array.isArray(node)) return Vnode("[", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined) - if (node != null && typeof node !== "object") return Vnode("#", undefined, undefined, node === false ? "" : node, undefined, undefined) - return node -} -Vnode.normalizeChildren = function normalizeChildren(children) { - for (var i = 0; i < children.length; i++) { - children[i] = Vnode.normalize(children[i]) - } - return children -} -var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g -var selectorCache = {} -function hyperscript(selector) { - if (selector == null || typeof selector !== "string" && typeof selector.view !== "function") { - throw Error("The selector must be either a string or a component."); - } - if (typeof selector === "string" && selectorCache[selector] === undefined) { - var match, tag, classes = [], attributes = {} - while (match = selectorParser.exec(selector)) { - var type = match[1], value = match[2] - if (type === "" && value !== "") tag = value - else if (type === "#") attributes.id = value - else if (type === ".") classes.push(value) - else if (match[3][0] === "[") { - var attrValue = match[6] - if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\") - if (match[4] === "class") classes.push(attrValue) - else attributes[match[4]] = attrValue || true - } - } - if (classes.length > 0) attributes.className = classes.join(" ") - selectorCache[selector] = function(attrs, children) { - var hasAttrs = false, childList, text - var className = attrs.className || attrs.class - for (var key in attributes) attrs[key] = attributes[key] - if (className !== undefined) { - if (attrs.class !== undefined) { - attrs.class = undefined - attrs.className = className - } - if (attributes.className !== undefined) attrs.className = attributes.className + " " + className - } - for (var key in attrs) { - if (key !== "key") { - hasAttrs = true - break - } - } - if (Array.isArray(children) && children.length == 1 && children[0] != null && children[0].tag === "#") text = children[0].children - else childList = children - return Vnode(tag || "div", attrs.key, hasAttrs ? attrs : undefined, childList, text, undefined) - } - } - var attrs, children, childrenIndex - if (arguments[1] == null || typeof arguments[1] === "object" && arguments[1].tag === undefined && !Array.isArray(arguments[1])) { - attrs = arguments[1] - childrenIndex = 2 - } - else childrenIndex = 1 - if (arguments.length === childrenIndex + 1) { - children = Array.isArray(arguments[childrenIndex]) ? arguments[childrenIndex] : [arguments[childrenIndex]] - } - else { - children = [] - for (var i = childrenIndex; i < arguments.length; i++) children.push(arguments[i]) - } - if (typeof selector === "string") return selectorCache[selector](attrs || {}, Vnode.normalizeChildren(children)) - return Vnode(selector, attrs && attrs.key, attrs || {}, Vnode.normalizeChildren(children), undefined, undefined) -} -hyperscript.trust = function(html) { - if (html == null) html = "" - return Vnode("<", undefined, undefined, html, undefined, undefined) -} -hyperscript.fragment = function(attrs1, children) { - return Vnode("[", attrs1.key, attrs1, Vnode.normalizeChildren(children), undefined, undefined) -} -var m = hyperscript -/** @constructor */ -var PromisePolyfill = function(executor) { - if (!(this instanceof PromisePolyfill)) throw new Error("Promise must be called with `new`") - if (typeof executor !== "function") throw new TypeError("executor must be a function") - var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false) - var instance = self._instance = {resolvers: resolvers, rejectors: rejectors} - var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout - function handler(list, shouldAbsorb) { - return function execute(value) { - var then - try { - if (shouldAbsorb && value != null && (typeof value === "object" || typeof value === "function") && typeof (then = value.then) === "function") { - if (value === self) throw new TypeError("Promise can't be resolved w/ itself") - executeOnce(then.bind(value)) - } - else { - callAsync(function() { - if (!shouldAbsorb && list.length === 0) console.error("Possible unhandled promise rejection:", value) - for (var i = 0; i < list.length; i++) list[i](value) - resolvers.length = 0, rejectors.length = 0 - instance.state = shouldAbsorb - instance.retry = function() {execute(value)} - }) - } - } - catch (e) { - rejectCurrent(e) - } - } - } - function executeOnce(then) { - var runs = 0 - function run(fn) { - return function(value) { - if (runs++ > 0) return - fn(value) - } - } - var onerror = run(rejectCurrent) - try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)} - } - executeOnce(executor) -} -PromisePolyfill.prototype.then = function(onFulfilled, onRejection) { - var self = this, instance = self._instance - function handle(callback, list, next, state) { - list.push(function(value) { - if (typeof callback !== "function") next(value) - else try {resolveNext(callback(value))} catch (e) {if (rejectNext) rejectNext(e)} - }) - if (typeof instance.retry === "function" && state === instance.state) instance.retry() - } - var resolveNext, rejectNext - var promise = new PromisePolyfill(function(resolve, reject) {resolveNext = resolve, rejectNext = reject}) - handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false) - return promise -} -PromisePolyfill.prototype.catch = function(onRejection) { - return this.then(null, onRejection) -} -PromisePolyfill.resolve = function(value) { - if (value instanceof PromisePolyfill) return value - return new PromisePolyfill(function(resolve) {resolve(value)}) -} -PromisePolyfill.reject = function(value) { - return new PromisePolyfill(function(resolve, reject) {reject(value)}) -} -PromisePolyfill.all = function(list) { - return new PromisePolyfill(function(resolve, reject) { - var total = list.length, count = 0, values = [] - if (list.length === 0) resolve([]) - else for (var i = 0; i < list.length; i++) { - (function(i) { - function consume(value) { - count++ - values[i] = value - if (count === total) resolve(values) - } - if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") { - list[i].then(consume, reject) - } - else consume(list[i]) - })(i) - } - }) -} -PromisePolyfill.race = function(list) { - return new PromisePolyfill(function(resolve, reject) { - for (var i = 0; i < list.length; i++) { - list[i].then(resolve, reject) - } - }) -} -if (typeof window !== "undefined") { - if (typeof window.Promise === "undefined") window.Promise = PromisePolyfill - var PromisePolyfill = window.Promise -} else if (typeof global !== "undefined") { - if (typeof global.Promise === "undefined") global.Promise = PromisePolyfill - var PromisePolyfill = global.Promise -} else { -} -var buildQueryString = function(object) { - if (Object.prototype.toString.call(object) !== "[object Object]") return "" - var args = [] - for (var key0 in object) { - destructure(key0, object[key0]) - } - return args.join("&") - function destructure(key0, value) { - if (Array.isArray(value)) { - for (var i = 0; i < value.length; i++) { - destructure(key0 + "[" + i + "]", value[i]) - } - } - else if (Object.prototype.toString.call(value) === "[object Object]") { - for (var i in value) { - destructure(key0 + "[" + i + "]", value[i]) - } - } - else args.push(encodeURIComponent(key0) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : "")) - } -} -var _8 = function($window, Promise) { - var callbackCount = 0 - var oncompletion - function setCompletionCallback(callback) {oncompletion = callback} - function finalizer() { - var count = 0 - function complete() {if (--count === 0 && typeof oncompletion === "function") oncompletion()} - return function finalize(promise0) { - var then0 = promise0.then - promise0.then = function() { - count++ - var next = then0.apply(promise0, arguments) - next.then(complete, function(e) { - complete() - if (count === 0) throw e - }) - return finalize(next) - } - return promise0 - } - } - function normalize(args, extra) { - if (typeof args === "string") { - var url = args - args = extra || {} - if (args.url == null) args.url = url - } - return args - } - function request(args, extra) { - var finalize = finalizer() - args = normalize(args, extra) - var promise0 = new Promise(function(resolve, reject) { - if (args.method == null) args.method = "GET" - args.method = args.method.toUpperCase() - var useBody = typeof args.useBody === "boolean" ? args.useBody : args.method !== "GET" && args.method !== "TRACE" - if (typeof args.serialize !== "function") args.serialize = typeof FormData !== "undefined" && args.data instanceof FormData ? function(value) {return value} : JSON.stringify - if (typeof args.deserialize !== "function") args.deserialize = deserialize - if (typeof args.extract !== "function") args.extract = extract - args.url = interpolate(args.url, args.data) - if (useBody) args.data = args.serialize(args.data) - else args.url = assemble(args.url, args.data) - var xhr = new $window.XMLHttpRequest() - xhr.open(args.method, args.url, typeof args.async === "boolean" ? args.async : true, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined) - if (args.serialize === JSON.stringify && useBody) { - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8") - } - if (args.deserialize === deserialize) { - xhr.setRequestHeader("Accept", "application/json, text/*") - } - if (args.withCredentials) xhr.withCredentials = args.withCredentials - for (var key in args.headers) if ({}.hasOwnProperty.call(args.headers, key)) { - xhr.setRequestHeader(key, args.headers[key]) - } - if (typeof args.config === "function") xhr = args.config(xhr, args) || xhr - xhr.onreadystatechange = function() { - // Don't throw errors on xhr.abort(). XMLHttpRequests ends up in a state of - // xhr.status == 0 and xhr.readyState == 4 if aborted after open, but before completion. - if (xhr.status && xhr.readyState === 4) { - try { - var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args)) - if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { - resolve(cast(args.type, response)) - } - else { - var error = new Error(xhr.responseText) - for (var key in response) error[key] = response[key] - reject(error) - } - } - catch (e) { - reject(e) - } - } - } - if (useBody && (args.data != null)) xhr.send(args.data) - else xhr.send() - }) - return args.background === true ? promise0 : finalize(promise0) - } - function jsonp(args, extra) { - var finalize = finalizer() - args = normalize(args, extra) - var promise0 = new Promise(function(resolve, reject) { - var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++ - var script = $window.document.createElement("script") - $window[callbackName] = function(data) { - script.parentNode.removeChild(script) - resolve(cast(args.type, data)) - delete $window[callbackName] - } - script.onerror = function() { - script.parentNode.removeChild(script) - reject(new Error("JSONP request failed")) - delete $window[callbackName] - } - if (args.data == null) args.data = {} - args.url = interpolate(args.url, args.data) - args.data[args.callbackKey || "callback"] = callbackName - script.src = assemble(args.url, args.data) - $window.document.documentElement.appendChild(script) - }) - return args.background === true? promise0 : finalize(promise0) - } - function interpolate(url, data) { - if (data == null) return url - var tokens = url.match(/:[^\/]+/gi) || [] - for (var i = 0; i < tokens.length; i++) { - var key = tokens[i].slice(1) - if (data[key] != null) { - url = url.replace(tokens[i], data[key]) - } - } - return url - } - function assemble(url, data) { - var querystring = buildQueryString(data) - if (querystring !== "") { - var prefix = url.indexOf("?") < 0 ? "?" : "&" - url += prefix + querystring - } - return url - } - function deserialize(data) { - try {return data !== "" ? JSON.parse(data) : null} - catch (e) {throw new Error(data)} - } - function extract(xhr) {return xhr.responseText} - function cast(type0, data) { - if (typeof type0 === "function") { - if (Array.isArray(data)) { - for (var i = 0; i < data.length; i++) { - data[i] = new type0(data[i]) - } - } - else return new type0(data) - } - return data - } - return {request: request, jsonp: jsonp, setCompletionCallback: setCompletionCallback} -} -var requestService = _8(window, PromisePolyfill) -var coreRenderer = function($window) { - var $doc = $window.document - var $emptyFragment = $doc.createDocumentFragment() - var onevent - function setEventCallback(callback) {return onevent = callback} - //create - function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) { - for (var i = start; i < end; i++) { - var vnode = vnodes[i] - if (vnode != null) { - insertNode(parent, createNode(vnode, hooks, ns), nextSibling) - } - } - } - function createNode(vnode, hooks, ns) { - var tag = vnode.tag - if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) - if (typeof tag === "string") { - switch (tag) { - case "#": return createText(vnode) - case "<": return createHTML(vnode) - case "[": return createFragment(vnode, hooks, ns) - default: return createElement(vnode, hooks, ns) - } - } - else return createComponent(vnode, hooks, ns) - } - function createText(vnode) { - return vnode.dom = $doc.createTextNode(vnode.children) - } - function createHTML(vnode) { - var match1 = vnode.children.match(/^\s*?<(\w+)/im) || [] - var parent = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match1[1]] || "div" - var temp = $doc.createElement(parent) - temp.innerHTML = vnode.children - vnode.dom = temp.firstChild - vnode.domSize = temp.childNodes.length - var fragment = $doc.createDocumentFragment() - var child - while (child = temp.firstChild) { - fragment.appendChild(child) - } - return fragment - } - function createFragment(vnode, hooks, ns) { - var fragment = $doc.createDocumentFragment() - if (vnode.children != null) { - var children = vnode.children - createNodes(fragment, children, 0, children.length, hooks, null, ns) - } - vnode.dom = fragment.firstChild - vnode.domSize = fragment.childNodes.length - return fragment - } - function createElement(vnode, hooks, ns) { - var tag = vnode.tag - switch (vnode.tag) { - case "svg": ns = "http://www.w3.org/2000/svg"; break - case "math": ns = "http://www.w3.org/1998/Math/MathML"; break - } - var attrs2 = vnode.attrs - var is = attrs2 && attrs2.is - var element = ns ? - is ? $doc.createElementNS(ns, tag, {is: is}) : $doc.createElementNS(ns, tag) : - is ? $doc.createElement(tag, {is: is}) : $doc.createElement(tag) - vnode.dom = element - if (attrs2 != null) { - setAttrs(vnode, attrs2, ns) - } - if (vnode.attrs != null && vnode.attrs.contenteditable != null) { - setContentEditable(vnode) - } - else { - if (vnode.text != null) { - if (vnode.text !== "") element.textContent = vnode.text - else vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] - } - if (vnode.children != null) { - var children = vnode.children - createNodes(element, children, 0, children.length, hooks, null, ns) - setLateAttrs(vnode) - } - } - return element - } - function createComponent(vnode, hooks, ns) { - vnode.state = Object.create(vnode.tag) - var view = vnode.tag.view - if (view.reentrantLock != null) return $emptyFragment - view.reentrantLock = true - initLifecycle(vnode.tag, vnode, hooks) - vnode.instance = Vnode.normalize(view.call(vnode.state, vnode)) - view.reentrantLock = null - if (vnode.instance != null) { - if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments") - var element = createNode(vnode.instance, hooks, ns) - vnode.dom = vnode.instance.dom - vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 - return element - } - else { - vnode.domSize = 0 - return $emptyFragment - } - } - //update - function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) { - if (old === vnodes || old == null && vnodes == null) return - else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, undefined) - else if (vnodes == null) removeNodes(old, 0, old.length, vnodes) - else { - if (old.length === vnodes.length) { - var isUnkeyed = false - for (var i = 0; i < vnodes.length; i++) { - if (vnodes[i] != null && old[i] != null) { - isUnkeyed = vnodes[i].key == null && old[i].key == null - break - } - } - if (isUnkeyed) { - for (var i = 0; i < old.length; i++) { - if (old[i] === vnodes[i]) continue - else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling)) - else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes) - else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns) - } - return - } - } - var recycling = isRecyclable(old, vnodes) - if (recycling) old = old.concat(old.pool) - var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map - while (oldEnd >= oldStart && end >= start) { - var o = old[oldStart], v = vnodes[start] - if (o === v && !recycling) oldStart++, start++ - else if (o == null) oldStart++ - else if (v == null) start++ - else if (o.key === v.key) { - oldStart++, start++ - updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns) - if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) - } - else { - var o = old[oldEnd] - if (o === v && !recycling) oldEnd--, start++ - else if (o == null) oldEnd-- - else if (v == null) start++ - else if (o.key === v.key) { - updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) - if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling)) - oldEnd--, start++ - } - else break - } - } - while (oldEnd >= oldStart && end >= start) { - var o = old[oldEnd], v = vnodes[end] - if (o === v && !recycling) oldEnd--, end-- - else if (o == null) oldEnd-- - else if (v == null) end-- - else if (o.key === v.key) { - updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) - if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) - if (o.dom != null) nextSibling = o.dom - oldEnd--, end-- - } - else { - if (!map) map = getKeyMap(old, oldEnd) - if (v != null) { - var oldIndex = map[v.key] - if (oldIndex != null) { - var movable = old[oldIndex] - updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) - insertNode(parent, toFragment(movable), nextSibling) - old[oldIndex].skip = true - if (movable.dom != null) nextSibling = movable.dom - } - else { - var dom = createNode(v, hooks, undefined) - insertNode(parent, dom, nextSibling) - nextSibling = dom - } - } - end-- - } - if (end < start) break - } - createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns) - removeNodes(old, oldStart, oldEnd + 1, vnodes) - } - } - function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) { - var oldTag = old.tag, tag = vnode.tag - if (oldTag === tag) { - vnode.state = old.state - vnode.events = old.events - if (shouldUpdate(vnode, old)) return - if (vnode.attrs != null) { - updateLifecycle(vnode.attrs, vnode, hooks, recycling) - } - if (typeof oldTag === "string") { - switch (oldTag) { - case "#": updateText(old, vnode); break - case "<": updateHTML(parent, old, vnode, nextSibling); break - case "[": updateFragment(parent, old, vnode, hooks, nextSibling, ns); break - default: updateElement(old, vnode, hooks, ns) - } - } - else updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) - } - else { - removeNode(old, null) - insertNode(parent, createNode(vnode, hooks, ns), nextSibling) - } - } - function updateText(old, vnode) { - if (old.children.toString() !== vnode.children.toString()) { - old.dom.nodeValue = vnode.children - } - vnode.dom = old.dom - } - function updateHTML(parent, old, vnode, nextSibling) { - if (old.children !== vnode.children) { - toFragment(old) - insertNode(parent, createHTML(vnode), nextSibling) - } - else vnode.dom = old.dom, vnode.domSize = old.domSize - } - function updateFragment(parent, old, vnode, hooks, nextSibling, ns) { - updateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns) - var domSize = 0, children = vnode.children - vnode.dom = null - if (children != null) { - for (var i = 0; i < children.length; i++) { - var child = children[i] - if (child != null && child.dom != null) { - if (vnode.dom == null) vnode.dom = child.dom - domSize += child.domSize || 1 - } - } - if (domSize !== 1) vnode.domSize = domSize - } - } - function updateElement(old, vnode, hooks, ns) { - var element = vnode.dom = old.dom - switch (vnode.tag) { - case "svg": ns = "http://www.w3.org/2000/svg"; break - case "math": ns = "http://www.w3.org/1998/Math/MathML"; break - } - if (vnode.tag === "textarea") { - if (vnode.attrs == null) vnode.attrs = {} - if (vnode.text != null) { - vnode.attrs.value = vnode.text //FIXME handle0 multiple children - vnode.text = undefined - } - } - updateAttrs(vnode, old.attrs, vnode.attrs, ns) - if (vnode.attrs != null && vnode.attrs.contenteditable != null) { - setContentEditable(vnode) - } - else if (old.text != null && vnode.text != null && vnode.text !== "") { - if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text - } - else { - if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)] - if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] - updateNodes(element, old.children, vnode.children, hooks, null, ns) - } - } - function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) { - vnode.instance = Vnode.normalize(vnode.tag.view.call(vnode.state, vnode)) - updateLifecycle(vnode.tag, vnode, hooks, recycling) - if (vnode.instance != null) { - if (old.instance == null) insertNode(parent, createNode(vnode.instance, hooks, ns), nextSibling) - else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns) - vnode.dom = vnode.instance.dom - vnode.domSize = vnode.instance.domSize - } - else if (old.instance != null) { - removeNode(old.instance, null) - vnode.dom = undefined - vnode.domSize = 0 - } - else { - vnode.dom = old.dom - vnode.domSize = old.domSize - } - } - function isRecyclable(old, vnodes) { - if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) { - var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0 - var poolChildrenLength = old.pool[0] && old.pool[0].children && old.pool[0].children.length || 0 - var vnodesChildrenLength = vnodes[0] && vnodes[0].children && vnodes[0].children.length || 0 - if (Math.abs(poolChildrenLength - vnodesChildrenLength) <= Math.abs(oldChildrenLength - vnodesChildrenLength)) { - return true - } - } - return false - } - function getKeyMap(vnodes, end) { - var map = {}, i = 0 - for (var i = 0; i < end; i++) { - var vnode = vnodes[i] - if (vnode != null) { - var key2 = vnode.key - if (key2 != null) map[key2] = i - } - } - return map - } - function toFragment(vnode) { - var count0 = vnode.domSize - if (count0 != null || vnode.dom == null) { - var fragment = $doc.createDocumentFragment() - if (count0 > 0) { - var dom = vnode.dom - while (--count0) fragment.appendChild(dom.nextSibling) - fragment.insertBefore(dom, fragment.firstChild) - } - return fragment - } - else return vnode.dom - } - function getNextSibling(vnodes, i, nextSibling) { - for (; i < vnodes.length; i++) { - if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom - } - return nextSibling - } - function insertNode(parent, dom, nextSibling) { - if (nextSibling && nextSibling.parentNode) parent.insertBefore(dom, nextSibling) - else parent.appendChild(dom) - } - function setContentEditable(vnode) { - var children = vnode.children - if (children != null && children.length === 1 && children[0].tag === "<") { - var content = children[0].children - if (vnode.dom.innerHTML !== content) vnode.dom.innerHTML = content - } - else if (vnode.text != null || children != null && children.length !== 0) throw new Error("Child node of a contenteditable must be trusted") - } - //remove - function removeNodes(vnodes, start, end, context) { - for (var i = start; i < end; i++) { - var vnode = vnodes[i] - if (vnode != null) { - if (vnode.skip) vnode.skip = false - else removeNode(vnode, context) - } - } - } - function removeNode(vnode, context) { - var expected = 1, called = 0 - if (vnode.attrs && vnode.attrs.onbeforeremove) { - var result = vnode.attrs.onbeforeremove.call(vnode.state, vnode) - if (result != null && typeof result.then === "function") { - expected++ - result.then(continuation, continuation) - } - } - if (typeof vnode.tag !== "string" && vnode.tag.onbeforeremove) { - var result = vnode.tag.onbeforeremove.call(vnode.state, vnode) - if (result != null && typeof result.then === "function") { - expected++ - result.then(continuation, continuation) - } - } - continuation() - function continuation() { - if (++called === expected) { - onremove(vnode) - if (vnode.dom) { - var count0 = vnode.domSize || 1 - if (count0 > 1) { - var dom = vnode.dom - while (--count0) { - removeNodeFromDOM(dom.nextSibling) - } - } - removeNodeFromDOM(vnode.dom) - if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string") { //TODO test custom elements - if (!context.pool) context.pool = [vnode] - else context.pool.push(vnode) - } - } - } - } - } - function removeNodeFromDOM(node) { - var parent = node.parentNode - if (parent != null) parent.removeChild(node) - } - function onremove(vnode) { - if (vnode.attrs && vnode.attrs.onremove) vnode.attrs.onremove.call(vnode.state, vnode) - if (typeof vnode.tag !== "string" && vnode.tag.onremove) vnode.tag.onremove.call(vnode.state, vnode) - if (vnode.instance != null) onremove(vnode.instance) - else { - var children = vnode.children - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var child = children[i] - if (child != null) onremove(child) - } - } - } - } - //attrs2 - function setAttrs(vnode, attrs2, ns) { - for (var key2 in attrs2) { - setAttr(vnode, key2, null, attrs2[key2], ns) - } - } - function setAttr(vnode, key2, old, value, ns) { - var element = vnode.dom - if (key2 === "key" || key2 === "is" || (old === value && !isFormAttribute(vnode, key2)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key2)) return - var nsLastIndex = key2.indexOf(":") - if (nsLastIndex > -1 && key2.substr(0, nsLastIndex) === "xlink") { - element.setAttributeNS("http://www.w3.org/1999/xlink", key2.slice(nsLastIndex + 1), value) - } - else if (key2[0] === "o" && key2[1] === "n" && typeof value === "function") updateEvent(vnode, key2, value) - else if (key2 === "style") updateStyle(element, old, value) - else if (key2 in element && !isAttribute(key2) && ns === undefined && !isCustomElement(vnode)) { - //setting input[value] to same value by typing on focused element moves cursor to end in Chrome - if (vnode.tag === "input" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return - //setting select[value] to same value while having select open blinks select dropdown in Chrome - if (vnode.tag === "select" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return - //setting option[value] to same value while having select open blinks select dropdown in Chrome - if (vnode.tag === "option" && key2 === "value" && vnode.dom.value === value) return - element[key2] = value - } - else { - if (typeof value === "boolean") { - if (value) element.setAttribute(key2, "") - else element.removeAttribute(key2) - } - else element.setAttribute(key2 === "className" ? "class" : key2, value) - } - } - function setLateAttrs(vnode) { - var attrs2 = vnode.attrs - if (vnode.tag === "select" && attrs2 != null) { - if ("value" in attrs2) setAttr(vnode, "value", null, attrs2.value, undefined) - if ("selectedIndex" in attrs2) setAttr(vnode, "selectedIndex", null, attrs2.selectedIndex, undefined) - } - } - function updateAttrs(vnode, old, attrs2, ns) { - if (attrs2 != null) { - for (var key2 in attrs2) { - setAttr(vnode, key2, old && old[key2], attrs2[key2], ns) - } - } - if (old != null) { - for (var key2 in old) { - if (attrs2 == null || !(key2 in attrs2)) { - if (key2 === "className") key2 = "class" - if (key2[0] === "o" && key2[1] === "n" && !isLifecycleMethod(key2)) updateEvent(vnode, key2, undefined) - else if (key2 !== "key") vnode.dom.removeAttribute(key2) - } - } - } - } - function isFormAttribute(vnode, attr) { - return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === $doc.activeElement - } - function isLifecycleMethod(attr) { - return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate" - } - function isAttribute(attr) { - return attr === "href" || attr === "list" || attr === "form" || attr === "width" || attr === "height"// || attr === "type" - } - function isCustomElement(vnode){ - return vnode.attrs.is || vnode.tag.indexOf("-") > -1 - } - function hasIntegrationMethods(source) { - return source != null && (source.oncreate || source.onupdate || source.onbeforeremove || source.onremove) - } - //style - function updateStyle(element, old, style) { - if (old === style) element.style.cssText = "", old = null - if (style == null) element.style.cssText = "" - else if (typeof style === "string") element.style.cssText = style - else { - if (typeof old === "string") element.style.cssText = "" - for (var key2 in style) { - element.style[key2] = style[key2] - } - if (old != null && typeof old !== "string") { - for (var key2 in old) { - if (!(key2 in style)) element.style[key2] = "" - } - } - } - } - //event - function updateEvent(vnode, key2, value) { - var element = vnode.dom - var callback = typeof onevent !== "function" ? value : function(e) { - var result = value.call(element, e) - onevent.call(element, e) - return result - } - if (key2 in element) element[key2] = typeof value === "function" ? callback : null - else { - var eventName = key2.slice(2) - if (vnode.events === undefined) vnode.events = {} - if (vnode.events[key2] === callback) return - if (vnode.events[key2] != null) element.removeEventListener(eventName, vnode.events[key2], false) - if (typeof value === "function") { - vnode.events[key2] = callback - element.addEventListener(eventName, vnode.events[key2], false) - } - } - } - //lifecycle - function initLifecycle(source, vnode, hooks) { - if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode) - if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) - } - function updateLifecycle(source, vnode, hooks, recycling) { - if (recycling) initLifecycle(source, vnode, hooks) - else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) - } - function shouldUpdate(vnode, old) { - var forceVnodeUpdate, forceComponentUpdate - if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") forceVnodeUpdate = vnode.attrs.onbeforeupdate.call(vnode.state, vnode, old) - if (typeof vnode.tag !== "string" && typeof vnode.tag.onbeforeupdate === "function") forceComponentUpdate = vnode.tag.onbeforeupdate.call(vnode.state, vnode, old) - if (!(forceVnodeUpdate === undefined && forceComponentUpdate === undefined) && !forceVnodeUpdate && !forceComponentUpdate) { - vnode.dom = old.dom - vnode.domSize = old.domSize - vnode.instance = old.instance - return true - } - return false - } - function render(dom, vnodes) { - if (!dom) throw new Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.") - var hooks = [] - var active = $doc.activeElement - // First time0 rendering into a node clears it out - if (dom.vnodes == null) dom.textContent = "" - if (!Array.isArray(vnodes)) vnodes = [vnodes] - updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), hooks, null, undefined) - dom.vnodes = vnodes - for (var i = 0; i < hooks.length; i++) hooks[i]() - if ($doc.activeElement !== active) active.focus() - } - return {render: render, setEventCallback: setEventCallback} -} -function throttle(callback) { - //60fps translates to 16.6ms, round it down since setTimeout requires int - var time = 16 - var last = 0, pending = null - var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout - return function() { - var now = Date.now() - if (last === 0 || now - last >= time) { - last = now - callback() - } - else if (pending === null) { - pending = timeout(function() { - pending = null - callback() - last = Date.now() - }, time - (now - last)) - } - } -} -var _11 = function($window) { - var renderService = coreRenderer($window) - renderService.setEventCallback(function(e) { - if (e.redraw !== false) redraw() - }) - - var callbacks = [] - function subscribe(key1, callback) { - unsubscribe(key1) - callbacks.push(key1, throttle(callback)) - } - function unsubscribe(key1) { - var index = callbacks.indexOf(key1) - if (index > -1) callbacks.splice(index, 2) - } - function redraw() { - for (var i = 1; i < callbacks.length; i += 2) { - callbacks[i]() - } - } - return {subscribe: subscribe, unsubscribe: unsubscribe, redraw: redraw, render: renderService.render} -} -var redrawService = _11(window) -requestService.setCompletionCallback(redrawService.redraw) -var _16 = function(redrawService0) { - return function(root, component) { - if (component === null) { - redrawService0.render(root, []) - redrawService0.unsubscribe(root) - return - } - - if (component.view == null) throw new Error("m.mount(element, component) expects a component, not a vnode") - - var run0 = function() { - redrawService0.render(root, Vnode(component)) - } - redrawService0.subscribe(root, run0) - redrawService0.redraw() - } -} -m.mount = _16(redrawService) -var Promise = PromisePolyfill -var parseQueryString = function(string) { - if (string === "" || string == null) return {} - if (string.charAt(0) === "?") string = string.slice(1) - var entries = string.split("&"), data0 = {}, counters = {} - for (var i = 0; i < entries.length; i++) { - var entry = entries[i].split("=") - var key5 = decodeURIComponent(entry[0]) - var value = entry.length === 2 ? decodeURIComponent(entry[1]) : "" - if (value === "true") value = true - else if (value === "false") value = false - var levels = key5.split(/\]\[?|\[/) - var cursor = data0 - if (key5.indexOf("[") > -1) levels.pop() - for (var j = 0; j < levels.length; j++) { - var level = levels[j], nextLevel = levels[j + 1] - var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10)) - var isValue = j === levels.length - 1 - if (level === "") { - var key5 = levels.slice(0, j).join() - if (counters[key5] == null) counters[key5] = 0 - level = counters[key5]++ - } - if (cursor[level] == null) { - cursor[level] = isValue ? value : isNumber ? [] : {} - } - cursor = cursor[level] - } - } - return data0 -} -var coreRouter = function($window) { - var supportsPushState = typeof $window.history.pushState === "function" - var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout - function normalize1(fragment0) { - var data = $window.location[fragment0].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent) - if (fragment0 === "pathname" && data[0] !== "/") data = "/" + data - return data - } - var asyncId - function debounceAsync(callback0) { - return function() { - if (asyncId != null) return - asyncId = callAsync0(function() { - asyncId = null - callback0() - }) - } - } - function parsePath(path, queryData, hashData) { - var queryIndex = path.indexOf("?") - var hashIndex = path.indexOf("#") - var pathEnd = queryIndex > -1 ? queryIndex : hashIndex > -1 ? hashIndex : path.length - if (queryIndex > -1) { - var queryEnd = hashIndex > -1 ? hashIndex : path.length - var queryParams = parseQueryString(path.slice(queryIndex + 1, queryEnd)) - for (var key4 in queryParams) queryData[key4] = queryParams[key4] - } - if (hashIndex > -1) { - var hashParams = parseQueryString(path.slice(hashIndex + 1)) - for (var key4 in hashParams) hashData[key4] = hashParams[key4] - } - return path.slice(0, pathEnd) - } - var router = {prefix: "#!"} - router.getPath = function() { - var type2 = router.prefix.charAt(0) - switch (type2) { - case "#": return normalize1("hash").slice(router.prefix.length) - case "?": return normalize1("search").slice(router.prefix.length) + normalize1("hash") - default: return normalize1("pathname").slice(router.prefix.length) + normalize1("search") + normalize1("hash") - } - } - router.setPath = function(path, data, options) { - var queryData = {}, hashData = {} - path = parsePath(path, queryData, hashData) - if (data != null) { - for (var key4 in data) queryData[key4] = data[key4] - path = path.replace(/:([^\/]+)/g, function(match2, token) { - delete queryData[token] - return data[token] - }) - } - var query = buildQueryString(queryData) - if (query) path += "?" + query - var hash = buildQueryString(hashData) - if (hash) path += "#" + hash - if (supportsPushState) { - var state = options ? options.state : null - var title = options ? options.title : null - $window.onpopstate() - if (options && options.replace) $window.history.replaceState(state, title, router.prefix + path) - else $window.history.pushState(state, title, router.prefix + path) - } - else $window.location.href = router.prefix + path - } - router.defineRoutes = function(routes, resolve, reject) { - function resolveRoute() { - var path = router.getPath() - var params = {} - var pathname = parsePath(path, params, params) - - var state = $window.history.state - if (state != null) { - for (var k in state) params[k] = state[k] - } - for (var route0 in routes) { - var matcher = new RegExp("^" + route0.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$") - if (matcher.test(pathname)) { - pathname.replace(matcher, function() { - var keys = route0.match(/:[^\/]+/g) || [] - var values = [].slice.call(arguments, 1, -2) - for (var i = 0; i < keys.length; i++) { - params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]) - } - resolve(routes[route0], params, path, route0) - }) - return - } - } - reject(path, params) - } - - if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute) - else if (router.prefix.charAt(0) === "#") $window.onhashchange = resolveRoute - resolveRoute() - } - - return router -} -var _20 = function($window, redrawService0) { - var routeService = coreRouter($window) - var identity = function(v) {return v} - var render1, component, attrs3, currentPath, lastUpdate - var route = function(root, defaultRoute, routes) { - if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined") - var run1 = function() { - if (render1 != null) redrawService0.render(root, render1(Vnode(component, attrs3.key, attrs3))) - } - var bail = function() { - routeService.setPath(defaultRoute, null, {replace: true}) - } - routeService.defineRoutes(routes, function(payload, params, path) { - var update = lastUpdate = function(routeResolver, comp) { - if (update !== lastUpdate) return - component = comp != null && typeof comp.view === "function" ? comp : "div", attrs3 = params, currentPath = path, lastUpdate = null - render1 = (routeResolver.render || identity).bind(routeResolver) - run1() - } - if (payload.view) update({}, payload) - else { - if (payload.onmatch) { - Promise.resolve(payload.onmatch(params, path)).then(function(resolved) { - update(payload, resolved) - }, bail) - } - else update(payload, "div") - } - }, bail) - redrawService0.subscribe(root, run1) - } - route.set = function(path, data, options) { - if (lastUpdate != null) options = {replace: true} - lastUpdate = null - routeService.setPath(path, data, options) - } - route.get = function() {return currentPath} - route.prefix = function(prefix0) {routeService.prefix = prefix0} - route.link = function(vnode1) { - vnode1.dom.setAttribute("href", routeService.prefix + vnode1.attrs.href) - vnode1.dom.onclick = function(e) { - if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return - e.preventDefault() - e.redraw = false - var href = this.getAttribute("href") - if (href.indexOf(routeService.prefix) === 0) href = href.slice(routeService.prefix.length) - route.set(href, undefined, undefined) - } - } - route.param = function(key3) { - if(typeof attrs3 !== "undefined" && typeof key3 !== "undefined") return attrs3[key3] - return attrs3 - } - return route -} -m.route = _20(window, redrawService) -m.withAttr = function(attrName, callback1, context) { - return function(e) { - callback1.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName)) - } -} -var _28 = coreRenderer(window) -m.render = _28.render -m.redraw = redrawService.redraw -m.request = requestService.request -m.jsonp = requestService.jsonp -m.parseQueryString = parseQueryString -m.buildQueryString = buildQueryString -m.version = "1.0.0" -m.vnode = Vnode -if (typeof module !== "undefined") module["exports"] = m -else window.m = m +new function() { + +function Vnode(tag, key, attrs0, children, text, dom) { + return {tag: tag, key: key, attrs: attrs0, children: children, text: text, dom: dom, domSize: undefined, state: {}, events: undefined, instance: undefined, skip: false} +} +Vnode.normalize = function(node) { + if (Array.isArray(node)) return Vnode("[", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined) + if (node != null && typeof node !== "object") return Vnode("#", undefined, undefined, node === false ? "" : node, undefined, undefined) + return node +} +Vnode.normalizeChildren = function normalizeChildren(children) { + for (var i = 0; i < children.length; i++) { + children[i] = Vnode.normalize(children[i]) + } + return children +} +var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g +var selectorCache = {} +function hyperscript(selector) { + if (selector == null || typeof selector !== "string" && typeof selector.view !== "function") { + throw Error("The selector must be either a string or a component."); + } + if (typeof selector === "string" && selectorCache[selector] === undefined) { + var match, tag, classes = [], attributes = {} + while (match = selectorParser.exec(selector)) { + var type = match[1], value = match[2] + if (type === "" && value !== "") tag = value + else if (type === "#") attributes.id = value + else if (type === ".") classes.push(value) + else if (match[3][0] === "[") { + var attrValue = match[6] + if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\") + if (match[4] === "class") classes.push(attrValue) + else attributes[match[4]] = attrValue || true + } + } + if (classes.length > 0) attributes.className = classes.join(" ") + selectorCache[selector] = function(attrs, children) { + var hasAttrs = false, childList, text + var className = attrs.className || attrs.class + for (var key in attributes) attrs[key] = attributes[key] + if (className !== undefined) { + if (attrs.class !== undefined) { + attrs.class = undefined + attrs.className = className + } + if (attributes.className !== undefined) attrs.className = attributes.className + " " + className + } + for (var key in attrs) { + if (key !== "key") { + hasAttrs = true + break + } + } + if (Array.isArray(children) && children.length == 1 && children[0] != null && children[0].tag === "#") text = children[0].children + else childList = children + return Vnode(tag || "div", attrs.key, hasAttrs ? attrs : undefined, childList, text, undefined) + } + } + var attrs, children, childrenIndex + if (arguments[1] == null || typeof arguments[1] === "object" && arguments[1].tag === undefined && !Array.isArray(arguments[1])) { + attrs = arguments[1] + childrenIndex = 2 + } + else childrenIndex = 1 + if (arguments.length === childrenIndex + 1) { + children = Array.isArray(arguments[childrenIndex]) ? arguments[childrenIndex] : [arguments[childrenIndex]] + } + else { + children = [] + for (var i = childrenIndex; i < arguments.length; i++) children.push(arguments[i]) + } + if (typeof selector === "string") return selectorCache[selector](attrs || {}, Vnode.normalizeChildren(children)) + return Vnode(selector, attrs && attrs.key, attrs || {}, Vnode.normalizeChildren(children), undefined, undefined) +} +hyperscript.trust = function(html) { + if (html == null) html = "" + return Vnode("<", undefined, undefined, html, undefined, undefined) +} +hyperscript.fragment = function(attrs1, children) { + return Vnode("[", attrs1.key, attrs1, Vnode.normalizeChildren(children), undefined, undefined) +} +var m = hyperscript +/** @constructor */ +var PromisePolyfill = function(executor) { + if (!(this instanceof PromisePolyfill)) throw new Error("Promise must be called with `new`") + if (typeof executor !== "function") throw new TypeError("executor must be a function") + var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false) + var instance = self._instance = {resolvers: resolvers, rejectors: rejectors} + var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout + function handler(list, shouldAbsorb) { + return function execute(value) { + var then + try { + if (shouldAbsorb && value != null && (typeof value === "object" || typeof value === "function") && typeof (then = value.then) === "function") { + if (value === self) throw new TypeError("Promise can't be resolved w/ itself") + executeOnce(then.bind(value)) + } + else { + callAsync(function() { + if (!shouldAbsorb && list.length === 0) console.error("Possible unhandled promise rejection:", value) + for (var i = 0; i < list.length; i++) list[i](value) + resolvers.length = 0, rejectors.length = 0 + instance.state = shouldAbsorb + instance.retry = function() {execute(value)} + }) + } + } + catch (e) { + rejectCurrent(e) + } + } + } + function executeOnce(then) { + var runs = 0 + function run(fn) { + return function(value) { + if (runs++ > 0) return + fn(value) + } + } + var onerror = run(rejectCurrent) + try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)} + } + executeOnce(executor) +} +PromisePolyfill.prototype.then = function(onFulfilled, onRejection) { + var self = this, instance = self._instance + function handle(callback, list, next, state) { + list.push(function(value) { + if (typeof callback !== "function") next(value) + else try {resolveNext(callback(value))} catch (e) {if (rejectNext) rejectNext(e)} + }) + if (typeof instance.retry === "function" && state === instance.state) instance.retry() + } + var resolveNext, rejectNext + var promise = new PromisePolyfill(function(resolve, reject) {resolveNext = resolve, rejectNext = reject}) + handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false) + return promise +} +PromisePolyfill.prototype.catch = function(onRejection) { + return this.then(null, onRejection) +} +PromisePolyfill.resolve = function(value) { + if (value instanceof PromisePolyfill) return value + return new PromisePolyfill(function(resolve) {resolve(value)}) +} +PromisePolyfill.reject = function(value) { + return new PromisePolyfill(function(resolve, reject) {reject(value)}) +} +PromisePolyfill.all = function(list) { + return new PromisePolyfill(function(resolve, reject) { + var total = list.length, count = 0, values = [] + if (list.length === 0) resolve([]) + else for (var i = 0; i < list.length; i++) { + (function(i) { + function consume(value) { + count++ + values[i] = value + if (count === total) resolve(values) + } + if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") { + list[i].then(consume, reject) + } + else consume(list[i]) + })(i) + } + }) +} +PromisePolyfill.race = function(list) { + return new PromisePolyfill(function(resolve, reject) { + for (var i = 0; i < list.length; i++) { + list[i].then(resolve, reject) + } + }) +} +if (typeof window !== "undefined") { + if (typeof window.Promise === "undefined") window.Promise = PromisePolyfill + var PromisePolyfill = window.Promise +} else if (typeof global !== "undefined") { + if (typeof global.Promise === "undefined") global.Promise = PromisePolyfill + var PromisePolyfill = global.Promise +} else { +} +var buildQueryString = function(object) { + if (Object.prototype.toString.call(object) !== "[object Object]") return "" + var args = [] + for (var key0 in object) { + destructure(key0, object[key0]) + } + return args.join("&") + function destructure(key0, value) { + if (Array.isArray(value)) { + for (var i = 0; i < value.length; i++) { + destructure(key0 + "[" + i + "]", value[i]) + } + } + else if (Object.prototype.toString.call(value) === "[object Object]") { + for (var i in value) { + destructure(key0 + "[" + i + "]", value[i]) + } + } + else args.push(encodeURIComponent(key0) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : "")) + } +} +var _8 = function($window, Promise) { + var callbackCount = 0 + var oncompletion + function setCompletionCallback(callback) {oncompletion = callback} + function finalizer() { + var count = 0 + function complete() {if (--count === 0 && typeof oncompletion === "function") oncompletion()} + return function finalize(promise0) { + var then0 = promise0.then + promise0.then = function() { + count++ + var next = then0.apply(promise0, arguments) + next.then(complete, function(e) { + complete() + if (count === 0) throw e + }) + return finalize(next) + } + return promise0 + } + } + function normalize(args, extra) { + if (typeof args === "string") { + var url = args + args = extra || {} + if (args.url == null) args.url = url + } + return args + } + function request(args, extra) { + var finalize = finalizer() + args = normalize(args, extra) + var promise0 = new Promise(function(resolve, reject) { + if (args.method == null) args.method = "GET" + args.method = args.method.toUpperCase() + var useBody = typeof args.useBody === "boolean" ? args.useBody : args.method !== "GET" && args.method !== "TRACE" + if (typeof args.serialize !== "function") args.serialize = typeof FormData !== "undefined" && args.data instanceof FormData ? function(value) {return value} : JSON.stringify + if (typeof args.deserialize !== "function") args.deserialize = deserialize + if (typeof args.extract !== "function") args.extract = extract + args.url = interpolate(args.url, args.data) + if (useBody) args.data = args.serialize(args.data) + else args.url = assemble(args.url, args.data) + var xhr = new $window.XMLHttpRequest() + xhr.open(args.method, args.url, typeof args.async === "boolean" ? args.async : true, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined) + if (args.serialize === JSON.stringify && useBody) { + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8") + } + if (args.deserialize === deserialize) { + xhr.setRequestHeader("Accept", "application/json, text/*") + } + if (args.withCredentials) xhr.withCredentials = args.withCredentials + for (var key in args.headers) if ({}.hasOwnProperty.call(args.headers, key)) { + xhr.setRequestHeader(key, args.headers[key]) + } + if (typeof args.config === "function") xhr = args.config(xhr, args) || xhr + xhr.onreadystatechange = function() { + // Don't throw errors on xhr.abort(). XMLHttpRequests ends up in a state of + // xhr.status == 0 and xhr.readyState == 4 if aborted after open, but before completion. + if (xhr.status && xhr.readyState === 4) { + try { + var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args)) + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { + resolve(cast(args.type, response)) + } + else { + var error = new Error(xhr.responseText) + for (var key in response) error[key] = response[key] + reject(error) + } + } + catch (e) { + reject(e) + } + } + } + if (useBody && (args.data != null)) xhr.send(args.data) + else xhr.send() + }) + return args.background === true ? promise0 : finalize(promise0) + } + function jsonp(args, extra) { + var finalize = finalizer() + args = normalize(args, extra) + var promise0 = new Promise(function(resolve, reject) { + var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++ + var script = $window.document.createElement("script") + $window[callbackName] = function(data) { + script.parentNode.removeChild(script) + resolve(cast(args.type, data)) + delete $window[callbackName] + } + script.onerror = function() { + script.parentNode.removeChild(script) + reject(new Error("JSONP request failed")) + delete $window[callbackName] + } + if (args.data == null) args.data = {} + args.url = interpolate(args.url, args.data) + args.data[args.callbackKey || "callback"] = callbackName + script.src = assemble(args.url, args.data) + $window.document.documentElement.appendChild(script) + }) + return args.background === true? promise0 : finalize(promise0) + } + function interpolate(url, data) { + if (data == null) return url + var tokens = url.match(/:[^\/]+/gi) || [] + for (var i = 0; i < tokens.length; i++) { + var key = tokens[i].slice(1) + if (data[key] != null) { + url = url.replace(tokens[i], data[key]) + } + } + return url + } + function assemble(url, data) { + var querystring = buildQueryString(data) + if (querystring !== "") { + var prefix = url.indexOf("?") < 0 ? "?" : "&" + url += prefix + querystring + } + return url + } + function deserialize(data) { + try {return data !== "" ? JSON.parse(data) : null} + catch (e) {throw new Error(data)} + } + function extract(xhr) {return xhr.responseText} + function cast(type0, data) { + if (typeof type0 === "function") { + if (Array.isArray(data)) { + for (var i = 0; i < data.length; i++) { + data[i] = new type0(data[i]) + } + } + else return new type0(data) + } + return data + } + return {request: request, jsonp: jsonp, setCompletionCallback: setCompletionCallback} +} +var requestService = _8(window, PromisePolyfill) +var coreRenderer = function($window) { + var $doc = $window.document + var $emptyFragment = $doc.createDocumentFragment() + var onevent + function setEventCallback(callback) {return onevent = callback} + //create + function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) { + for (var i = start; i < end; i++) { + var vnode = vnodes[i] + if (vnode != null) { + insertNode(parent, createNode(vnode, hooks, ns), nextSibling) + } + } + } + function createNode(vnode, hooks, ns) { + var tag = vnode.tag + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) + if (typeof tag === "string") { + switch (tag) { + case "#": return createText(vnode) + case "<": return createHTML(vnode) + case "[": return createFragment(vnode, hooks, ns) + default: return createElement(vnode, hooks, ns) + } + } + else return createComponent(vnode, hooks, ns) + } + function createText(vnode) { + return vnode.dom = $doc.createTextNode(vnode.children) + } + function createHTML(vnode) { + var match1 = vnode.children.match(/^\s*?<(\w+)/im) || [] + var parent = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match1[1]] || "div" + var temp = $doc.createElement(parent) + temp.innerHTML = vnode.children + vnode.dom = temp.firstChild + vnode.domSize = temp.childNodes.length + var fragment = $doc.createDocumentFragment() + var child + while (child = temp.firstChild) { + fragment.appendChild(child) + } + return fragment + } + function createFragment(vnode, hooks, ns) { + var fragment = $doc.createDocumentFragment() + if (vnode.children != null) { + var children = vnode.children + createNodes(fragment, children, 0, children.length, hooks, null, ns) + } + vnode.dom = fragment.firstChild + vnode.domSize = fragment.childNodes.length + return fragment + } + function createElement(vnode, hooks, ns) { + var tag = vnode.tag + switch (vnode.tag) { + case "svg": ns = "http://www.w3.org/2000/svg"; break + case "math": ns = "http://www.w3.org/1998/Math/MathML"; break + } + var attrs2 = vnode.attrs + var is = attrs2 && attrs2.is + var element = ns ? + is ? $doc.createElementNS(ns, tag, {is: is}) : $doc.createElementNS(ns, tag) : + is ? $doc.createElement(tag, {is: is}) : $doc.createElement(tag) + vnode.dom = element + if (attrs2 != null) { + setAttrs(vnode, attrs2, ns) + } + if (vnode.attrs != null && vnode.attrs.contenteditable != null) { + setContentEditable(vnode) + } + else { + if (vnode.text != null) { + if (vnode.text !== "") element.textContent = vnode.text + else vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] + } + if (vnode.children != null) { + var children = vnode.children + createNodes(element, children, 0, children.length, hooks, null, ns) + setLateAttrs(vnode) + } + } + return element + } + function createComponent(vnode, hooks, ns) { + vnode.state = Object.create(vnode.tag) + var view = vnode.tag.view + if (view.reentrantLock != null) return $emptyFragment + view.reentrantLock = true + initLifecycle(vnode.tag, vnode, hooks) + vnode.instance = Vnode.normalize(view.call(vnode.state, vnode)) + view.reentrantLock = null + if (vnode.instance != null) { + if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments") + var element = createNode(vnode.instance, hooks, ns) + vnode.dom = vnode.instance.dom + vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 + return element + } + else { + vnode.domSize = 0 + return $emptyFragment + } + } + //update + function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) { + if (old === vnodes || old == null && vnodes == null) return + else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, undefined) + else if (vnodes == null) removeNodes(old, 0, old.length, vnodes) + else { + if (old.length === vnodes.length) { + var isUnkeyed = false + for (var i = 0; i < vnodes.length; i++) { + if (vnodes[i] != null && old[i] != null) { + isUnkeyed = vnodes[i].key == null && old[i].key == null + break + } + } + if (isUnkeyed) { + for (var i = 0; i < old.length; i++) { + if (old[i] === vnodes[i]) continue + else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling)) + else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes) + else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns) + } + return + } + } + var recycling = isRecyclable(old, vnodes) + if (recycling) old = old.concat(old.pool) + var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map + while (oldEnd >= oldStart && end >= start) { + var o = old[oldStart], v = vnodes[start] + if (o === v && !recycling) oldStart++, start++ + else if (o == null) oldStart++ + else if (v == null) start++ + else if (o.key === v.key) { + oldStart++, start++ + updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns) + if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) + } + else { + var o = old[oldEnd] + if (o === v && !recycling) oldEnd--, start++ + else if (o == null) oldEnd-- + else if (v == null) start++ + else if (o.key === v.key) { + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) + if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling)) + oldEnd--, start++ + } + else break + } + } + while (oldEnd >= oldStart && end >= start) { + var o = old[oldEnd], v = vnodes[end] + if (o === v && !recycling) oldEnd--, end-- + else if (o == null) oldEnd-- + else if (v == null) end-- + else if (o.key === v.key) { + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) + if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) + if (o.dom != null) nextSibling = o.dom + oldEnd--, end-- + } + else { + if (!map) map = getKeyMap(old, oldEnd) + if (v != null) { + var oldIndex = map[v.key] + if (oldIndex != null) { + var movable = old[oldIndex] + updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) + insertNode(parent, toFragment(movable), nextSibling) + old[oldIndex].skip = true + if (movable.dom != null) nextSibling = movable.dom + } + else { + var dom = createNode(v, hooks, undefined) + insertNode(parent, dom, nextSibling) + nextSibling = dom + } + } + end-- + } + if (end < start) break + } + createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns) + removeNodes(old, oldStart, oldEnd + 1, vnodes) + } + } + function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) { + var oldTag = old.tag, tag = vnode.tag + if (oldTag === tag) { + vnode.state = old.state + vnode.events = old.events + if (shouldUpdate(vnode, old)) return + if (vnode.attrs != null) { + updateLifecycle(vnode.attrs, vnode, hooks, recycling) + } + if (typeof oldTag === "string") { + switch (oldTag) { + case "#": updateText(old, vnode); break + case "<": updateHTML(parent, old, vnode, nextSibling); break + case "[": updateFragment(parent, old, vnode, hooks, nextSibling, ns); break + default: updateElement(old, vnode, hooks, ns) + } + } + else updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) + } + else { + removeNode(old, null) + insertNode(parent, createNode(vnode, hooks, ns), nextSibling) + } + } + function updateText(old, vnode) { + if (old.children.toString() !== vnode.children.toString()) { + old.dom.nodeValue = vnode.children + } + vnode.dom = old.dom + } + function updateHTML(parent, old, vnode, nextSibling) { + if (old.children !== vnode.children) { + toFragment(old) + insertNode(parent, createHTML(vnode), nextSibling) + } + else vnode.dom = old.dom, vnode.domSize = old.domSize + } + function updateFragment(parent, old, vnode, hooks, nextSibling, ns) { + updateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns) + var domSize = 0, children = vnode.children + vnode.dom = null + if (children != null) { + for (var i = 0; i < children.length; i++) { + var child = children[i] + if (child != null && child.dom != null) { + if (vnode.dom == null) vnode.dom = child.dom + domSize += child.domSize || 1 + } + } + if (domSize !== 1) vnode.domSize = domSize + } + } + function updateElement(old, vnode, hooks, ns) { + var element = vnode.dom = old.dom + switch (vnode.tag) { + case "svg": ns = "http://www.w3.org/2000/svg"; break + case "math": ns = "http://www.w3.org/1998/Math/MathML"; break + } + if (vnode.tag === "textarea") { + if (vnode.attrs == null) vnode.attrs = {} + if (vnode.text != null) { + vnode.attrs.value = vnode.text //FIXME handle0 multiple children + vnode.text = undefined + } + } + updateAttrs(vnode, old.attrs, vnode.attrs, ns) + if (vnode.attrs != null && vnode.attrs.contenteditable != null) { + setContentEditable(vnode) + } + else if (old.text != null && vnode.text != null && vnode.text !== "") { + if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text + } + else { + if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)] + if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] + updateNodes(element, old.children, vnode.children, hooks, null, ns) + } + } + function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) { + vnode.instance = Vnode.normalize(vnode.tag.view.call(vnode.state, vnode)) + updateLifecycle(vnode.tag, vnode, hooks, recycling) + if (vnode.instance != null) { + if (old.instance == null) insertNode(parent, createNode(vnode.instance, hooks, ns), nextSibling) + else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns) + vnode.dom = vnode.instance.dom + vnode.domSize = vnode.instance.domSize + } + else if (old.instance != null) { + removeNode(old.instance, null) + vnode.dom = undefined + vnode.domSize = 0 + } + else { + vnode.dom = old.dom + vnode.domSize = old.domSize + } + } + function isRecyclable(old, vnodes) { + if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) { + var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0 + var poolChildrenLength = old.pool[0] && old.pool[0].children && old.pool[0].children.length || 0 + var vnodesChildrenLength = vnodes[0] && vnodes[0].children && vnodes[0].children.length || 0 + if (Math.abs(poolChildrenLength - vnodesChildrenLength) <= Math.abs(oldChildrenLength - vnodesChildrenLength)) { + return true + } + } + return false + } + function getKeyMap(vnodes, end) { + var map = {}, i = 0 + for (var i = 0; i < end; i++) { + var vnode = vnodes[i] + if (vnode != null) { + var key2 = vnode.key + if (key2 != null) map[key2] = i + } + } + return map + } + function toFragment(vnode) { + var count0 = vnode.domSize + if (count0 != null || vnode.dom == null) { + var fragment = $doc.createDocumentFragment() + if (count0 > 0) { + var dom = vnode.dom + while (--count0) fragment.appendChild(dom.nextSibling) + fragment.insertBefore(dom, fragment.firstChild) + } + return fragment + } + else return vnode.dom + } + function getNextSibling(vnodes, i, nextSibling) { + for (; i < vnodes.length; i++) { + if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom + } + return nextSibling + } + function insertNode(parent, dom, nextSibling) { + if (nextSibling && nextSibling.parentNode) parent.insertBefore(dom, nextSibling) + else parent.appendChild(dom) + } + function setContentEditable(vnode) { + var children = vnode.children + if (children != null && children.length === 1 && children[0].tag === "<") { + var content = children[0].children + if (vnode.dom.innerHTML !== content) vnode.dom.innerHTML = content + } + else if (vnode.text != null || children != null && children.length !== 0) throw new Error("Child node of a contenteditable must be trusted") + } + //remove + function removeNodes(vnodes, start, end, context) { + for (var i = start; i < end; i++) { + var vnode = vnodes[i] + if (vnode != null) { + if (vnode.skip) vnode.skip = false + else removeNode(vnode, context) + } + } + } + function removeNode(vnode, context) { + var expected = 1, called = 0 + if (vnode.attrs && vnode.attrs.onbeforeremove) { + var result = vnode.attrs.onbeforeremove.call(vnode.state, vnode) + if (result != null && typeof result.then === "function") { + expected++ + result.then(continuation, continuation) + } + } + if (typeof vnode.tag !== "string" && vnode.tag.onbeforeremove) { + var result = vnode.tag.onbeforeremove.call(vnode.state, vnode) + if (result != null && typeof result.then === "function") { + expected++ + result.then(continuation, continuation) + } + } + continuation() + function continuation() { + if (++called === expected) { + onremove(vnode) + if (vnode.dom) { + var count0 = vnode.domSize || 1 + if (count0 > 1) { + var dom = vnode.dom + while (--count0) { + removeNodeFromDOM(dom.nextSibling) + } + } + removeNodeFromDOM(vnode.dom) + if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string") { //TODO test custom elements + if (!context.pool) context.pool = [vnode] + else context.pool.push(vnode) + } + } + } + } + } + function removeNodeFromDOM(node) { + var parent = node.parentNode + if (parent != null) parent.removeChild(node) + } + function onremove(vnode) { + if (vnode.attrs && vnode.attrs.onremove) vnode.attrs.onremove.call(vnode.state, vnode) + if (typeof vnode.tag !== "string" && vnode.tag.onremove) vnode.tag.onremove.call(vnode.state, vnode) + if (vnode.instance != null) onremove(vnode.instance) + else { + var children = vnode.children + if (Array.isArray(children)) { + for (var i = 0; i < children.length; i++) { + var child = children[i] + if (child != null) onremove(child) + } + } + } + } + //attrs2 + function setAttrs(vnode, attrs2, ns) { + for (var key2 in attrs2) { + setAttr(vnode, key2, null, attrs2[key2], ns) + } + } + function setAttr(vnode, key2, old, value, ns) { + var element = vnode.dom + if (key2 === "key" || key2 === "is" || (old === value && !isFormAttribute(vnode, key2)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key2)) return + var nsLastIndex = key2.indexOf(":") + if (nsLastIndex > -1 && key2.substr(0, nsLastIndex) === "xlink") { + element.setAttributeNS("http://www.w3.org/1999/xlink", key2.slice(nsLastIndex + 1), value) + } + else if (key2[0] === "o" && key2[1] === "n" && typeof value === "function") updateEvent(vnode, key2, value) + else if (key2 === "style") updateStyle(element, old, value) + else if (key2 in element && !isAttribute(key2) && ns === undefined && !isCustomElement(vnode)) { + //setting input[value] to same value by typing on focused element moves cursor to end in Chrome + if (vnode.tag === "input" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return + //setting select[value] to same value while having select open blinks select dropdown in Chrome + if (vnode.tag === "select" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return + //setting option[value] to same value while having select open blinks select dropdown in Chrome + if (vnode.tag === "option" && key2 === "value" && vnode.dom.value === value) return + element[key2] = value + } + else { + if (typeof value === "boolean") { + if (value) element.setAttribute(key2, "") + else element.removeAttribute(key2) + } + else element.setAttribute(key2 === "className" ? "class" : key2, value) + } + } + function setLateAttrs(vnode) { + var attrs2 = vnode.attrs + if (vnode.tag === "select" && attrs2 != null) { + if ("value" in attrs2) setAttr(vnode, "value", null, attrs2.value, undefined) + if ("selectedIndex" in attrs2) setAttr(vnode, "selectedIndex", null, attrs2.selectedIndex, undefined) + } + } + function updateAttrs(vnode, old, attrs2, ns) { + if (attrs2 != null) { + for (var key2 in attrs2) { + setAttr(vnode, key2, old && old[key2], attrs2[key2], ns) + } + } + if (old != null) { + for (var key2 in old) { + if (attrs2 == null || !(key2 in attrs2)) { + if (key2 === "className") key2 = "class" + if (key2[0] === "o" && key2[1] === "n" && !isLifecycleMethod(key2)) updateEvent(vnode, key2, undefined) + else if (key2 !== "key") vnode.dom.removeAttribute(key2) + } + } + } + } + function isFormAttribute(vnode, attr) { + return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === $doc.activeElement + } + function isLifecycleMethod(attr) { + return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate" + } + function isAttribute(attr) { + return attr === "href" || attr === "list" || attr === "form" || attr === "width" || attr === "height"// || attr === "type" + } + function isCustomElement(vnode){ + return vnode.attrs.is || vnode.tag.indexOf("-") > -1 + } + function hasIntegrationMethods(source) { + return source != null && (source.oncreate || source.onupdate || source.onbeforeremove || source.onremove) + } + //style + function updateStyle(element, old, style) { + if (old === style) element.style.cssText = "", old = null + if (style == null) element.style.cssText = "" + else if (typeof style === "string") element.style.cssText = style + else { + if (typeof old === "string") element.style.cssText = "" + for (var key2 in style) { + element.style[key2] = style[key2] + } + if (old != null && typeof old !== "string") { + for (var key2 in old) { + if (!(key2 in style)) element.style[key2] = "" + } + } + } + } + //event + function updateEvent(vnode, key2, value) { + var element = vnode.dom + var callback = typeof onevent !== "function" ? value : function(e) { + var result = value.call(element, e) + onevent.call(element, e) + return result + } + if (key2 in element) element[key2] = typeof value === "function" ? callback : null + else { + var eventName = key2.slice(2) + if (vnode.events === undefined) vnode.events = {} + if (vnode.events[key2] === callback) return + if (vnode.events[key2] != null) element.removeEventListener(eventName, vnode.events[key2], false) + if (typeof value === "function") { + vnode.events[key2] = callback + element.addEventListener(eventName, vnode.events[key2], false) + } + } + } + //lifecycle + function initLifecycle(source, vnode, hooks) { + if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode) + if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) + } + function updateLifecycle(source, vnode, hooks, recycling) { + if (recycling) initLifecycle(source, vnode, hooks) + else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) + } + function shouldUpdate(vnode, old) { + var forceVnodeUpdate, forceComponentUpdate + if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") forceVnodeUpdate = vnode.attrs.onbeforeupdate.call(vnode.state, vnode, old) + if (typeof vnode.tag !== "string" && typeof vnode.tag.onbeforeupdate === "function") forceComponentUpdate = vnode.tag.onbeforeupdate.call(vnode.state, vnode, old) + if (!(forceVnodeUpdate === undefined && forceComponentUpdate === undefined) && !forceVnodeUpdate && !forceComponentUpdate) { + vnode.dom = old.dom + vnode.domSize = old.domSize + vnode.instance = old.instance + return true + } + return false + } + function render(dom, vnodes) { + if (!dom) throw new Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.") + var hooks = [] + var active = $doc.activeElement + // First time0 rendering into a node clears it out + if (dom.vnodes == null) dom.textContent = "" + if (!Array.isArray(vnodes)) vnodes = [vnodes] + updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), hooks, null, undefined) + dom.vnodes = vnodes + for (var i = 0; i < hooks.length; i++) hooks[i]() + if ($doc.activeElement !== active) active.focus() + } + return {render: render, setEventCallback: setEventCallback} +} +function throttle(callback) { + //60fps translates to 16.6ms, round it down since setTimeout requires int + var time = 16 + var last = 0, pending = null + var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout + return function() { + var now = Date.now() + if (last === 0 || now - last >= time) { + last = now + callback() + } + else if (pending === null) { + pending = timeout(function() { + pending = null + callback() + last = Date.now() + }, time - (now - last)) + } + } +} +var _11 = function($window) { + var renderService = coreRenderer($window) + renderService.setEventCallback(function(e) { + if (e.redraw !== false) redraw() + }) + + var callbacks = [] + function subscribe(key1, callback) { + unsubscribe(key1) + callbacks.push(key1, throttle(callback)) + } + function unsubscribe(key1) { + var index = callbacks.indexOf(key1) + if (index > -1) callbacks.splice(index, 2) + } + function redraw() { + for (var i = 1; i < callbacks.length; i += 2) { + callbacks[i]() + } + } + return {subscribe: subscribe, unsubscribe: unsubscribe, redraw: redraw, render: renderService.render} +} +var redrawService = _11(window) +requestService.setCompletionCallback(redrawService.redraw) +var _16 = function(redrawService0) { + return function(root, component) { + if (component === null) { + redrawService0.render(root, []) + redrawService0.unsubscribe(root) + return + } + + if (component.view == null) throw new Error("m.mount(element, component) expects a component, not a vnode") + + var run0 = function() { + redrawService0.render(root, Vnode(component)) + } + redrawService0.subscribe(root, run0) + redrawService0.redraw() + } +} +m.mount = _16(redrawService) +var Promise = PromisePolyfill +var parseQueryString = function(string) { + if (string === "" || string == null) return {} + if (string.charAt(0) === "?") string = string.slice(1) + var entries = string.split("&"), data0 = {}, counters = {} + for (var i = 0; i < entries.length; i++) { + var entry = entries[i].split("=") + var key5 = decodeURIComponent(entry[0]) + var value = entry.length === 2 ? decodeURIComponent(entry[1]) : "" + if (value === "true") value = true + else if (value === "false") value = false + var levels = key5.split(/\]\[?|\[/) + var cursor = data0 + if (key5.indexOf("[") > -1) levels.pop() + for (var j = 0; j < levels.length; j++) { + var level = levels[j], nextLevel = levels[j + 1] + var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10)) + var isValue = j === levels.length - 1 + if (level === "") { + var key5 = levels.slice(0, j).join() + if (counters[key5] == null) counters[key5] = 0 + level = counters[key5]++ + } + if (cursor[level] == null) { + cursor[level] = isValue ? value : isNumber ? [] : {} + } + cursor = cursor[level] + } + } + return data0 +} +var coreRouter = function($window) { + var supportsPushState = typeof $window.history.pushState === "function" + var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout + function normalize1(fragment0) { + var data = $window.location[fragment0].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent) + if (fragment0 === "pathname" && data[0] !== "/") data = "/" + data + return data + } + var asyncId + function debounceAsync(callback0) { + return function() { + if (asyncId != null) return + asyncId = callAsync0(function() { + asyncId = null + callback0() + }) + } + } + function parsePath(path, queryData, hashData) { + var queryIndex = path.indexOf("?") + var hashIndex = path.indexOf("#") + var pathEnd = queryIndex > -1 ? queryIndex : hashIndex > -1 ? hashIndex : path.length + if (queryIndex > -1) { + var queryEnd = hashIndex > -1 ? hashIndex : path.length + var queryParams = parseQueryString(path.slice(queryIndex + 1, queryEnd)) + for (var key4 in queryParams) queryData[key4] = queryParams[key4] + } + if (hashIndex > -1) { + var hashParams = parseQueryString(path.slice(hashIndex + 1)) + for (var key4 in hashParams) hashData[key4] = hashParams[key4] + } + return path.slice(0, pathEnd) + } + var router = {prefix: "#!"} + router.getPath = function() { + var type2 = router.prefix.charAt(0) + switch (type2) { + case "#": return normalize1("hash").slice(router.prefix.length) + case "?": return normalize1("search").slice(router.prefix.length) + normalize1("hash") + default: return normalize1("pathname").slice(router.prefix.length) + normalize1("search") + normalize1("hash") + } + } + router.setPath = function(path, data, options) { + var queryData = {}, hashData = {} + path = parsePath(path, queryData, hashData) + if (data != null) { + for (var key4 in data) queryData[key4] = data[key4] + path = path.replace(/:([^\/]+)/g, function(match2, token) { + delete queryData[token] + return data[token] + }) + } + var query = buildQueryString(queryData) + if (query) path += "?" + query + var hash = buildQueryString(hashData) + if (hash) path += "#" + hash + if (supportsPushState) { + var state = options ? options.state : null + var title = options ? options.title : null + $window.onpopstate() + if (options && options.replace) $window.history.replaceState(state, title, router.prefix + path) + else $window.history.pushState(state, title, router.prefix + path) + } + else $window.location.href = router.prefix + path + } + router.defineRoutes = function(routes, resolve, reject) { + function resolveRoute() { + var path = router.getPath() + var params = {} + var pathname = parsePath(path, params, params) + + var state = $window.history.state + if (state != null) { + for (var k in state) params[k] = state[k] + } + for (var route0 in routes) { + var matcher = new RegExp("^" + route0.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$") + if (matcher.test(pathname)) { + pathname.replace(matcher, function() { + var keys = route0.match(/:[^\/]+/g) || [] + var values = [].slice.call(arguments, 1, -2) + for (var i = 0; i < keys.length; i++) { + params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]) + } + resolve(routes[route0], params, path, route0) + }) + return + } + } + reject(path, params) + } + + if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute) + else if (router.prefix.charAt(0) === "#") $window.onhashchange = resolveRoute + resolveRoute() + } + + return router +} +var _20 = function($window, redrawService0) { + var routeService = coreRouter($window) + var identity = function(v) {return v} + var render1, component, attrs3, currentPath, lastUpdate + var route = function(root, defaultRoute, routes) { + if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined") + var run1 = function() { + if (render1 != null) redrawService0.render(root, render1(Vnode(component, attrs3.key, attrs3))) + } + var bail = function() { + routeService.setPath(defaultRoute, null, {replace: true}) + } + routeService.defineRoutes(routes, function(payload, params, path) { + var update = lastUpdate = function(routeResolver, comp) { + if (update !== lastUpdate) return + component = comp != null && typeof comp.view === "function" ? comp : "div", attrs3 = params, currentPath = path, lastUpdate = null + render1 = (routeResolver.render || identity).bind(routeResolver) + run1() + } + if (payload.view) update({}, payload) + else { + if (payload.onmatch) { + Promise.resolve(payload.onmatch(params, path)).then(function(resolved) { + update(payload, resolved) + }, bail) + } + else update(payload, "div") + } + }, bail) + redrawService0.subscribe(root, run1) + } + route.set = function(path, data, options) { + if (lastUpdate != null) options = {replace: true} + lastUpdate = null + routeService.setPath(path, data, options) + } + route.get = function() {return currentPath} + route.prefix = function(prefix0) {routeService.prefix = prefix0} + route.link = function(vnode1) { + vnode1.dom.setAttribute("href", routeService.prefix + vnode1.attrs.href) + vnode1.dom.onclick = function(e) { + if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return + e.preventDefault() + e.redraw = false + var href = this.getAttribute("href") + if (href.indexOf(routeService.prefix) === 0) href = href.slice(routeService.prefix.length) + route.set(href, undefined, undefined) + } + } + route.param = function(key3) { + if(typeof attrs3 !== "undefined" && typeof key3 !== "undefined") return attrs3[key3] + return attrs3 + } + return route +} +m.route = _20(window, redrawService) +m.withAttr = function(attrName, callback1, context) { + return function(e) { + callback1.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName)) + } +} +var _28 = coreRenderer(window) +m.render = _28.render +m.redraw = redrawService.redraw +m.request = requestService.request +m.jsonp = requestService.jsonp +m.parseQueryString = parseQueryString +m.buildQueryString = buildQueryString +m.version = "1.0.0" +m.vnode = Vnode +if (typeof module !== "undefined") module["exports"] = m +else window.m = m } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate) },{"timers":2}],4:[function(require,module,exports){ From f09a0017f977848c569ffe88ec3f47bc5f73c1f8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 30 Mar 2021 17:06:41 +0200 Subject: [PATCH 37/44] lineendings login_server --- login_server/CMakeLists.txt | 354 +-- login_server/skeema/gradido_login/users.sql | 32 +- login_server/src/cpp/Gradido_LoginServer.cpp | 534 ++-- login_server/src/cpp/Gradido_LoginServer.h | 84 +- .../src/cpp/HTTPInterface/LoginPage.cpp | 730 ++--- .../src/cpp/HTTPInterface/LoginPage.h | 40 +- login_server/src/cpp/ImportantTests.cpp | 2 +- .../src/cpp/JSONInterface/JsonCreateUser.cpp | 214 +- .../cpp/JSONInterface/JsonGetUserInfos.cpp | 286 +- .../src/cpp/JSONInterface/JsonLogout.cpp | 86 +- .../src/cpp/JSONInterface/JsonLogout.h | 34 +- .../cpp/JSONInterface/JsonRequestHandler.cpp | 282 +- .../JsonRequestHandlerFactory.cpp | 164 +- .../src/cpp/JSONInterface/JsonTransaction.cpp | 350 +-- .../src/cpp/JSONInterface/JsonTransaction.h | 36 +- login_server/src/cpp/ServerConfig.cpp | 696 ++--- login_server/src/cpp/ServerConfig.h | 178 +- .../cpp/SingletonManager/SessionManager.cpp | 1252 ++++---- login_server/src/cpp/lib/Error.h | 126 +- login_server/src/cpp/lib/ErrorList.cpp | 400 +-- login_server/src/cpp/lib/ErrorList.h | 152 +- login_server/src/cpp/main.cpp | 112 +- login_server/src/cpp/model/Session.cpp | 2646 ++++++++--------- login_server/src/cpp/model/Session.h | 564 ++-- .../src/cpp/model/TransactionCreation.cpp | 144 +- .../src/cpp/model/TransactionTransfer.cpp | 344 +-- login_server/src/cpp/model/email/Email.cpp | 2 +- .../AuthenticatedEncryptionCreateKeyTask.cpp | 78 +- .../src/cpp/tasks/ProcessingTransaction.cpp | 2 +- .../src/cpp/tasks/SigningTransaction.cpp | 566 ++-- .../src/cpp/tasks/SigningTransaction.h | 90 +- login_server/src/cpsp/login.cpsp | 408 +-- 32 files changed, 5494 insertions(+), 5494 deletions(-) diff --git a/login_server/CMakeLists.txt b/login_server/CMakeLists.txt index 5a484c3e9..177c6a5c6 100644 --- a/login_server/CMakeLists.txt +++ b/login_server/CMakeLists.txt @@ -1,177 +1,177 @@ -cmake_minimum_required(VERSION 3.0) -project(Gradido_LoginServer C CXX) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin" ) - -SET ( CMAKE_CXX_FLAGS "-std=c++17" ) - -include_directories( - "dependencies" - "dependencies/tinf/src/" - "dependencies/iroha-ed25519/include" - "dependencies/mariadb-connector-c/include" - "dependencies/mariadb-connector-c/build/include" - "dependencies/spirit-po/include" - "src/cpp/proto" - #"dependencies/mariadb-connector-c/build/include" - #"dependencies/mariadb-connector-c/include" - #"import/mariadb/include" -) - - -FILE(GLOB CONTROLLER "src/cpp/controller/*.cpp" "src/cpp/controller/*.h") -FILE(GLOB TINF "dependencies/tinf/src/*.c" "dependencies/tinf/src/*.h") -FILE(GLOB HTTPInterface "src/cpp/HTTPInterface/*.h" "src/cpp/HTTPInterface/*.cpp") -FILE(GLOB JSONInterface "src/cpp/JSONInterface/*.h" "src/cpp/JSONInterface/*.cpp") -FILE(GLOB TASKS "src/cpp/tasks/*.cpp" "src/cpp/tasks/*.h") -FILE(GLOB SINGLETON_MANAGER "src/cpp/SingletonManager/*.h" "src/cpp/SingletonManager/*.cpp") -FILE(GLOB LIB_SRC "src/cpp/lib/*.h" "src/cpp/lib/*.cpp") -FILE(GLOB MODEL "src/cpp/model/*.h" "src/cpp/model/*.cpp") -FILE(GLOB MODEL_TABLE "src/cpp/model/table/*.h" "src/cpp/model/table/*.cpp") -FILE(GLOB MODEL_EMAIL "src/cpp/model/email/*.h" "src/cpp/model/email/*.cpp") -FILE(GLOB CRYPTO "src/cpp/Crypto/*.h" "src/cpp/Crypto/*.cpp") -FILE(GLOB MAIN "src/cpp/*.cpp" "src/cpp/*.c" "src/cpp/*.h") -FILE(GLOB MYSQL "src/cpp/MySQL/*.cpp" "src/cpp/MySQL/*.h" "src/cpp/MySQL/Poco/*.h") -FILE(GLOB PROTO_GRADIDO "src/cpp/proto/gradido/*.cc" "src/cpp/proto/gradido/*.h") -FILE(GLOB PROTO_HEDERA "src/cpp/proto/hedera/*.cc" "src/cpp/proto/hedera/*.h") - -# used only for test project -FILE(GLOB TEST "src/cpp/test/*.cpp" "src/cpp/test/*.h") -FILE(GLOB TEST_CRYPTO "src/cpp/test/crypto/*.cpp" "src/cpp/test/crypto/*.h") -FILE(GLOB TEST_MODEL "src/cpp/test/model/*.cpp" "src/cpp/test/model/*.h") -FILE(GLOB TEST_MODEL_TABLE "src/cpp/test/model/table/*.cpp" "src/cpp/test/model/table/*.h") -FILE(GLOB TEST_CONTROLLER "src/cpp/test/controller/*.cpp" "src/cpp/test/controller/*.h") - -SET(LOCAL_SRCS - ${CONTROLLER} ${TINF} ${MAIN} ${HTTPInterface} - ${JSONInterface} ${CRYPTO} ${MODEL} ${MODEL_TABLE} ${MODEL_EMAIL} - ${SINGLETON_MANAGER} ${LIB_SRC} ${MYSQL} ${TASKS} - ${PROTO_GRADIDO} ${PROTO_HEDERA} -) -SET(LOCAL_TEST_SRC - ${TEST} ${TEST_CRYPTO} ${TEST_MODEL} ${TEST_MODEL_TABLE} ${TEST_CONTROLLER} -) -aux_source_directory("src/cpp" LOCAL_SRCS) - -if(MSVC) -# src -source_group("controller" FILES ${CONTROLLER}) -source_group("proto\\gradido" FILES ${PROTO_GRADIDO}) -source_group("proto\\hedera" FILES ${PROTO_HEDERA}) -source_group("tinf" FILES ${TINF}) -source_group("Crypto" FILES ${CRYPTO}) -source_group("tasks" FILES ${TASKS}) -source_group("model\\table" FILES ${MODEL_TABLE}) -source_group("model\\email" FILES ${MODEL_EMAIL}) -source_group("model" FILES ${MODEL}) -source_group("mysql" FILES ${MYSQL}) -source_group("SingletonManager" FILES ${SINGLETON_MANAGER}) -source_group("lib" FILES ${LIB_SRC}) -source_group("HTTP-Interface" FILES ${HTTPInterface}) -source_group("Json-Interface" FILES ${JSONInterface}) -source_group("Test\\crypto" FILES ${TEST_CRYPTO}) -source_group("Test\\model\\table" FILES ${TEST_MODEL_TABLE}) -source_group("Test\\model" FILES ${TEST_MODEL}) -source_group("Test\\controller" FILES ${TEST_CONTROLLER}) -source_group("Test" FILES ${TEST}) -endif(MSVC) - -include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) -conan_basic_setup() - -#add_subdirectory("dependencies/curl") -#add_subdirectory("dependencies/mariadb-connector-c") - - -add_executable(Gradido_LoginServer ${LOCAL_SRCS}) -#SUBDIRS("src/test") - -if(WIN32) - -find_library(MYSQL_LIBRARIES mariadbclient.lib PATHS "dependencies/mariadb-connector-c/build/libmariadb/Release" REQUIRED) -#find_library(MYSQL_LIBRARIES_DEBUG mariadbclient.lib PATHS "import/mariadb/lib/debug") -find_library(COMPILED_MARIADB_CLIENT_DEBUG mariadbclient PATHS "dependencies/mariadb-connector-c/build/libmariadb/Debug" REQUIRED) -find_library(IROHA_ED25519 ed25519 PATHS "dependencies/iroha-ed25519/build/Debug" REQUIRED) -set(MYSQL_INCLUDE_DIR "dependencies/mariadb-connector-c/include") - -#set(POCO_DEBUG_PATH "I:/FremdCode/C++/poco/win64/lib/Debug") - -#find_library(POCO_DEBUG_FOUNDATION PocoFoundationd PocoFoundation PATHS ${POCO_DEBUG_PATH} REQUIRED) -#find_library(POCO_DEBUG_DATA PocoDatad PocoData PATHS ${POCO_DEBUG_PATH} REQUIRED) -#find_library(POCO_DEBUG_NET PocoNetd PocoNet PATHS ${POCO_DEBUG_PATH} REQUIRED) -#find_library(POCO_DEBUG_NET_SSL PocoNetSSLd PocoNetSSL PATHS ${POCO_DEBUG_PATH} REQUIRED) -#find_library(POCO_DEBUG_UTIL PocoUtild PocoUtil PATHS ${POCO_DEBUG_PATH} REQUIRED) -#find_library(POCO_DEBUG_CRYPTO PocoCryptod PocoCrypto PATHS ${POCO_DEBUG_PATH} REQUIRED) - -#set(POCO_DEBUG_LIBS ${POCO_DEBUG_FOUNDATION} ${POCO_DEBUG_UTIL} ${POCO_DEBUG_DATA} ${POCO_DEBUG_NET} ${POCO_DEBUG_NET_SSL} ${POCO_DEBUG_CRYPTO}) -#include_directories( -# "I:/FremdCode/C++/poco/Foundation/include" -# "I:/FremdCode/C++/poco/Data/include" -# "I:/FremdCode/C++/poco/Net/include" - #"I:/FremdCode/C++/poco/NetSSL_Win/include" -# "I:/FremdCode/C++/poco/NetSSL_OpenSSL/include" -# "I:/FremdCode/C++/poco/Crypto/include" -# "I:/FremdCode/C++/poco/Util/include" -# "I:/FremdCode/C++/ssl/include" -#) - -set(CMAKE_CXX_FLAGS "/MP /EHsc") -#set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") -#set(CMAKE_CXX_FLAGS_RELEASE "-O3") - -else() - -find_library(IROHA_ED25519 ed25519 PATHS "dependencies/iroha-ed25519/build" REQUIRED) -# set vars for mariadb cmake files -set(INSTALL_BINDIR "bin") -set(INSTALL_PLUGINDIR "bin") -add_subdirectory("dependencies/mariadb-connector-c") - - -include_directories( - "dependencies/mariadb-connector-c/include" - "build/dependencies/mariadb-connector-c/include" -) - - - -endif() - -target_link_libraries(Gradido_LoginServer ${CONAN_LIBS} ${IROHA_ED25519}) -if(WIN32) -TARGET_LINK_LIBRARIES(Gradido_LoginServer optimized ${MYSQL_LIBRARIES} Shlwapi) -TARGET_LINK_LIBRARIES(Gradido_LoginServer debug ${COMPILED_MARIADB_CLIENT_DEBUG} Shlwapi) -else() -target_link_libraries(Gradido_LoginServer libmariadb -pthread) -endif() - -# install -if(UNIX) -install(TARGETS Gradido_LoginServer RUNTIME DESTINATION /usr/local/bin) -#install(FILES lib/libmariadb /usr/local/lib) -install(FILES DESTINATION lib COMPONENT libmariadb) -install(DIRECTORY src/LOCALE DESTINATION /etc/grd_login/ - FILES_MATCHING PATTERN "*.po(t)") - - -endif(UNIX) - -enable_testing() - -# ---------------------- Test ----------------------------------------- -#project(Gradido_LoginServer_Test C CXX) -#_TEST_BUILD - - -add_executable(Gradido_LoginServer_Test ${LOCAL_SRCS} ${LOCAL_TEST_SRC}) -target_compile_definitions(Gradido_LoginServer_Test PUBLIC "_TEST_BUILD") - -target_link_libraries(Gradido_LoginServer_Test ${CONAN_LIBS} ${IROHA_ED25519}) - -if(WIN32) - TARGET_LINK_LIBRARIES(Gradido_LoginServer_Test optimized ${MYSQL_LIBRARIES} Shlwapi) - TARGET_LINK_LIBRARIES(Gradido_LoginServer_Test debug ${COMPILED_MARIADB_CLIENT_DEBUG} Shlwapi) -else() - target_link_libraries(Gradido_LoginServer_Test libmariadb -pthread) -endif() - -add_test(NAME main COMMAND Gradido_LoginServer_Test) +cmake_minimum_required(VERSION 3.0) +project(Gradido_LoginServer C CXX) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin" ) + +SET ( CMAKE_CXX_FLAGS "-std=c++17" ) + +include_directories( + "dependencies" + "dependencies/tinf/src/" + "dependencies/iroha-ed25519/include" + "dependencies/mariadb-connector-c/include" + "dependencies/mariadb-connector-c/build/include" + "dependencies/spirit-po/include" + "src/cpp/proto" + #"dependencies/mariadb-connector-c/build/include" + #"dependencies/mariadb-connector-c/include" + #"import/mariadb/include" +) + + +FILE(GLOB CONTROLLER "src/cpp/controller/*.cpp" "src/cpp/controller/*.h") +FILE(GLOB TINF "dependencies/tinf/src/*.c" "dependencies/tinf/src/*.h") +FILE(GLOB HTTPInterface "src/cpp/HTTPInterface/*.h" "src/cpp/HTTPInterface/*.cpp") +FILE(GLOB JSONInterface "src/cpp/JSONInterface/*.h" "src/cpp/JSONInterface/*.cpp") +FILE(GLOB TASKS "src/cpp/tasks/*.cpp" "src/cpp/tasks/*.h") +FILE(GLOB SINGLETON_MANAGER "src/cpp/SingletonManager/*.h" "src/cpp/SingletonManager/*.cpp") +FILE(GLOB LIB_SRC "src/cpp/lib/*.h" "src/cpp/lib/*.cpp") +FILE(GLOB MODEL "src/cpp/model/*.h" "src/cpp/model/*.cpp") +FILE(GLOB MODEL_TABLE "src/cpp/model/table/*.h" "src/cpp/model/table/*.cpp") +FILE(GLOB MODEL_EMAIL "src/cpp/model/email/*.h" "src/cpp/model/email/*.cpp") +FILE(GLOB CRYPTO "src/cpp/Crypto/*.h" "src/cpp/Crypto/*.cpp") +FILE(GLOB MAIN "src/cpp/*.cpp" "src/cpp/*.c" "src/cpp/*.h") +FILE(GLOB MYSQL "src/cpp/MySQL/*.cpp" "src/cpp/MySQL/*.h" "src/cpp/MySQL/Poco/*.h") +FILE(GLOB PROTO_GRADIDO "src/cpp/proto/gradido/*.cc" "src/cpp/proto/gradido/*.h") +FILE(GLOB PROTO_HEDERA "src/cpp/proto/hedera/*.cc" "src/cpp/proto/hedera/*.h") + +# used only for test project +FILE(GLOB TEST "src/cpp/test/*.cpp" "src/cpp/test/*.h") +FILE(GLOB TEST_CRYPTO "src/cpp/test/crypto/*.cpp" "src/cpp/test/crypto/*.h") +FILE(GLOB TEST_MODEL "src/cpp/test/model/*.cpp" "src/cpp/test/model/*.h") +FILE(GLOB TEST_MODEL_TABLE "src/cpp/test/model/table/*.cpp" "src/cpp/test/model/table/*.h") +FILE(GLOB TEST_CONTROLLER "src/cpp/test/controller/*.cpp" "src/cpp/test/controller/*.h") + +SET(LOCAL_SRCS + ${CONTROLLER} ${TINF} ${MAIN} ${HTTPInterface} + ${JSONInterface} ${CRYPTO} ${MODEL} ${MODEL_TABLE} ${MODEL_EMAIL} + ${SINGLETON_MANAGER} ${LIB_SRC} ${MYSQL} ${TASKS} + ${PROTO_GRADIDO} ${PROTO_HEDERA} +) +SET(LOCAL_TEST_SRC + ${TEST} ${TEST_CRYPTO} ${TEST_MODEL} ${TEST_MODEL_TABLE} ${TEST_CONTROLLER} +) +aux_source_directory("src/cpp" LOCAL_SRCS) + +if(MSVC) +# src +source_group("controller" FILES ${CONTROLLER}) +source_group("proto\\gradido" FILES ${PROTO_GRADIDO}) +source_group("proto\\hedera" FILES ${PROTO_HEDERA}) +source_group("tinf" FILES ${TINF}) +source_group("Crypto" FILES ${CRYPTO}) +source_group("tasks" FILES ${TASKS}) +source_group("model\\table" FILES ${MODEL_TABLE}) +source_group("model\\email" FILES ${MODEL_EMAIL}) +source_group("model" FILES ${MODEL}) +source_group("mysql" FILES ${MYSQL}) +source_group("SingletonManager" FILES ${SINGLETON_MANAGER}) +source_group("lib" FILES ${LIB_SRC}) +source_group("HTTP-Interface" FILES ${HTTPInterface}) +source_group("Json-Interface" FILES ${JSONInterface}) +source_group("Test\\crypto" FILES ${TEST_CRYPTO}) +source_group("Test\\model\\table" FILES ${TEST_MODEL_TABLE}) +source_group("Test\\model" FILES ${TEST_MODEL}) +source_group("Test\\controller" FILES ${TEST_CONTROLLER}) +source_group("Test" FILES ${TEST}) +endif(MSVC) + +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +conan_basic_setup() + +#add_subdirectory("dependencies/curl") +#add_subdirectory("dependencies/mariadb-connector-c") + + +add_executable(Gradido_LoginServer ${LOCAL_SRCS}) +#SUBDIRS("src/test") + +if(WIN32) + +find_library(MYSQL_LIBRARIES mariadbclient.lib PATHS "dependencies/mariadb-connector-c/build/libmariadb/Release" REQUIRED) +#find_library(MYSQL_LIBRARIES_DEBUG mariadbclient.lib PATHS "import/mariadb/lib/debug") +find_library(COMPILED_MARIADB_CLIENT_DEBUG mariadbclient PATHS "dependencies/mariadb-connector-c/build/libmariadb/Debug" REQUIRED) +find_library(IROHA_ED25519 ed25519 PATHS "dependencies/iroha-ed25519/build/Debug" REQUIRED) +set(MYSQL_INCLUDE_DIR "dependencies/mariadb-connector-c/include") + +#set(POCO_DEBUG_PATH "I:/FremdCode/C++/poco/win64/lib/Debug") + +#find_library(POCO_DEBUG_FOUNDATION PocoFoundationd PocoFoundation PATHS ${POCO_DEBUG_PATH} REQUIRED) +#find_library(POCO_DEBUG_DATA PocoDatad PocoData PATHS ${POCO_DEBUG_PATH} REQUIRED) +#find_library(POCO_DEBUG_NET PocoNetd PocoNet PATHS ${POCO_DEBUG_PATH} REQUIRED) +#find_library(POCO_DEBUG_NET_SSL PocoNetSSLd PocoNetSSL PATHS ${POCO_DEBUG_PATH} REQUIRED) +#find_library(POCO_DEBUG_UTIL PocoUtild PocoUtil PATHS ${POCO_DEBUG_PATH} REQUIRED) +#find_library(POCO_DEBUG_CRYPTO PocoCryptod PocoCrypto PATHS ${POCO_DEBUG_PATH} REQUIRED) + +#set(POCO_DEBUG_LIBS ${POCO_DEBUG_FOUNDATION} ${POCO_DEBUG_UTIL} ${POCO_DEBUG_DATA} ${POCO_DEBUG_NET} ${POCO_DEBUG_NET_SSL} ${POCO_DEBUG_CRYPTO}) +#include_directories( +# "I:/FremdCode/C++/poco/Foundation/include" +# "I:/FremdCode/C++/poco/Data/include" +# "I:/FremdCode/C++/poco/Net/include" + #"I:/FremdCode/C++/poco/NetSSL_Win/include" +# "I:/FremdCode/C++/poco/NetSSL_OpenSSL/include" +# "I:/FremdCode/C++/poco/Crypto/include" +# "I:/FremdCode/C++/poco/Util/include" +# "I:/FremdCode/C++/ssl/include" +#) + +set(CMAKE_CXX_FLAGS "/MP /EHsc") +#set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") +#set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +else() + +find_library(IROHA_ED25519 ed25519 PATHS "dependencies/iroha-ed25519/build" REQUIRED) +# set vars for mariadb cmake files +set(INSTALL_BINDIR "bin") +set(INSTALL_PLUGINDIR "bin") +add_subdirectory("dependencies/mariadb-connector-c") + + +include_directories( + "dependencies/mariadb-connector-c/include" + "build/dependencies/mariadb-connector-c/include" +) + + + +endif() + +target_link_libraries(Gradido_LoginServer ${CONAN_LIBS} ${IROHA_ED25519}) +if(WIN32) +TARGET_LINK_LIBRARIES(Gradido_LoginServer optimized ${MYSQL_LIBRARIES} Shlwapi) +TARGET_LINK_LIBRARIES(Gradido_LoginServer debug ${COMPILED_MARIADB_CLIENT_DEBUG} Shlwapi) +else() +target_link_libraries(Gradido_LoginServer libmariadb -pthread) +endif() + +# install +if(UNIX) +install(TARGETS Gradido_LoginServer RUNTIME DESTINATION /usr/local/bin) +#install(FILES lib/libmariadb /usr/local/lib) +install(FILES DESTINATION lib COMPONENT libmariadb) +install(DIRECTORY src/LOCALE DESTINATION /etc/grd_login/ + FILES_MATCHING PATTERN "*.po(t)") + + +endif(UNIX) + +enable_testing() + +# ---------------------- Test ----------------------------------------- +#project(Gradido_LoginServer_Test C CXX) +#_TEST_BUILD + + +add_executable(Gradido_LoginServer_Test ${LOCAL_SRCS} ${LOCAL_TEST_SRC}) +target_compile_definitions(Gradido_LoginServer_Test PUBLIC "_TEST_BUILD") + +target_link_libraries(Gradido_LoginServer_Test ${CONAN_LIBS} ${IROHA_ED25519}) + +if(WIN32) + TARGET_LINK_LIBRARIES(Gradido_LoginServer_Test optimized ${MYSQL_LIBRARIES} Shlwapi) + TARGET_LINK_LIBRARIES(Gradido_LoginServer_Test debug ${COMPILED_MARIADB_CLIENT_DEBUG} Shlwapi) +else() + target_link_libraries(Gradido_LoginServer_Test libmariadb -pthread) +endif() + +add_test(NAME main COMMAND Gradido_LoginServer_Test) diff --git a/login_server/skeema/gradido_login/users.sql b/login_server/skeema/gradido_login/users.sql index 6c3eb5b82..71c2d09e8 100644 --- a/login_server/skeema/gradido_login/users.sql +++ b/login_server/skeema/gradido_login/users.sql @@ -1,16 +1,16 @@ -CREATE TABLE `users` ( - `id` int UNSIGNED NOT NULL AUTO_INCREMENT, - `email` varchar(191) NOT NULL, - `first_name` varchar(150) NOT NULL, - `last_name` varchar(255) DEFAULT '', - `password` bigint unsigned DEFAULT 0, - `pubkey` binary(32) DEFAULT NULL, - `privkey` binary(80) DEFAULT NULL, - `created` datetime NOT NULL DEFAULT current_timestamp(), - `email_checked` tinyint NOT NULL DEFAULT 0, - `passphrase_shown` tinyint NOT NULL DEFAULT 0, - `language` varchar(4) NOT NULL DEFAULT 'de', - `disabled` BOOLEAN NULL DEFAULT FALSE, - PRIMARY KEY (`id`), - UNIQUE KEY `email` (`email`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `users` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT, + `email` varchar(191) NOT NULL, + `first_name` varchar(150) NOT NULL, + `last_name` varchar(255) DEFAULT '', + `password` bigint unsigned DEFAULT 0, + `pubkey` binary(32) DEFAULT NULL, + `privkey` binary(80) DEFAULT NULL, + `created` datetime NOT NULL DEFAULT current_timestamp(), + `email_checked` tinyint NOT NULL DEFAULT 0, + `passphrase_shown` tinyint NOT NULL DEFAULT 0, + `language` varchar(4) NOT NULL DEFAULT 'de', + `disabled` BOOLEAN NULL DEFAULT FALSE, + PRIMARY KEY (`id`), + UNIQUE KEY `email` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/login_server/src/cpp/Gradido_LoginServer.cpp b/login_server/src/cpp/Gradido_LoginServer.cpp index 2ad89e19b..423f603df 100644 --- a/login_server/src/cpp/Gradido_LoginServer.cpp +++ b/login_server/src/cpp/Gradido_LoginServer.cpp @@ -1,267 +1,267 @@ -#include "Gradido_LoginServer.h" -#include "ServerConfig.h" -#include "HTTPInterface/PageRequestHandlerFactory.h" -#include "JSONInterface/JsonRequestHandlerFactory.h" - -#include "lib/Profiler.h" - -#include "SingletonManager/ConnectionManager.h" -#include "SingletonManager/SessionManager.h" -#include "SingletonManager/EmailManager.h" - -#include "controller/User.h" - -#include "Poco/Util/HelpFormatter.h" -#include "Poco/Net/ServerSocket.h" -#include "Poco/Net/HTTPServer.h" -#include "Poco/Net/SSLManager.h" -#include "Poco/Environment.h" -#include "Poco/Logger.h" -#include "Poco/Path.h" -#include "Poco/AsyncChannel.h" -#include "Poco/SimpleFileChannel.h" -#include "Poco/ConsoleChannel.h" -#include "Poco/SplitterChannel.h" -#include "MySQL/Poco/Connector.h" - - -#include - - - -Gradido_LoginServer::Gradido_LoginServer() - : _helpRequested(false) -{ -} - -Gradido_LoginServer::~Gradido_LoginServer() -{ -} - - -void Gradido_LoginServer::initialize(Application& self) -{ - loadConfiguration(); // load default configuration files, if present - ServerApplication::initialize(self); -} - -void Gradido_LoginServer::uninitialize() -{ - ServerApplication::uninitialize(); -} - -void Gradido_LoginServer::defineOptions(Poco::Util::OptionSet& options) -{ - ServerApplication::defineOptions(options); - - /*options.addOption( - Poco::Util::Option("help", "h", "display help information on command line arguments") - .required(false) - .repeatable(false));*/ - options.addOption( - Poco::Util::Option("config", "c", "use non default config file (default is /etc/grd_login.properties)", false) - .repeatable(false) - .argument("Gradido_LoginServer.properties", true) - .callback(Poco::Util::OptionCallback(this, &Gradido_LoginServer::handleOption))); - -} - -void Gradido_LoginServer::handleOption(const std::string& name, const std::string& value) -{ - //printf("handle option: %s with value: %s\n", name.data(), value.data()); - if (name == "config") { - mConfigPath = value; - return; - } - ServerApplication::handleOption(name, value); - if (name == "help") _helpRequested = true; - -} - -void Gradido_LoginServer::displayHelp() -{ - Poco::Util::HelpFormatter helpFormatter(options()); - helpFormatter.setCommand(commandName()); - helpFormatter.setUsage("OPTIONS"); - helpFormatter.setHeader("Gradido Login Server"); - helpFormatter.format(std::cout); -} - -void Gradido_LoginServer::createConsoleFileAsyncLogger(std::string name, std::string filePath) -{ - Poco::AutoPtr logConsoleChannel(new Poco::ConsoleChannel); - Poco::AutoPtr logFileChannel(new Poco::SimpleFileChannel(filePath)); - logFileChannel->setProperty("rotation", "500 K"); - Poco::AutoPtr logSplitter(new Poco::SplitterChannel); - logSplitter->addChannel(logConsoleChannel); - logSplitter->addChannel(logFileChannel); - - Poco::AutoPtr logAsyncChannel(new Poco::AsyncChannel(logSplitter)); - - Poco::Logger& log = Poco::Logger::get(name); - log.setChannel(logAsyncChannel); - log.setLevel("information"); -} - -int Gradido_LoginServer::main(const std::vector& args) -{ - - Profiler usedTime; - if (_helpRequested) - { - displayHelp(); - } - else - { - // ********** logging ************************************ - std::string log_Path = "/var/log/grd_login/"; -//#ifdef _WIN32 -#if defined(_WIN32) || defined(_WIN64) - log_Path = "./"; -#endif - - // init speed logger - Poco::AutoPtr speedLogFileChannel(new Poco::SimpleFileChannel(log_Path + "speedLog.txt")); - /* - The optional log file rotation mode: - never: no rotation (default) - : rotate if file size exceeds bytes - K: rotate if file size exceeds Kilobytes - M: rotate if file size exceeds Megabytes - */ - speedLogFileChannel->setProperty("rotation", "500 K"); - Poco::AutoPtr speedLogAsyncChannel(new Poco::AsyncChannel(speedLogFileChannel)); - - Poco::Logger& speedLogger = Poco::Logger::get("SpeedLog"); - speedLogger.setChannel(speedLogAsyncChannel); - speedLogger.setLevel("information"); - - // logging for request handling - createConsoleFileAsyncLogger("requestLog", log_Path + "requestLog.txt"); - - // error logging - createConsoleFileAsyncLogger("errorLog", log_Path + "errorLog.txt"); - Poco::Logger& errorLog = Poco::Logger::get("errorLog"); - - createConsoleFileAsyncLogger("emailLog", log_Path + "emailLog.txt"); - - // *************** load from config ******************************************** - - std::string cfg_Path = Poco::Path::config() + "grd_login/grd_login.properties"; - if (mConfigPath != "") { - cfg_Path = mConfigPath; - } - - try { - loadConfiguration(cfg_Path); - } - catch (Poco::Exception& ex) { - errorLog.error("error loading config: %s from path: %s", ex.displayText(), cfg_Path); - } - - unsigned short port = (unsigned short)config().getInt("HTTPServer.port", 9980); - unsigned short json_port = (unsigned short)config().getInt("JSONServer.port", 1201); - - - //printf("show mnemonic list: \n"); - //printf(ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER].getCompleteWordList().data()); - if (!ServerConfig::initServerCrypto(config())) { - //printf("[Gradido_LoginServer::%s] error init server crypto\n", __FUNCTION__); - errorLog.error("[Gradido_LoginServer::main] error init server crypto"); - return Application::EXIT_CONFIG; - } - - // first check time for crypto - auto testUser = new User("email@google.de", "Max", "Mustermann"); - Profiler timeUsed; - testUser->validatePwd("haz27Newpassword", nullptr); - ServerConfig::g_FakeLoginSleepTime = (int)std::round(timeUsed.millis()); - delete testUser; - - Poco::Int64 i1 = randombytes_random(); - Poco::Int64 i2 = randombytes_random(); - ServerConfig::g_ServerKeySeed->put(1, i1 | (i2 << 8)); - - ServerConfig::initEMailAccount(config()); - EmailManager::getInstance()->init(config()); - - // start cpu scheduler - uint8_t worker_count = Poco::Environment::processorCount() * 2; - - ServerConfig::g_CPUScheduler = new UniLib::controller::CPUSheduler(worker_count, "Default Worker"); - ServerConfig::g_CryptoCPUScheduler = new UniLib::controller::CPUSheduler(2, "Crypto Worker"); - - // load up connection configs - // register MySQL connector - Poco::Data::MySQL::Connector::registerConnector(); - //Poco::Data::MySQL::Connector::KEY; - auto conn = ConnectionManager::getInstance(); - //conn->setConnection() - //printf("try connect login server mysql db\n"); - try { - conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); - } - catch (Poco::Exception& ex) { - // maybe we in docker environment and db needs some time to start up - // let's wait 10 seconds - int count = 10; - while (count > 0) { - printf("\rwait on mysql/mariadb %d seconds...", count); - count--; - Poco::Thread::sleep(1000); - } - conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); - } - //printf("try connect php server mysql \n"); - //conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_PHP_SERVER); - - SessionManager::getInstance()->init(); - // put urandom on linux servers - //srand(); - - Poco::Net::initializeSSL(); - if(!ServerConfig::initSSLClientContext()) { - //printf("[Gradido_LoginServer::%s] error init server SSL Client\n", __FUNCTION__); - errorLog.error("[Gradido_LoginServer::main] error init server SSL Client\n"); - return Application::EXIT_CONFIG; - } - - // schedule email verification resend - controller::User::checkIfVerificationEmailsShouldBeResend(ServerConfig::g_CronJobsTimer); - - // HTTP Interface Server - // set-up a server socket - Poco::Net::ServerSocket svs(port); - // set-up a HTTPServer instance - Poco::ThreadPool& pool = Poco::ThreadPool::defaultPool(); - Poco::Net::HTTPServer srv(new PageRequestHandlerFactory, svs, new Poco::Net::HTTPServerParams); - ServerConfig::g_ServerKeySeed->put(7, 918276611); - - // start the HTTPServer - srv.start(); - - // JSON Interface Server - Poco::Net::ServerSocket json_svs(json_port); - Poco::Net::HTTPServer json_srv(new JsonRequestHandlerFactory, json_svs, new Poco::Net::HTTPServerParams); - - // start the json server - json_srv.start(); - - printf("[Gradido_LoginServer::main] started in %s\n", usedTime.string().data()); - // wait for CTRL-C or kill - waitForTerminationRequest(); - - // Stop the HTTPServer - srv.stop(); - // Stop the json server - json_srv.stop(); - - ServerConfig::unload(); - Poco::Net::uninitializeSSL(); - // Optional: Delete all global objects allocated by libprotobuf. - google::protobuf::ShutdownProtobufLibrary(); - - } - return Application::EXIT_OK; -} - +#include "Gradido_LoginServer.h" +#include "ServerConfig.h" +#include "HTTPInterface/PageRequestHandlerFactory.h" +#include "JSONInterface/JsonRequestHandlerFactory.h" + +#include "lib/Profiler.h" + +#include "SingletonManager/ConnectionManager.h" +#include "SingletonManager/SessionManager.h" +#include "SingletonManager/EmailManager.h" + +#include "controller/User.h" + +#include "Poco/Util/HelpFormatter.h" +#include "Poco/Net/ServerSocket.h" +#include "Poco/Net/HTTPServer.h" +#include "Poco/Net/SSLManager.h" +#include "Poco/Environment.h" +#include "Poco/Logger.h" +#include "Poco/Path.h" +#include "Poco/AsyncChannel.h" +#include "Poco/SimpleFileChannel.h" +#include "Poco/ConsoleChannel.h" +#include "Poco/SplitterChannel.h" +#include "MySQL/Poco/Connector.h" + + +#include + + + +Gradido_LoginServer::Gradido_LoginServer() + : _helpRequested(false) +{ +} + +Gradido_LoginServer::~Gradido_LoginServer() +{ +} + + +void Gradido_LoginServer::initialize(Application& self) +{ + loadConfiguration(); // load default configuration files, if present + ServerApplication::initialize(self); +} + +void Gradido_LoginServer::uninitialize() +{ + ServerApplication::uninitialize(); +} + +void Gradido_LoginServer::defineOptions(Poco::Util::OptionSet& options) +{ + ServerApplication::defineOptions(options); + + /*options.addOption( + Poco::Util::Option("help", "h", "display help information on command line arguments") + .required(false) + .repeatable(false));*/ + options.addOption( + Poco::Util::Option("config", "c", "use non default config file (default is /etc/grd_login.properties)", false) + .repeatable(false) + .argument("Gradido_LoginServer.properties", true) + .callback(Poco::Util::OptionCallback(this, &Gradido_LoginServer::handleOption))); + +} + +void Gradido_LoginServer::handleOption(const std::string& name, const std::string& value) +{ + //printf("handle option: %s with value: %s\n", name.data(), value.data()); + if (name == "config") { + mConfigPath = value; + return; + } + ServerApplication::handleOption(name, value); + if (name == "help") _helpRequested = true; + +} + +void Gradido_LoginServer::displayHelp() +{ + Poco::Util::HelpFormatter helpFormatter(options()); + helpFormatter.setCommand(commandName()); + helpFormatter.setUsage("OPTIONS"); + helpFormatter.setHeader("Gradido Login Server"); + helpFormatter.format(std::cout); +} + +void Gradido_LoginServer::createConsoleFileAsyncLogger(std::string name, std::string filePath) +{ + Poco::AutoPtr logConsoleChannel(new Poco::ConsoleChannel); + Poco::AutoPtr logFileChannel(new Poco::SimpleFileChannel(filePath)); + logFileChannel->setProperty("rotation", "500 K"); + Poco::AutoPtr logSplitter(new Poco::SplitterChannel); + logSplitter->addChannel(logConsoleChannel); + logSplitter->addChannel(logFileChannel); + + Poco::AutoPtr logAsyncChannel(new Poco::AsyncChannel(logSplitter)); + + Poco::Logger& log = Poco::Logger::get(name); + log.setChannel(logAsyncChannel); + log.setLevel("information"); +} + +int Gradido_LoginServer::main(const std::vector& args) +{ + + Profiler usedTime; + if (_helpRequested) + { + displayHelp(); + } + else + { + // ********** logging ************************************ + std::string log_Path = "/var/log/grd_login/"; +//#ifdef _WIN32 +#if defined(_WIN32) || defined(_WIN64) + log_Path = "./"; +#endif + + // init speed logger + Poco::AutoPtr speedLogFileChannel(new Poco::SimpleFileChannel(log_Path + "speedLog.txt")); + /* + The optional log file rotation mode: + never: no rotation (default) + : rotate if file size exceeds bytes + K: rotate if file size exceeds Kilobytes + M: rotate if file size exceeds Megabytes + */ + speedLogFileChannel->setProperty("rotation", "500 K"); + Poco::AutoPtr speedLogAsyncChannel(new Poco::AsyncChannel(speedLogFileChannel)); + + Poco::Logger& speedLogger = Poco::Logger::get("SpeedLog"); + speedLogger.setChannel(speedLogAsyncChannel); + speedLogger.setLevel("information"); + + // logging for request handling + createConsoleFileAsyncLogger("requestLog", log_Path + "requestLog.txt"); + + // error logging + createConsoleFileAsyncLogger("errorLog", log_Path + "errorLog.txt"); + Poco::Logger& errorLog = Poco::Logger::get("errorLog"); + + createConsoleFileAsyncLogger("emailLog", log_Path + "emailLog.txt"); + + // *************** load from config ******************************************** + + std::string cfg_Path = Poco::Path::config() + "grd_login/grd_login.properties"; + if (mConfigPath != "") { + cfg_Path = mConfigPath; + } + + try { + loadConfiguration(cfg_Path); + } + catch (Poco::Exception& ex) { + errorLog.error("error loading config: %s from path: %s", ex.displayText(), cfg_Path); + } + + unsigned short port = (unsigned short)config().getInt("HTTPServer.port", 9980); + unsigned short json_port = (unsigned short)config().getInt("JSONServer.port", 1201); + + + //printf("show mnemonic list: \n"); + //printf(ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER].getCompleteWordList().data()); + if (!ServerConfig::initServerCrypto(config())) { + //printf("[Gradido_LoginServer::%s] error init server crypto\n", __FUNCTION__); + errorLog.error("[Gradido_LoginServer::main] error init server crypto"); + return Application::EXIT_CONFIG; + } + + // first check time for crypto + auto testUser = new User("email@google.de", "Max", "Mustermann"); + Profiler timeUsed; + testUser->validatePwd("haz27Newpassword", nullptr); + ServerConfig::g_FakeLoginSleepTime = (int)std::round(timeUsed.millis()); + delete testUser; + + Poco::Int64 i1 = randombytes_random(); + Poco::Int64 i2 = randombytes_random(); + ServerConfig::g_ServerKeySeed->put(1, i1 | (i2 << 8)); + + ServerConfig::initEMailAccount(config()); + EmailManager::getInstance()->init(config()); + + // start cpu scheduler + uint8_t worker_count = Poco::Environment::processorCount() * 2; + + ServerConfig::g_CPUScheduler = new UniLib::controller::CPUSheduler(worker_count, "Default Worker"); + ServerConfig::g_CryptoCPUScheduler = new UniLib::controller::CPUSheduler(2, "Crypto Worker"); + + // load up connection configs + // register MySQL connector + Poco::Data::MySQL::Connector::registerConnector(); + //Poco::Data::MySQL::Connector::KEY; + auto conn = ConnectionManager::getInstance(); + //conn->setConnection() + //printf("try connect login server mysql db\n"); + try { + conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); + } + catch (Poco::Exception& ex) { + // maybe we in docker environment and db needs some time to start up + // let's wait 10 seconds + int count = 10; + while (count > 0) { + printf("\rwait on mysql/mariadb %d seconds...", count); + count--; + Poco::Thread::sleep(1000); + } + conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); + } + //printf("try connect php server mysql \n"); + //conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_PHP_SERVER); + + SessionManager::getInstance()->init(); + // put urandom on linux servers + //srand(); + + Poco::Net::initializeSSL(); + if(!ServerConfig::initSSLClientContext()) { + //printf("[Gradido_LoginServer::%s] error init server SSL Client\n", __FUNCTION__); + errorLog.error("[Gradido_LoginServer::main] error init server SSL Client\n"); + return Application::EXIT_CONFIG; + } + + // schedule email verification resend + controller::User::checkIfVerificationEmailsShouldBeResend(ServerConfig::g_CronJobsTimer); + + // HTTP Interface Server + // set-up a server socket + Poco::Net::ServerSocket svs(port); + // set-up a HTTPServer instance + Poco::ThreadPool& pool = Poco::ThreadPool::defaultPool(); + Poco::Net::HTTPServer srv(new PageRequestHandlerFactory, svs, new Poco::Net::HTTPServerParams); + ServerConfig::g_ServerKeySeed->put(7, 918276611); + + // start the HTTPServer + srv.start(); + + // JSON Interface Server + Poco::Net::ServerSocket json_svs(json_port); + Poco::Net::HTTPServer json_srv(new JsonRequestHandlerFactory, json_svs, new Poco::Net::HTTPServerParams); + + // start the json server + json_srv.start(); + + printf("[Gradido_LoginServer::main] started in %s\n", usedTime.string().data()); + // wait for CTRL-C or kill + waitForTerminationRequest(); + + // Stop the HTTPServer + srv.stop(); + // Stop the json server + json_srv.stop(); + + ServerConfig::unload(); + Poco::Net::uninitializeSSL(); + // Optional: Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); + + } + return Application::EXIT_OK; +} + diff --git a/login_server/src/cpp/Gradido_LoginServer.h b/login_server/src/cpp/Gradido_LoginServer.h index 98c3c882c..0a14d9000 100644 --- a/login_server/src/cpp/Gradido_LoginServer.h +++ b/login_server/src/cpp/Gradido_LoginServer.h @@ -1,42 +1,42 @@ -#ifndef Gradido_LoginServer_INCLUDED -#define Gradido_LoginServer_INCLUDED - -#include "Poco/Util/ServerApplication.h" - -class Gradido_LoginServer : public Poco::Util::ServerApplication -{ - - /// The main application class. - /// - /// This class handles command-line arguments and - /// configuration files. - /// Start the Gradido_LoginServer executable with the help - /// option (/help on Windows, --help on Unix) for - /// the available command line options. - /// - - -public: - Gradido_LoginServer(); - ~Gradido_LoginServer(); - -protected: - void initialize(Application& self); - - void uninitialize(); - - void defineOptions(Poco::Util::OptionSet& options); - - void handleOption(const std::string& name, const std::string& value); - void displayHelp(); - - int main(const std::vector& args); - - void createConsoleFileAsyncLogger(std::string name, std::string filePath); - -private: - bool _helpRequested; - std::string mConfigPath; -}; - -#endif //Gradido_LoginServer_INCLUDED +#ifndef Gradido_LoginServer_INCLUDED +#define Gradido_LoginServer_INCLUDED + +#include "Poco/Util/ServerApplication.h" + +class Gradido_LoginServer : public Poco::Util::ServerApplication +{ + + /// The main application class. + /// + /// This class handles command-line arguments and + /// configuration files. + /// Start the Gradido_LoginServer executable with the help + /// option (/help on Windows, --help on Unix) for + /// the available command line options. + /// + + +public: + Gradido_LoginServer(); + ~Gradido_LoginServer(); + +protected: + void initialize(Application& self); + + void uninitialize(); + + void defineOptions(Poco::Util::OptionSet& options); + + void handleOption(const std::string& name, const std::string& value); + void displayHelp(); + + int main(const std::vector& args); + + void createConsoleFileAsyncLogger(std::string name, std::string filePath); + +private: + bool _helpRequested; + std::string mConfigPath; +}; + +#endif //Gradido_LoginServer_INCLUDED diff --git a/login_server/src/cpp/HTTPInterface/LoginPage.cpp b/login_server/src/cpp/HTTPInterface/LoginPage.cpp index f3e6aa077..65d144c9e 100644 --- a/login_server/src/cpp/HTTPInterface/LoginPage.cpp +++ b/login_server/src/cpp/HTTPInterface/LoginPage.cpp @@ -1,365 +1,365 @@ -#include "LoginPage.h" -#include "Poco/Net/HTTPServerRequest.h" -#include "Poco/Net/HTTPServerResponse.h" -#include "Poco/Net/HTMLForm.h" -#include "Poco/DeflatingStream.h" - - -#line 7 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" - -#include "../gettext.h" - -#include "Poco/Net/HTTPCookie.h" -#include "Poco/Net/HTTPServerParams.h" -#include "Poco/Logger.h" -#include "../SingletonManager/SessionManager.h" -#include "../SingletonManager/LanguageManager.h" -#include "../SingletonManager/ErrorManager.h" - -#line 1 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" - -#include "../ServerConfig.h" - - -LoginPage::LoginPage(Session* arg): - SessionHTTPRequestHandler(arg) -{ -} - - -void LoginPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) -{ - response.setChunkedTransferEncoding(true); - response.setContentType("text/html"); - bool _compressResponse(request.hasToken("Accept-Encoding", "gzip")); - if (_compressResponse) response.set("Content-Encoding", "gzip"); - - Poco::Net::HTMLForm form(request, request.stream()); -#line 18 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" - - const char* pageName = "Login"; - auto sm = SessionManager::getInstance(); - auto lm = LanguageManager::getInstance(); - auto em = ErrorManager::getInstance(); - - auto lang = chooseLanguage(request); - //printf("choose language return: %d\n", lang); - auto langCatalog = lm->getFreeCatalog(lang); - - std::string presetEmail(""); - if(mSession && mSession->getUser()) { - presetEmail = mSession->getUser()->getEmail(); - } - - if(!form.empty()) { - - bool langUpdatedByBtn = false; - auto langBtn = form.get("lang", ""); - if(langBtn != "") { - langUpdatedByBtn = true; - } - /* - auto langInput = form.get("lang", ""); - auto updatedLang = LANG_NULL; - if(langBtn != "") { - updatedLang = chooseLanguage(request, langBtn); - langUpdatedByBtn = true; - } else if(langInput != "") { - updatedLang = chooseLanguage(request, langInput); - } - - if(updatedLang != LANG_NULL && updatedLang != lang) { - lang = updatedLang; - langCatalog = lm->getFreeCatalog(lang); - } - */ - auto email = form.get("login-email", ""); - auto password = form.get("login-password", ""); - - if(email != "" && password != "") { - //auto session = sm->getSession(request); - //if(!mSession) mSession = sm->findByEmail(email); - if(!mSession) { - mSession = sm->getNewSession(); - mSession->setLanguageCatalog(langCatalog); - // get language - // first check url, second check language header - // for debugging client ip - auto client_host = request.clientAddress().host(); - //auto client_ip = request.clientAddress(); - // X-Real-IP forwarded ip from nginx config - auto client_host_string = request.get("X-Real-IP", client_host.toString()); - std::string clientIpString = "client ip: "; - client_host = Poco::Net::IPAddress(client_host_string); - clientIpString += client_host_string; - Poco::Logger::get("requestLog").information(clientIpString); - // debugging end - mSession->setClientIp(client_host); - response.addCookie(mSession->getLoginCookie()); - } else { - langCatalog = mSession->getLanguageCatalog(); - } - UserStates user_state; - try { - user_state = mSession->loadUser(email, password); - } catch (Poco::Exception& ex) { - addError(new ParamError("login", "exception by calling loadUser: ", ex.displayText())); - sendErrorsAsEmail(); - addError(new Error("Error", "Intern Server error, please try again later")); - } - auto user = mSession->getNewUser(); - - if(user_state >= USER_LOADED_FROM_DB && !user.isNull() && !user->getModel()->getPublicKey()) { - if(mSession->generateKeys(true, true)) { - user_state = USER_COMPLETE; - if(user->getModel()->isDisabled()) { - user_state = USER_DISABLED; - } - } - } else { - //printf("pubkey exist: %p\n",user->getModel()->getPublicKey()); - } - getErrors(mSession); - - auto uri_start = request.serverParams().getServerName(); - auto lastExternReferer = mSession->getLastReferer(); - - printf("user_state: %d\n", user_state); - - switch(user_state) { - case USER_EMPTY: - case USER_PASSWORD_INCORRECT: - addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("E-Mail or password isn't right, please try again!")), false); - if(mSession) { - getErrors(mSession); - sm->releaseSession(mSession); - } - sm->deleteLoginCookies(request, response); - break; - case USER_PASSWORD_ENCRYPTION_IN_PROCESS: - addError(new Error(langCatalog->gettext("Passwort"), langCatalog->gettext("Passwort wird noch berechnet, bitte versuche es in etwa 1 Minute erneut.")), false); - break; - case USER_KEYS_DONT_MATCH: - addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Error in saved data, the server admin will look at it."))); - break; - case USER_DISABLED: - addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Benutzer ist deaktiviert, kein Login möglich!"))); - if(mSession) { - getErrors(mSession); - sm->releaseSession(mSession); - } - sm->deleteLoginCookies(request, response); - break; - case USER_NO_PRIVATE_KEY: - case USER_COMPLETE: - case USER_EMAIL_NOT_ACTIVATED: - auto referer = request.find("Referer"); - std::string refererString; - if (referer != request.end()) { - refererString = referer->second; - } - if(lastExternReferer != "") { - //printf("redirect to: %s\n", lastExternReferer.data()); - response.redirect(lastExternReferer); - } else if(refererString != "" && - refererString.find("login") == std::string::npos && - refererString.find("logout") == std::string::npos && - refererString.find("user_delete") == std::string::npos && - refererString != ServerConfig::g_serverPath + request.getURI()) { - std::string uri = request.getURI(); - printf("request uri: %s, redirect to: %s\n", uri.data(), refererString.data()); - response.redirect(refererString); - } else { - //printf("redirect to: %s\n", ServerConfig::g_php_serverPath.data()); - response.redirect(ServerConfig::g_php_serverPath + "/"); - } - return; - } - - } else if(!langUpdatedByBtn) { - addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("Username and password are needed!")), false); - } - - } else { - - // on enter login page with empty form - //auto session = sm->getSession(request); - // remove old cookies and session if exist - if(mSession) { - getErrors(mSession); - sm->releaseSession(mSession); - } - sm->deleteLoginCookies(request, response); - } - -#line 3 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" - - bool withMaterialIcons = false; - std::ostream& _responseStream = response.send(); - Poco::DeflatingOutputStream _gzipStream(_responseStream, Poco::DeflatingStreamBuf::STREAM_GZIP, 1); - std::ostream& responseStream = _compressResponse ? _gzipStream : _responseStream; - responseStream << "\n"; - // begin include header.cpsp - responseStream << "\n"; - responseStream << "\n"; - responseStream << "\n"; - responseStream << "\n"; - responseStream << "\n"; - responseStream << "\n"; - responseStream << "Gradido Login Server: "; -#line 11 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" - responseStream << ( pageName ); - responseStream << "\n"; - responseStream << "\n"; -#line 13 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" - if(withMaterialIcons) { responseStream << "\n"; - responseStream << "\n"; -#line 15 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" - } responseStream << "\n"; - responseStream << "\n"; - responseStream << "\n"; - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << " "; - // end include header.cpsp - responseStream << "\n"; -#line 175 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" - responseStream << ( getErrorsHtml() ); - responseStream << "\n"; - responseStream << "\n"; - responseStream << "
\n"; - responseStream << " "; - // begin include flags.cpsp - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << "\t\n"; - responseStream << "\t\n"; - responseStream << "
\n"; - responseStream << "
"; - // end include flags.cpsp - responseStream << "\n"; - responseStream << "
\n"; - responseStream << "\t\t
\n"; - responseStream << "\t\t\tgettext("E-Mail") ); - responseStream << "\" value=\""; -#line 181 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" - responseStream << ( presetEmail ); - responseStream << "\"/>\n"; - responseStream << "\t\t\tgettext("Password") ); - responseStream << "\" />\n"; - responseStream << "\t\t \n"; - responseStream << "\t\t
\n"; - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << "\t

"; -#line 188 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" - responseStream << ( langCatalog->gettext("You haven't any account yet? Please follow the link to create one.") ); - responseStream << "

\n"; - responseStream << "\t \n"; - responseStream << "\t\t\t"; -#line 190 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" - responseStream << ( langCatalog->gettext("Create New Account") ); - responseStream << "\n"; - responseStream << "\t\t \n"; - responseStream << "\t
\n"; - responseStream << "\t\t\n"; - responseStream << "\t
\n"; - responseStream << "
\n"; - responseStream << "

 

\n"; - responseStream << "
\n"; - responseStream << "\tZum Whitepaper\n"; - responseStream << "\t
\n"; - responseStream << "\t
\n"; - responseStream << "\tTo the Whitepaper\n"; - responseStream << "
\n"; - // begin include footer.cpsp - responseStream << "
\n"; - responseStream << "

Copyright © Gradido 2020

\n"; - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << " "; -#line 6 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\footer.cpsp" - responseStream << ( mTimeProfiler.string() ); - responseStream << "\n"; - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << "

Login Server in Entwicklung

\n"; - responseStream << "

Alpha "; -#line 10 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\footer.cpsp" - responseStream << ( ServerConfig::g_versionString ); - responseStream << "

\n"; - responseStream << "
\n"; - responseStream << "
\n"; - responseStream << "\n"; - responseStream << "\n"; - responseStream << ""; - // end include footer.cpsp - if (_compressResponse) _gzipStream.close(); -} +#include "LoginPage.h" +#include "Poco/Net/HTTPServerRequest.h" +#include "Poco/Net/HTTPServerResponse.h" +#include "Poco/Net/HTMLForm.h" +#include "Poco/DeflatingStream.h" + + +#line 7 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" + +#include "../gettext.h" + +#include "Poco/Net/HTTPCookie.h" +#include "Poco/Net/HTTPServerParams.h" +#include "Poco/Logger.h" +#include "../SingletonManager/SessionManager.h" +#include "../SingletonManager/LanguageManager.h" +#include "../SingletonManager/ErrorManager.h" + +#line 1 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" + +#include "../ServerConfig.h" + + +LoginPage::LoginPage(Session* arg): + SessionHTTPRequestHandler(arg) +{ +} + + +void LoginPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) +{ + response.setChunkedTransferEncoding(true); + response.setContentType("text/html"); + bool _compressResponse(request.hasToken("Accept-Encoding", "gzip")); + if (_compressResponse) response.set("Content-Encoding", "gzip"); + + Poco::Net::HTMLForm form(request, request.stream()); +#line 18 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" + + const char* pageName = "Login"; + auto sm = SessionManager::getInstance(); + auto lm = LanguageManager::getInstance(); + auto em = ErrorManager::getInstance(); + + auto lang = chooseLanguage(request); + //printf("choose language return: %d\n", lang); + auto langCatalog = lm->getFreeCatalog(lang); + + std::string presetEmail(""); + if(mSession && mSession->getUser()) { + presetEmail = mSession->getUser()->getEmail(); + } + + if(!form.empty()) { + + bool langUpdatedByBtn = false; + auto langBtn = form.get("lang", ""); + if(langBtn != "") { + langUpdatedByBtn = true; + } + /* + auto langInput = form.get("lang", ""); + auto updatedLang = LANG_NULL; + if(langBtn != "") { + updatedLang = chooseLanguage(request, langBtn); + langUpdatedByBtn = true; + } else if(langInput != "") { + updatedLang = chooseLanguage(request, langInput); + } + + if(updatedLang != LANG_NULL && updatedLang != lang) { + lang = updatedLang; + langCatalog = lm->getFreeCatalog(lang); + } + */ + auto email = form.get("login-email", ""); + auto password = form.get("login-password", ""); + + if(email != "" && password != "") { + //auto session = sm->getSession(request); + //if(!mSession) mSession = sm->findByEmail(email); + if(!mSession) { + mSession = sm->getNewSession(); + mSession->setLanguageCatalog(langCatalog); + // get language + // first check url, second check language header + // for debugging client ip + auto client_host = request.clientAddress().host(); + //auto client_ip = request.clientAddress(); + // X-Real-IP forwarded ip from nginx config + auto client_host_string = request.get("X-Real-IP", client_host.toString()); + std::string clientIpString = "client ip: "; + client_host = Poco::Net::IPAddress(client_host_string); + clientIpString += client_host_string; + Poco::Logger::get("requestLog").information(clientIpString); + // debugging end + mSession->setClientIp(client_host); + response.addCookie(mSession->getLoginCookie()); + } else { + langCatalog = mSession->getLanguageCatalog(); + } + UserStates user_state; + try { + user_state = mSession->loadUser(email, password); + } catch (Poco::Exception& ex) { + addError(new ParamError("login", "exception by calling loadUser: ", ex.displayText())); + sendErrorsAsEmail(); + addError(new Error("Error", "Intern Server error, please try again later")); + } + auto user = mSession->getNewUser(); + + if(user_state >= USER_LOADED_FROM_DB && !user.isNull() && !user->getModel()->getPublicKey()) { + if(mSession->generateKeys(true, true)) { + user_state = USER_COMPLETE; + if(user->getModel()->isDisabled()) { + user_state = USER_DISABLED; + } + } + } else { + //printf("pubkey exist: %p\n",user->getModel()->getPublicKey()); + } + getErrors(mSession); + + auto uri_start = request.serverParams().getServerName(); + auto lastExternReferer = mSession->getLastReferer(); + + printf("user_state: %d\n", user_state); + + switch(user_state) { + case USER_EMPTY: + case USER_PASSWORD_INCORRECT: + addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("E-Mail or password isn't right, please try again!")), false); + if(mSession) { + getErrors(mSession); + sm->releaseSession(mSession); + } + sm->deleteLoginCookies(request, response); + break; + case USER_PASSWORD_ENCRYPTION_IN_PROCESS: + addError(new Error(langCatalog->gettext("Passwort"), langCatalog->gettext("Passwort wird noch berechnet, bitte versuche es in etwa 1 Minute erneut.")), false); + break; + case USER_KEYS_DONT_MATCH: + addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Error in saved data, the server admin will look at it."))); + break; + case USER_DISABLED: + addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Benutzer ist deaktiviert, kein Login möglich!"))); + if(mSession) { + getErrors(mSession); + sm->releaseSession(mSession); + } + sm->deleteLoginCookies(request, response); + break; + case USER_NO_PRIVATE_KEY: + case USER_COMPLETE: + case USER_EMAIL_NOT_ACTIVATED: + auto referer = request.find("Referer"); + std::string refererString; + if (referer != request.end()) { + refererString = referer->second; + } + if(lastExternReferer != "") { + //printf("redirect to: %s\n", lastExternReferer.data()); + response.redirect(lastExternReferer); + } else if(refererString != "" && + refererString.find("login") == std::string::npos && + refererString.find("logout") == std::string::npos && + refererString.find("user_delete") == std::string::npos && + refererString != ServerConfig::g_serverPath + request.getURI()) { + std::string uri = request.getURI(); + printf("request uri: %s, redirect to: %s\n", uri.data(), refererString.data()); + response.redirect(refererString); + } else { + //printf("redirect to: %s\n", ServerConfig::g_php_serverPath.data()); + response.redirect(ServerConfig::g_php_serverPath + "/"); + } + return; + } + + } else if(!langUpdatedByBtn) { + addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("Username and password are needed!")), false); + } + + } else { + + // on enter login page with empty form + //auto session = sm->getSession(request); + // remove old cookies and session if exist + if(mSession) { + getErrors(mSession); + sm->releaseSession(mSession); + } + sm->deleteLoginCookies(request, response); + } + +#line 3 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" + + bool withMaterialIcons = false; + std::ostream& _responseStream = response.send(); + Poco::DeflatingOutputStream _gzipStream(_responseStream, Poco::DeflatingStreamBuf::STREAM_GZIP, 1); + std::ostream& responseStream = _compressResponse ? _gzipStream : _responseStream; + responseStream << "\n"; + // begin include header.cpsp + responseStream << "\n"; + responseStream << "\n"; + responseStream << "\n"; + responseStream << "\n"; + responseStream << "\n"; + responseStream << "\n"; + responseStream << "Gradido Login Server: "; +#line 11 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" + responseStream << ( pageName ); + responseStream << "\n"; + responseStream << "\n"; +#line 13 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" + if(withMaterialIcons) { responseStream << "\n"; + responseStream << "\n"; +#line 15 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\header.cpsp" + } responseStream << "\n"; + responseStream << "\n"; + responseStream << "\n"; + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << " "; + // end include header.cpsp + responseStream << "\n"; +#line 175 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" + responseStream << ( getErrorsHtml() ); + responseStream << "\n"; + responseStream << "\n"; + responseStream << "
\n"; + responseStream << " "; + // begin include flags.cpsp + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << "\t\n"; + responseStream << "\t\n"; + responseStream << "
\n"; + responseStream << "
"; + // end include flags.cpsp + responseStream << "\n"; + responseStream << "
\n"; + responseStream << "\t\t
\n"; + responseStream << "\t\t\tgettext("E-Mail") ); + responseStream << "\" value=\""; +#line 181 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" + responseStream << ( presetEmail ); + responseStream << "\"/>\n"; + responseStream << "\t\t\tgettext("Password") ); + responseStream << "\" />\n"; + responseStream << "\t\t \n"; + responseStream << "\t\t
\n"; + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << "\t

"; +#line 188 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" + responseStream << ( langCatalog->gettext("You haven't any account yet? Please follow the link to create one.") ); + responseStream << "

\n"; + responseStream << "\t \n"; + responseStream << "\t\t\t"; +#line 190 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\login.cpsp" + responseStream << ( langCatalog->gettext("Create New Account") ); + responseStream << "\n"; + responseStream << "\t\t \n"; + responseStream << "\t
\n"; + responseStream << "\t\t\n"; + responseStream << "\t
\n"; + responseStream << "
\n"; + responseStream << "

 

\n"; + responseStream << "
\n"; + responseStream << "\tZum Whitepaper\n"; + responseStream << "\t
\n"; + responseStream << "\t
\n"; + responseStream << "\tTo the Whitepaper\n"; + responseStream << "
\n"; + // begin include footer.cpsp + responseStream << "
\n"; + responseStream << "

Copyright © Gradido 2020

\n"; + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << " "; +#line 6 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\footer.cpsp" + responseStream << ( mTimeProfiler.string() ); + responseStream << "\n"; + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << "

Login Server in Entwicklung

\n"; + responseStream << "

Alpha "; +#line 10 "F:\\Gradido\\gradido_local\\login_server\\src\\cpsp\\footer.cpsp" + responseStream << ( ServerConfig::g_versionString ); + responseStream << "

\n"; + responseStream << "
\n"; + responseStream << "
\n"; + responseStream << "\n"; + responseStream << "\n"; + responseStream << ""; + // end include footer.cpsp + if (_compressResponse) _gzipStream.close(); +} diff --git a/login_server/src/cpp/HTTPInterface/LoginPage.h b/login_server/src/cpp/HTTPInterface/LoginPage.h index be3dfd97d..30c617eaf 100644 --- a/login_server/src/cpp/HTTPInterface/LoginPage.h +++ b/login_server/src/cpp/HTTPInterface/LoginPage.h @@ -1,20 +1,20 @@ -#ifndef LoginPage_INCLUDED -#define LoginPage_INCLUDED - - -#include "Poco/Net/HTTPRequestHandler.h" - - -#include "SessionHTTPRequestHandler.h" - - -class LoginPage: public SessionHTTPRequestHandler -{ -public: - LoginPage(Session*); - - void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response); -}; - - -#endif // LoginPage_INCLUDED +#ifndef LoginPage_INCLUDED +#define LoginPage_INCLUDED + + +#include "Poco/Net/HTTPRequestHandler.h" + + +#include "SessionHTTPRequestHandler.h" + + +class LoginPage: public SessionHTTPRequestHandler +{ +public: + LoginPage(Session*); + + void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response); +}; + + +#endif // LoginPage_INCLUDED diff --git a/login_server/src/cpp/ImportantTests.cpp b/login_server/src/cpp/ImportantTests.cpp index d321bab5f..a80af3cec 100644 --- a/login_server/src/cpp/ImportantTests.cpp +++ b/login_server/src/cpp/ImportantTests.cpp @@ -1,4 +1,4 @@ -#include "ImportantTests.h" +#include "ImportantTests.h" #include #include "ServerConfig.h" diff --git a/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp b/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp index 6b5ecf33d..7b5cf8270 100644 --- a/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp +++ b/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp @@ -1,108 +1,108 @@ -#include "JsonCreateUser.h" - -#include "../model/email/Email.h" -#include "../controller/User.h" -#include "../controller/EmailVerificationCode.h" - -#include "../SingletonManager/EmailManager.h" -#include "../SingletonManager/SessionManager.h" - -#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h" - -Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params) -{ - std::string email; - std::string first_name; - std::string last_name; - std::string password; - bool login_after_register = false; - int emailType; - auto em = EmailManager::getInstance(); - auto sm = SessionManager::getInstance(); - - // if is json object - if (params.type() == typeid(Poco::JSON::Object::Ptr)) { - Poco::JSON::Object::Ptr paramJsonObject = params.extract(); - /// Throws a RangeException if the value does not fit - /// into the result variable. - /// Throws a NotImplementedException if conversion is - /// not available for the given type. - /// Throws InvalidAccessException if Var is empty. - try { - paramJsonObject->get("email").convert(email); - paramJsonObject->get("first_name").convert(first_name); - paramJsonObject->get("last_name").convert(last_name); - paramJsonObject->get("emailType").convert(emailType); - - if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { - paramJsonObject->get("password").convert(password); - } - if (!paramJsonObject->isNull("login_after_register")) { - paramJsonObject->get("login_after_register").convert(login_after_register); - } - } - catch (Poco::Exception& ex) { - return stateError("json exception", ex.displayText()); - } - } - else { - return stateError("parameter format unknown"); - } - - auto user = controller::User::create(); - if (user->load(email) > 0) { - return customStateError("exist", "user already exist"); - } - - if (password.size()) { - ErrorList errors; - if (!sm->checkPwdValidation(password, &errors)) { - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", "error"); - result->set("msg", errors.getLastError()->getString(false)); - if (errors.errorCount()) { - result->set("details", errors.getLastError()->getString(false)); - } - return result; - } - } - - // create user - user = controller::User::create(email, first_name, last_name); - auto userModel = user->getModel(); - Session* session = nullptr; - - if (!userModel->insertIntoDB(true)) { - userModel->sendErrorsAsEmail(); - return stateError("insert user failed"); - } - if (password.size()) { - session = sm->getNewSession(); - session->setUser(user); - session->generateKeys(true, true); - session->setClientIp(mClientIP); - - // calculate encryption key, could need some time, will save encrypted privkey to db - UniLib::controller::TaskPtr create_authenticated_encrypten_key = new AuthenticatedEncryptionCreateKeyTask(user, password); - create_authenticated_encrypten_key->scheduleTask(create_authenticated_encrypten_key); - } - - auto emailOptIn = controller::EmailVerificationCode::create(userModel->getID(), model::table::EMAIL_OPT_IN_REGISTER); - auto emailOptInModel = emailOptIn->getModel(); - if (!emailOptInModel->insertIntoDB(false)) { - emailOptInModel->sendErrorsAsEmail(); - return stateError("insert emailOptIn failed"); - } - - em->addEmail(new model::Email(emailOptIn, user, model::Email::convertTypeFromInt(emailType))); - - if (login_after_register && session) { - Poco::JSON::Object* result = stateSuccess(); - - result->set("session_id", session->getHandle()); - return result; - } - - return stateSuccess(); - +#include "JsonCreateUser.h" + +#include "../model/email/Email.h" +#include "../controller/User.h" +#include "../controller/EmailVerificationCode.h" + +#include "../SingletonManager/EmailManager.h" +#include "../SingletonManager/SessionManager.h" + +#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h" + +Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params) +{ + std::string email; + std::string first_name; + std::string last_name; + std::string password; + bool login_after_register = false; + int emailType; + auto em = EmailManager::getInstance(); + auto sm = SessionManager::getInstance(); + + // if is json object + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + /// Throws a RangeException if the value does not fit + /// into the result variable. + /// Throws a NotImplementedException if conversion is + /// not available for the given type. + /// Throws InvalidAccessException if Var is empty. + try { + paramJsonObject->get("email").convert(email); + paramJsonObject->get("first_name").convert(first_name); + paramJsonObject->get("last_name").convert(last_name); + paramJsonObject->get("emailType").convert(emailType); + + if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { + paramJsonObject->get("password").convert(password); + } + if (!paramJsonObject->isNull("login_after_register")) { + paramJsonObject->get("login_after_register").convert(login_after_register); + } + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + else { + return stateError("parameter format unknown"); + } + + auto user = controller::User::create(); + if (user->load(email) > 0) { + return customStateError("exist", "user already exist"); + } + + if (password.size()) { + ErrorList errors; + if (!sm->checkPwdValidation(password, &errors)) { + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "error"); + result->set("msg", errors.getLastError()->getString(false)); + if (errors.errorCount()) { + result->set("details", errors.getLastError()->getString(false)); + } + return result; + } + } + + // create user + user = controller::User::create(email, first_name, last_name); + auto userModel = user->getModel(); + Session* session = nullptr; + + if (!userModel->insertIntoDB(true)) { + userModel->sendErrorsAsEmail(); + return stateError("insert user failed"); + } + if (password.size()) { + session = sm->getNewSession(); + session->setUser(user); + session->generateKeys(true, true); + session->setClientIp(mClientIP); + + // calculate encryption key, could need some time, will save encrypted privkey to db + UniLib::controller::TaskPtr create_authenticated_encrypten_key = new AuthenticatedEncryptionCreateKeyTask(user, password); + create_authenticated_encrypten_key->scheduleTask(create_authenticated_encrypten_key); + } + + auto emailOptIn = controller::EmailVerificationCode::create(userModel->getID(), model::table::EMAIL_OPT_IN_REGISTER); + auto emailOptInModel = emailOptIn->getModel(); + if (!emailOptInModel->insertIntoDB(false)) { + emailOptInModel->sendErrorsAsEmail(); + return stateError("insert emailOptIn failed"); + } + + em->addEmail(new model::Email(emailOptIn, user, model::Email::convertTypeFromInt(emailType))); + + if (login_after_register && session) { + Poco::JSON::Object* result = stateSuccess(); + + result->set("session_id", session->getHandle()); + return result; + } + + return stateSuccess(); + } \ No newline at end of file diff --git a/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp b/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp index 5b426782a..c9754a14f 100644 --- a/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp +++ b/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp @@ -1,144 +1,144 @@ -#include "JsonGetUserInfos.h" - -#include "../lib/DataTypeConverter.h" -#include "../SingletonManager/SessionManager.h" -#include "../controller/User.h" -#include "../controller/EmailVerificationCode.h" - -#include "../ServerConfig.h" - -Poco::UInt64 JsonGetUserInfos::readOrCreateEmailVerificationCode(int user_id, model::table::EmailOptInType type) -{ - try { - auto emailVerificationCode = controller::EmailVerificationCode::load(user_id, type); - if (!emailVerificationCode) { - emailVerificationCode = controller::EmailVerificationCode::create(user_id, type); - UniLib::controller::TaskPtr insert = new model::table::ModelInsertTask(emailVerificationCode->getModel(), false); - insert->scheduleTask(insert); - } - return emailVerificationCode->getModel()->getCode(); - } - catch (Poco::Exception& ex) { - ErrorList errors; - //printf("exception: %s\n", ex.displayText().data()); - errors.addError(new ParamError("JsonGetUserInfos::readOrCreateEmailVerificationCode", "exception: ", ex.displayText())); - errors.sendErrorsAsEmail(); - } - return 0; -} - -Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params) -{ - /* - 'session_id' => $session_id, - 'email' => $email, - 'ask' => ['EmailOptIn.Register'] - */ - // incoming - int session_id = 0; - std::string email; - Poco::JSON::Array::Ptr askArray; - - auto sm = SessionManager::getInstance(); - - // if is json object - if (params.type() == typeid(Poco::JSON::Object::Ptr)) { - Poco::JSON::Object::Ptr paramJsonObject = params.extract(); - /// Throws a RangeException if the value does not fit - /// into the result variable. - /// Throws a NotImplementedException if conversion is - /// not available for the given type. - /// Throws InvalidAccessException if Var is empty. - try { - paramJsonObject->get("email").convert(email); - paramJsonObject->get("session_id").convert(session_id); - askArray = paramJsonObject->getArray("ask"); - } - catch (Poco::Exception& ex) { - return stateError("json exception", ex.displayText()); - } - } - else { - return stateError("parameter format unknown"); - } - - if (!session_id) { - return stateError("session_id invalid"); - } - if (askArray.isNull()) { - return stateError("ask is zero or not an array"); - } - - auto session = sm->getSession(session_id); - if (!session) { - return customStateError("not found", "session not found"); - } - - auto session_user = session->getNewUser(); - auto session_user_model = session_user->getModel(); - bool isAdmin = false; - bool emailBelongToUser = false; - if (model::table::ROLE_ADMIN == session_user_model->getRole()) { - isAdmin = true; - } - if (session_user_model->getEmail() == email) { - emailBelongToUser = true; - } - - auto user = controller::User::create(); - if (1 != user->load(email)) { - return customStateError("not found", "user not found"); - } - auto user_model = user->getModel(); - - - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", "success"); - Poco::JSON::Array jsonErrorsArray; - Poco::JSON::Object jsonUser; - Poco::JSON::Object jsonServer; - - for (auto it = askArray->begin(); it != askArray->end(); it++) { - auto parameter = *it; - std::string parameterString; - try { - parameter.convert(parameterString); - if (parameterString == "EmailVerificationCode.Register" && isAdmin && !emailBelongToUser) { - auto code = readOrCreateEmailVerificationCode(user_model->getID(), model::table::EMAIL_OPT_IN_REGISTER_DIRECT); - if (code) { - jsonUser.set("EmailVerificationCode.Register", std::to_string(code)); - } - } - else if (parameterString == "loginServer.path") { - jsonServer.set("loginServer.path", ServerConfig::g_serverPath); - } - else if (parameterString == "user.pubkeyhex") { - jsonUser.set("pubkeyhex", user_model->getPublicKeyHex()); - } - else if (parameterString == "user.first_name") { - jsonUser.set("first_name", user_model->getFirstName()); - } - else if (parameterString == "user.last_name") { - jsonUser.set("last_name", user_model->getLastName()); - } - else if (parameterString == "user.disabled") { - jsonUser.set("disabled", user_model->isDisabled()); - } - else if (parameterString == "user.email_checked" && (isAdmin || emailBelongToUser)) { - jsonUser.set("email_checked", user_model->isEmailChecked()); - } - else if (parameterString == "user.identHash") { - auto email = user_model->getEmail(); - jsonUser.set("identHash", DRMakeStringHash(email.data(), email.size())); - } - } - catch (Poco::Exception& ex) { - jsonErrorsArray.add("ask parameter invalid"); - } - } - result->set("errors", jsonErrorsArray); - result->set("userData", jsonUser); - result->set("server", jsonServer); - return result; - +#include "JsonGetUserInfos.h" + +#include "../lib/DataTypeConverter.h" +#include "../SingletonManager/SessionManager.h" +#include "../controller/User.h" +#include "../controller/EmailVerificationCode.h" + +#include "../ServerConfig.h" + +Poco::UInt64 JsonGetUserInfos::readOrCreateEmailVerificationCode(int user_id, model::table::EmailOptInType type) +{ + try { + auto emailVerificationCode = controller::EmailVerificationCode::load(user_id, type); + if (!emailVerificationCode) { + emailVerificationCode = controller::EmailVerificationCode::create(user_id, type); + UniLib::controller::TaskPtr insert = new model::table::ModelInsertTask(emailVerificationCode->getModel(), false); + insert->scheduleTask(insert); + } + return emailVerificationCode->getModel()->getCode(); + } + catch (Poco::Exception& ex) { + ErrorList errors; + //printf("exception: %s\n", ex.displayText().data()); + errors.addError(new ParamError("JsonGetUserInfos::readOrCreateEmailVerificationCode", "exception: ", ex.displayText())); + errors.sendErrorsAsEmail(); + } + return 0; +} + +Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params) +{ + /* + 'session_id' => $session_id, + 'email' => $email, + 'ask' => ['EmailOptIn.Register'] + */ + // incoming + int session_id = 0; + std::string email; + Poco::JSON::Array::Ptr askArray; + + auto sm = SessionManager::getInstance(); + + // if is json object + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + /// Throws a RangeException if the value does not fit + /// into the result variable. + /// Throws a NotImplementedException if conversion is + /// not available for the given type. + /// Throws InvalidAccessException if Var is empty. + try { + paramJsonObject->get("email").convert(email); + paramJsonObject->get("session_id").convert(session_id); + askArray = paramJsonObject->getArray("ask"); + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + else { + return stateError("parameter format unknown"); + } + + if (!session_id) { + return stateError("session_id invalid"); + } + if (askArray.isNull()) { + return stateError("ask is zero or not an array"); + } + + auto session = sm->getSession(session_id); + if (!session) { + return customStateError("not found", "session not found"); + } + + auto session_user = session->getNewUser(); + auto session_user_model = session_user->getModel(); + bool isAdmin = false; + bool emailBelongToUser = false; + if (model::table::ROLE_ADMIN == session_user_model->getRole()) { + isAdmin = true; + } + if (session_user_model->getEmail() == email) { + emailBelongToUser = true; + } + + auto user = controller::User::create(); + if (1 != user->load(email)) { + return customStateError("not found", "user not found"); + } + auto user_model = user->getModel(); + + + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "success"); + Poco::JSON::Array jsonErrorsArray; + Poco::JSON::Object jsonUser; + Poco::JSON::Object jsonServer; + + for (auto it = askArray->begin(); it != askArray->end(); it++) { + auto parameter = *it; + std::string parameterString; + try { + parameter.convert(parameterString); + if (parameterString == "EmailVerificationCode.Register" && isAdmin && !emailBelongToUser) { + auto code = readOrCreateEmailVerificationCode(user_model->getID(), model::table::EMAIL_OPT_IN_REGISTER_DIRECT); + if (code) { + jsonUser.set("EmailVerificationCode.Register", std::to_string(code)); + } + } + else if (parameterString == "loginServer.path") { + jsonServer.set("loginServer.path", ServerConfig::g_serverPath); + } + else if (parameterString == "user.pubkeyhex") { + jsonUser.set("pubkeyhex", user_model->getPublicKeyHex()); + } + else if (parameterString == "user.first_name") { + jsonUser.set("first_name", user_model->getFirstName()); + } + else if (parameterString == "user.last_name") { + jsonUser.set("last_name", user_model->getLastName()); + } + else if (parameterString == "user.disabled") { + jsonUser.set("disabled", user_model->isDisabled()); + } + else if (parameterString == "user.email_checked" && (isAdmin || emailBelongToUser)) { + jsonUser.set("email_checked", user_model->isEmailChecked()); + } + else if (parameterString == "user.identHash") { + auto email = user_model->getEmail(); + jsonUser.set("identHash", DRMakeStringHash(email.data(), email.size())); + } + } + catch (Poco::Exception& ex) { + jsonErrorsArray.add("ask parameter invalid"); + } + } + result->set("errors", jsonErrorsArray); + result->set("userData", jsonUser); + result->set("server", jsonServer); + return result; + } \ No newline at end of file diff --git a/login_server/src/cpp/JSONInterface/JsonLogout.cpp b/login_server/src/cpp/JSONInterface/JsonLogout.cpp index 0b347f767..03cb25fb3 100644 --- a/login_server/src/cpp/JSONInterface/JsonLogout.cpp +++ b/login_server/src/cpp/JSONInterface/JsonLogout.cpp @@ -1,44 +1,44 @@ -#include "JsonLogout.h" - - - -#include "../SingletonManager/SessionManager.h" - - -Poco::JSON::Object* JsonLogout::handle(Poco::Dynamic::Var params) -{ - - auto sm = SessionManager::getInstance(); - int session_id = 0; - - // if is json object - if (params.type() == typeid(Poco::JSON::Object::Ptr)) { - Poco::JSON::Object::Ptr paramJsonObject = params.extract(); - /// Throws a RangeException if the value does not fit - /// into the result variable. - /// Throws a NotImplementedException if conversion is - /// not available for the given type. - /// Throws InvalidAccessException if Var is empty. - try { - paramJsonObject->get("session_id").convert(session_id); - } - catch (Poco::Exception& ex) { - return stateError("json exception", ex.displayText()); - } - } - else { - return stateError("parameter format unknown"); - } - - auto session = sm->getSession(session_id); - if (!session) { - return stateError("session not found", std::to_string(session_id)); - } - if (sm->releaseSession(session_id)) { - return stateSuccess(); - } - return stateError("error by releasing session"); - - - +#include "JsonLogout.h" + + + +#include "../SingletonManager/SessionManager.h" + + +Poco::JSON::Object* JsonLogout::handle(Poco::Dynamic::Var params) +{ + + auto sm = SessionManager::getInstance(); + int session_id = 0; + + // if is json object + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + /// Throws a RangeException if the value does not fit + /// into the result variable. + /// Throws a NotImplementedException if conversion is + /// not available for the given type. + /// Throws InvalidAccessException if Var is empty. + try { + paramJsonObject->get("session_id").convert(session_id); + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + else { + return stateError("parameter format unknown"); + } + + auto session = sm->getSession(session_id); + if (!session) { + return stateError("session not found", std::to_string(session_id)); + } + if (sm->releaseSession(session_id)) { + return stateSuccess(); + } + return stateError("error by releasing session"); + + + } \ No newline at end of file diff --git a/login_server/src/cpp/JSONInterface/JsonLogout.h b/login_server/src/cpp/JSONInterface/JsonLogout.h index 7243d2b8c..8b3def105 100644 --- a/login_server/src/cpp/JSONInterface/JsonLogout.h +++ b/login_server/src/cpp/JSONInterface/JsonLogout.h @@ -1,18 +1,18 @@ -#ifndef __JSON_INTERFACE_JSON_LOGOUT_ -#define __JSON_INTERFACE_JSON_LOGOUT_ - -#include "JsonRequestHandler.h" - -class JsonLogout : public JsonRequestHandler -{ -public: - JsonLogout(Poco::Net::IPAddress ip) : mClientIP(ip) {} - Poco::JSON::Object* handle(Poco::Dynamic::Var params); - -protected: - Poco::Net::IPAddress mClientIP; - - -}; - +#ifndef __JSON_INTERFACE_JSON_LOGOUT_ +#define __JSON_INTERFACE_JSON_LOGOUT_ + +#include "JsonRequestHandler.h" + +class JsonLogout : public JsonRequestHandler +{ +public: + JsonLogout(Poco::Net::IPAddress ip) : mClientIP(ip) {} + Poco::JSON::Object* handle(Poco::Dynamic::Var params); + +protected: + Poco::Net::IPAddress mClientIP; + + +}; + #endif // __JSON_INTERFACE_JSON_LOGOUT_ \ No newline at end of file diff --git a/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp b/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp index b66cba495..3f9bd35b3 100644 --- a/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp +++ b/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp @@ -1,141 +1,141 @@ -#include "JsonRequestHandler.h" - -#include "Poco/Net/HTTPServerRequest.h" -#include "Poco/Net/HTTPServerResponse.h" - -#include "Poco/URI.h" -#include "Poco/DeflatingStream.h" - -#include "Poco/JSON/Parser.h" - -#include "../ServerConfig.h" - -#include "../lib/DataTypeConverter.h" -#include "../SingletonManager/SessionManager.h" - - -void JsonRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) -{ - - response.setChunkedTransferEncoding(false); - response.setContentType("application/json"); - if (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_CORS_ALL) { - response.set("Access-Control-Allow-Origin", "*"); - response.set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); - } - //bool _compressResponse(request.hasToken("Accept-Encoding", "gzip")); - //if (_compressResponse) response.set("Content-Encoding", "gzip"); - - std::ostream& responseStream = response.send(); - //Poco::DeflatingOutputStream _gzipStream(_responseStream, Poco::DeflatingStreamBuf::STREAM_GZIP, 1); - //std::ostream& responseStream = _compressResponse ? _gzipStream : _responseStream; - - auto method = request.getMethod(); - std::istream& request_stream = request.stream(); - Poco::JSON::Object* json_result = nullptr; - if (method == "POST" || method == "PUT") { - // extract parameter from request - Poco::Dynamic::Var parsedResult = parseJsonWithErrorPrintFile(request_stream); - - if (parsedResult.size() != 0) { - json_result = handle(parsedResult); - } - else { - json_result = stateError("empty body"); - } - - } - else if(method == "GET") { - Poco::URI uri(request.getURI()); - auto queryParameters = uri.getQueryParameters(); - json_result = handle(queryParameters); - } - - if (json_result) { - if (!json_result->isNull("session_id")) { - int session_id = 0; - try { - json_result->get("session_id").convert(session_id); - } - catch (Poco::Exception& e) { - ErrorList erros; - erros.addError(new Error("json request", "invalid session_id")); - erros.sendErrorsAsEmail(); - } - if (session_id) { - auto session = SessionManager::getInstance()->getSession(session_id); - response.addCookie(session->getLoginCookie()); - } - } - json_result->stringify(responseStream); - delete json_result; - } - - //if (_compressResponse) _gzipStream.close(); -} - - -Poco::Dynamic::Var JsonRequestHandler::parseJsonWithErrorPrintFile(std::istream& request_stream, ErrorList* errorHandler /* = nullptr*/, const char* functionName /* = nullptr*/) -{ - // debugging answer - - std::stringstream responseStringStream; - for (std::string line; std::getline(request_stream, line); ) { - responseStringStream << line << std::endl; - } - - // extract parameter from request - Poco::JSON::Parser jsonParser; - Poco::Dynamic::Var parsedJson; - try { - parsedJson = jsonParser.parse(responseStringStream.str()); - - return parsedJson; - } - catch (Poco::Exception& ex) { - if (errorHandler) { - errorHandler->addError(new ParamError(functionName, "error parsing request answer", ex.displayText().data())); - errorHandler->sendErrorsAsEmail(responseStringStream.str()); - } - std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d_%m_%yT%H_%M_%S"); - std::string filename = dateTimeString + "_response.html"; - FILE* f = fopen(filename.data(), "wt"); - if (f) { - std::string responseString = responseStringStream.str(); - fwrite(responseString.data(), 1, responseString.size(), f); - fclose(f); - } - return Poco::Dynamic::Var(); - } - return Poco::Dynamic::Var(); -} - -Poco::JSON::Object* JsonRequestHandler::stateError(const char* msg, std::string details) -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", "error"); - result->set("msg", msg); - if (details != "") { - result->set("details", details); - } - return result; -} - -Poco::JSON::Object* JsonRequestHandler::stateSuccess() -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", "success"); - return result; -} - -Poco::JSON::Object* JsonRequestHandler::customStateError(const char* state, const char* msg, std::string details/* = ""*/) -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", state); - result->set("msg", msg); - if (details != "") { - result->set("details", details); - } - return result; -} - +#include "JsonRequestHandler.h" + +#include "Poco/Net/HTTPServerRequest.h" +#include "Poco/Net/HTTPServerResponse.h" + +#include "Poco/URI.h" +#include "Poco/DeflatingStream.h" + +#include "Poco/JSON/Parser.h" + +#include "../ServerConfig.h" + +#include "../lib/DataTypeConverter.h" +#include "../SingletonManager/SessionManager.h" + + +void JsonRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) +{ + + response.setChunkedTransferEncoding(false); + response.setContentType("application/json"); + if (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_CORS_ALL) { + response.set("Access-Control-Allow-Origin", "*"); + response.set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); + } + //bool _compressResponse(request.hasToken("Accept-Encoding", "gzip")); + //if (_compressResponse) response.set("Content-Encoding", "gzip"); + + std::ostream& responseStream = response.send(); + //Poco::DeflatingOutputStream _gzipStream(_responseStream, Poco::DeflatingStreamBuf::STREAM_GZIP, 1); + //std::ostream& responseStream = _compressResponse ? _gzipStream : _responseStream; + + auto method = request.getMethod(); + std::istream& request_stream = request.stream(); + Poco::JSON::Object* json_result = nullptr; + if (method == "POST" || method == "PUT") { + // extract parameter from request + Poco::Dynamic::Var parsedResult = parseJsonWithErrorPrintFile(request_stream); + + if (parsedResult.size() != 0) { + json_result = handle(parsedResult); + } + else { + json_result = stateError("empty body"); + } + + } + else if(method == "GET") { + Poco::URI uri(request.getURI()); + auto queryParameters = uri.getQueryParameters(); + json_result = handle(queryParameters); + } + + if (json_result) { + if (!json_result->isNull("session_id")) { + int session_id = 0; + try { + json_result->get("session_id").convert(session_id); + } + catch (Poco::Exception& e) { + ErrorList erros; + erros.addError(new Error("json request", "invalid session_id")); + erros.sendErrorsAsEmail(); + } + if (session_id) { + auto session = SessionManager::getInstance()->getSession(session_id); + response.addCookie(session->getLoginCookie()); + } + } + json_result->stringify(responseStream); + delete json_result; + } + + //if (_compressResponse) _gzipStream.close(); +} + + +Poco::Dynamic::Var JsonRequestHandler::parseJsonWithErrorPrintFile(std::istream& request_stream, ErrorList* errorHandler /* = nullptr*/, const char* functionName /* = nullptr*/) +{ + // debugging answer + + std::stringstream responseStringStream; + for (std::string line; std::getline(request_stream, line); ) { + responseStringStream << line << std::endl; + } + + // extract parameter from request + Poco::JSON::Parser jsonParser; + Poco::Dynamic::Var parsedJson; + try { + parsedJson = jsonParser.parse(responseStringStream.str()); + + return parsedJson; + } + catch (Poco::Exception& ex) { + if (errorHandler) { + errorHandler->addError(new ParamError(functionName, "error parsing request answer", ex.displayText().data())); + errorHandler->sendErrorsAsEmail(responseStringStream.str()); + } + std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d_%m_%yT%H_%M_%S"); + std::string filename = dateTimeString + "_response.html"; + FILE* f = fopen(filename.data(), "wt"); + if (f) { + std::string responseString = responseStringStream.str(); + fwrite(responseString.data(), 1, responseString.size(), f); + fclose(f); + } + return Poco::Dynamic::Var(); + } + return Poco::Dynamic::Var(); +} + +Poco::JSON::Object* JsonRequestHandler::stateError(const char* msg, std::string details) +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "error"); + result->set("msg", msg); + if (details != "") { + result->set("details", details); + } + return result; +} + +Poco::JSON::Object* JsonRequestHandler::stateSuccess() +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "success"); + return result; +} + +Poco::JSON::Object* JsonRequestHandler::customStateError(const char* state, const char* msg, std::string details/* = ""*/) +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", state); + result->set("msg", msg); + if (details != "") { + result->set("details", details); + } + return result; +} + diff --git a/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp b/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp index 903ccfdfb..d6eff52f9 100644 --- a/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp +++ b/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp @@ -1,82 +1,82 @@ -#include "JsonRequestHandlerFactory.h" - -#include "Poco/Net/HTTPServerRequest.h" - -#include "../SingletonManager/SessionManager.h" - -#include "JsonAdminEmailVerificationResend.h" -#include "JsonCheckSessionState.h" -#include "JsonCreateUser.h" -#include "JsonGetLogin.h" -#include "JsonUnknown.h" -#include "JsonTransaction.h" -#include "JsonGetRunningUserTasks.h" -#include "JsonGetUsers.h" -#include "JsonLoginViaEmailVerificationCode.h" -#include "JsonGetUserInfos.h" -#include "JsonUpdateUserInfos.h" -#include "JsonUnsecureLogin.h" -#include "JsonLogout.h" - -JsonRequestHandlerFactory::JsonRequestHandlerFactory() - : mRemoveGETParameters("^/([a-zA-Z0-9_-]*)"), mLogging(Poco::Logger::get("requestLog")) -{ -} - -Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request) -{ - std::string uri = request.getURI(); - std::string url_first_part; - std::stringstream logStream; - - mRemoveGETParameters.extract(uri, url_first_part); - - std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S"); - logStream << dateTimeString << " call " << uri; - - mLogging.information(logStream.str()); - - auto client_host = request.clientAddress().host(); - //auto client_ip = request.clientAddress(); - // X-Real-IP forwarded ip from nginx config - auto client_host_string = request.get("X-Real-IP", client_host.toString()); - client_host = Poco::Net::IPAddress(client_host_string); - - if (url_first_part == "/login") { - return new JsonGetLogin; - } - else if (url_first_part == "/checkSessionState") { - return new JsonCheckSessionState; - } - else if (url_first_part == "/checkTransaction") { - return new JsonTransaction; - } - else if (url_first_part == "/getRunningUserTasks") { - return new JsonGetRunningUserTasks; - } - else if (url_first_part == "/getUsers") { - return new JsonGetUsers; - } - else if (url_first_part == "/createUser") { - return new JsonCreateUser(client_host); - } - else if (url_first_part == "/adminEmailVerificationResend") { - return new JsonAdminEmailVerificationResend; - } - else if (url_first_part == "/getUserInfos") { - return new JsonGetUserInfos; - } - else if (url_first_part == "/updateUserInfos") { - return new JsonUpdateUserInfos; - } - else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { - return new JsonUnsecureLogin(client_host); - } - else if (url_first_part == "/loginViaEmailVerificationCode") { - return new JsonLoginViaEmailVerificationCode(client_host); - } - else if (url_first_part == "/logout") { - return new JsonLogout(client_host); - } - return new JsonUnknown; -} +#include "JsonRequestHandlerFactory.h" + +#include "Poco/Net/HTTPServerRequest.h" + +#include "../SingletonManager/SessionManager.h" + +#include "JsonAdminEmailVerificationResend.h" +#include "JsonCheckSessionState.h" +#include "JsonCreateUser.h" +#include "JsonGetLogin.h" +#include "JsonUnknown.h" +#include "JsonTransaction.h" +#include "JsonGetRunningUserTasks.h" +#include "JsonGetUsers.h" +#include "JsonLoginViaEmailVerificationCode.h" +#include "JsonGetUserInfos.h" +#include "JsonUpdateUserInfos.h" +#include "JsonUnsecureLogin.h" +#include "JsonLogout.h" + +JsonRequestHandlerFactory::JsonRequestHandlerFactory() + : mRemoveGETParameters("^/([a-zA-Z0-9_-]*)"), mLogging(Poco::Logger::get("requestLog")) +{ +} + +Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request) +{ + std::string uri = request.getURI(); + std::string url_first_part; + std::stringstream logStream; + + mRemoveGETParameters.extract(uri, url_first_part); + + std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S"); + logStream << dateTimeString << " call " << uri; + + mLogging.information(logStream.str()); + + auto client_host = request.clientAddress().host(); + //auto client_ip = request.clientAddress(); + // X-Real-IP forwarded ip from nginx config + auto client_host_string = request.get("X-Real-IP", client_host.toString()); + client_host = Poco::Net::IPAddress(client_host_string); + + if (url_first_part == "/login") { + return new JsonGetLogin; + } + else if (url_first_part == "/checkSessionState") { + return new JsonCheckSessionState; + } + else if (url_first_part == "/checkTransaction") { + return new JsonTransaction; + } + else if (url_first_part == "/getRunningUserTasks") { + return new JsonGetRunningUserTasks; + } + else if (url_first_part == "/getUsers") { + return new JsonGetUsers; + } + else if (url_first_part == "/createUser") { + return new JsonCreateUser(client_host); + } + else if (url_first_part == "/adminEmailVerificationResend") { + return new JsonAdminEmailVerificationResend; + } + else if (url_first_part == "/getUserInfos") { + return new JsonGetUserInfos; + } + else if (url_first_part == "/updateUserInfos") { + return new JsonUpdateUserInfos; + } + else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { + return new JsonUnsecureLogin(client_host); + } + else if (url_first_part == "/loginViaEmailVerificationCode") { + return new JsonLoginViaEmailVerificationCode(client_host); + } + else if (url_first_part == "/logout") { + return new JsonLogout(client_host); + } + return new JsonUnknown; +} diff --git a/login_server/src/cpp/JSONInterface/JsonTransaction.cpp b/login_server/src/cpp/JSONInterface/JsonTransaction.cpp index 62f001469..dcc5023f9 100644 --- a/login_server/src/cpp/JSONInterface/JsonTransaction.cpp +++ b/login_server/src/cpp/JSONInterface/JsonTransaction.cpp @@ -1,175 +1,175 @@ -#include "JsonTransaction.h" -#include "Poco/URI.h" -#include "Poco/Dynamic/Struct.h" - -#include "../SingletonManager/SessionManager.h" -#include "../ServerConfig.h" - -Poco::JSON::Object* JsonTransaction::handle(Poco::Dynamic::Var params) -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - int session_id = 0; - - // if is json object - if (params.type() == typeid(Poco::JSON::Object::Ptr)) { - Poco::JSON::Object::Ptr paramJsonObject = params.extract(); - - try { - /// Throws a RangeException if the value does not fit - /// into the result variable. - /// Throws a NotImplementedException if conversion is - /// not available for the given type. - /// Throws InvalidAccessException if Var is empty. - paramJsonObject->get("session_id").convert(session_id); - auto sm = SessionManager::getInstance(); - if (session_id != 0) { - auto session = sm->getSession(session_id); - if (!session) { - result->set("state", "error"); - result->set("msg", "session not found"); - return result; - } - - int balance = 0; - - if (!paramJsonObject->isNull("balance")) { - paramJsonObject->get("balance").convert(balance); - if (balance) { - auto u = session->getUser(); - if (u) { - u->setBalance(balance); - } - auto nu = session->getNewUser(); - if (!nu.isNull()) { - nu->setBalance(balance); - } - } - } - - std::string transactionBase64String; - Poco::Dynamic::Var transaction_base64 = paramJsonObject->get("transaction_base64"); - bool auto_sign = false; - auto auto_sign_json = paramJsonObject->get("auto_sign"); - if (!auto_sign_json.isEmpty()) { - auto_sign_json.convert(auto_sign); - } - - if (transaction_base64.isString()) { - paramJsonObject->get("transaction_base64").convert(transactionBase64String); - - if (!session->startProcessingTransaction(transactionBase64String, auto_sign)) { - if (auto_sign) { - auto errorJson = session->getErrorsArray(); - result->set("state", "error"); - result->set("msg", "error processing transaction"); - result->set("details", errorJson); - return result; - } - auto lastError = session->getLastError(); - if (lastError) delete lastError; - result->set("state", "error"); - result->set("msg", "already enlisted"); - return result; - } - - } else { - Poco::DynamicStruct ds = *paramJsonObject; - int alreadyEnlisted = 0; - - for (int i = 0; i < ds["transaction_base64"].size(); i++) { - ds["transaction_base64"][i].convert(transactionBase64String); - if (!session->startProcessingTransaction(transactionBase64String, auto_sign)) { - auto lastError = session->getLastError(); - if (lastError) delete lastError; - alreadyEnlisted++; - } - } - - if (alreadyEnlisted > 0) { - result->set("state", "warning"); - result->set("msg", std::to_string(alreadyEnlisted) + " already enlisted"); - return result; - } - } - - - - result->set("state", "success"); - return result; - } - - } - catch (Poco::Exception& ex) { - printf("[JsonTransaction::handle] try to use params as jsonObject: %s\n", ex.displayText().data()); - result->set("state", "error"); - result->set("msg", "json exception"); - result->set("details", ex.displayText()); - return result; - } - } - else if (params.isVector()) { - const Poco::URI::QueryParameters queryParams = params.extract(); - auto transactionIT = queryParams.begin(); - for (auto it = queryParams.begin(); it != queryParams.end(); it++) { - if (it->first == "session_id") { - session_id = stoi(it->second); - //break; - } - else if (it->first == "transaction_base64") { - transactionIT = it; - } - } - if (session_id) { - auto sm = SessionManager::getInstance(); - auto session = sm->getSession(session_id); - if (!session) { - result->set("state", "error"); - result->set("msg", "session not found"); - return result; - } - if (!session->startProcessingTransaction(transactionIT->second)) { - auto lastError = session->getLastError(); - if (lastError) delete lastError; - result->set("state", "error"); - result->set("msg", "already enlisted"); - return result; - } - result->set("state", "success"); - return result; - } - else { - result->set("state", "error"); - result->set("msg", "session id not set"); - return result; - } - } - else if (params.isStruct()) { - result->set("state", "error"); - result->set("msg", "struct not implemented yet"); - } - else if (params.isArray()) { - result->set("state", "error"); - result->set("msg", "array not implemented yet"); - } - else if (params.isList()) { - result->set("state", "error"); - result->set("msg", "list not implemented yet"); - } - else if (params.isString()) { - result->set("state", "error"); - result->set("msg", "string not implemented yet"); - } - else if (params.isDeque()) { - result->set("state", "error"); - result->set("msg", "deque not implemented yet"); - } - else { - - result->set("state", "error"); - result->set("msg", "format not implemented"); - result->set("details", std::string(params.type().name())); - } - - return result; -} - +#include "JsonTransaction.h" +#include "Poco/URI.h" +#include "Poco/Dynamic/Struct.h" + +#include "../SingletonManager/SessionManager.h" +#include "../ServerConfig.h" + +Poco::JSON::Object* JsonTransaction::handle(Poco::Dynamic::Var params) +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + int session_id = 0; + + // if is json object + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + + try { + /// Throws a RangeException if the value does not fit + /// into the result variable. + /// Throws a NotImplementedException if conversion is + /// not available for the given type. + /// Throws InvalidAccessException if Var is empty. + paramJsonObject->get("session_id").convert(session_id); + auto sm = SessionManager::getInstance(); + if (session_id != 0) { + auto session = sm->getSession(session_id); + if (!session) { + result->set("state", "error"); + result->set("msg", "session not found"); + return result; + } + + int balance = 0; + + if (!paramJsonObject->isNull("balance")) { + paramJsonObject->get("balance").convert(balance); + if (balance) { + auto u = session->getUser(); + if (u) { + u->setBalance(balance); + } + auto nu = session->getNewUser(); + if (!nu.isNull()) { + nu->setBalance(balance); + } + } + } + + std::string transactionBase64String; + Poco::Dynamic::Var transaction_base64 = paramJsonObject->get("transaction_base64"); + bool auto_sign = false; + auto auto_sign_json = paramJsonObject->get("auto_sign"); + if (!auto_sign_json.isEmpty()) { + auto_sign_json.convert(auto_sign); + } + + if (transaction_base64.isString()) { + paramJsonObject->get("transaction_base64").convert(transactionBase64String); + + if (!session->startProcessingTransaction(transactionBase64String, auto_sign)) { + if (auto_sign) { + auto errorJson = session->getErrorsArray(); + result->set("state", "error"); + result->set("msg", "error processing transaction"); + result->set("details", errorJson); + return result; + } + auto lastError = session->getLastError(); + if (lastError) delete lastError; + result->set("state", "error"); + result->set("msg", "already enlisted"); + return result; + } + + } else { + Poco::DynamicStruct ds = *paramJsonObject; + int alreadyEnlisted = 0; + + for (int i = 0; i < ds["transaction_base64"].size(); i++) { + ds["transaction_base64"][i].convert(transactionBase64String); + if (!session->startProcessingTransaction(transactionBase64String, auto_sign)) { + auto lastError = session->getLastError(); + if (lastError) delete lastError; + alreadyEnlisted++; + } + } + + if (alreadyEnlisted > 0) { + result->set("state", "warning"); + result->set("msg", std::to_string(alreadyEnlisted) + " already enlisted"); + return result; + } + } + + + + result->set("state", "success"); + return result; + } + + } + catch (Poco::Exception& ex) { + printf("[JsonTransaction::handle] try to use params as jsonObject: %s\n", ex.displayText().data()); + result->set("state", "error"); + result->set("msg", "json exception"); + result->set("details", ex.displayText()); + return result; + } + } + else if (params.isVector()) { + const Poco::URI::QueryParameters queryParams = params.extract(); + auto transactionIT = queryParams.begin(); + for (auto it = queryParams.begin(); it != queryParams.end(); it++) { + if (it->first == "session_id") { + session_id = stoi(it->second); + //break; + } + else if (it->first == "transaction_base64") { + transactionIT = it; + } + } + if (session_id) { + auto sm = SessionManager::getInstance(); + auto session = sm->getSession(session_id); + if (!session) { + result->set("state", "error"); + result->set("msg", "session not found"); + return result; + } + if (!session->startProcessingTransaction(transactionIT->second)) { + auto lastError = session->getLastError(); + if (lastError) delete lastError; + result->set("state", "error"); + result->set("msg", "already enlisted"); + return result; + } + result->set("state", "success"); + return result; + } + else { + result->set("state", "error"); + result->set("msg", "session id not set"); + return result; + } + } + else if (params.isStruct()) { + result->set("state", "error"); + result->set("msg", "struct not implemented yet"); + } + else if (params.isArray()) { + result->set("state", "error"); + result->set("msg", "array not implemented yet"); + } + else if (params.isList()) { + result->set("state", "error"); + result->set("msg", "list not implemented yet"); + } + else if (params.isString()) { + result->set("state", "error"); + result->set("msg", "string not implemented yet"); + } + else if (params.isDeque()) { + result->set("state", "error"); + result->set("msg", "deque not implemented yet"); + } + else { + + result->set("state", "error"); + result->set("msg", "format not implemented"); + result->set("details", std::string(params.type().name())); + } + + return result; +} + diff --git a/login_server/src/cpp/JSONInterface/JsonTransaction.h b/login_server/src/cpp/JSONInterface/JsonTransaction.h index 7d842fac0..95e9885be 100644 --- a/login_server/src/cpp/JSONInterface/JsonTransaction.h +++ b/login_server/src/cpp/JSONInterface/JsonTransaction.h @@ -1,19 +1,19 @@ -#ifndef __JSON_INTERFACE_JSON_TRANSACTION_ -#define __JSON_INTERFACE_JSON_TRANSACTION_ - -#include "JsonRequestHandler.h" - -class Session; - -class JsonTransaction : public JsonRequestHandler -{ -public: - Poco::JSON::Object* handle(Poco::Dynamic::Var params); - -protected: - bool startProcessingTransaction(Session* session, const std::string& transactionBase64); - - -}; - +#ifndef __JSON_INTERFACE_JSON_TRANSACTION_ +#define __JSON_INTERFACE_JSON_TRANSACTION_ + +#include "JsonRequestHandler.h" + +class Session; + +class JsonTransaction : public JsonRequestHandler +{ +public: + Poco::JSON::Object* handle(Poco::Dynamic::Var params); + +protected: + bool startProcessingTransaction(Session* session, const std::string& transactionBase64); + + +}; + #endif // __JSON_INTERFACE_JSON_TRANSACTION_ \ No newline at end of file diff --git a/login_server/src/cpp/ServerConfig.cpp b/login_server/src/cpp/ServerConfig.cpp index f319ea460..d37480a80 100644 --- a/login_server/src/cpp/ServerConfig.cpp +++ b/login_server/src/cpp/ServerConfig.cpp @@ -1,349 +1,349 @@ -#include "ServerConfig.h" -#include "Crypto/mnemonic_german.h" -#include "Crypto/mnemonic_german2.h" -#include "Crypto/mnemonic_bip0039.h" -#include "Crypto/DRRandom.h" -#include "lib/DataTypeConverter.h" -#include "sodium.h" - - -#include "Poco/Net/SSLManager.h" -#include "Poco/Net/KeyConsoleHandler.h" -#include "Poco/Net/RejectCertificateHandler.h" -#include "Poco/Net/DNS.h" -#include "Poco/SharedPtr.h" - -#include "Poco/Mutex.h" -#include "Poco/Path.h" -#include "Poco/FileStream.h" -#include "Poco/LocalDateTime.h" -#include "Poco/DateTimeFormat.h" -#include "Poco/DateTimeFormatter.h" - - -using Poco::Net::SSLManager; -using Poco::Net::Context; -using Poco::Net::KeyConsoleHandler; -using Poco::Net::PrivateKeyPassphraseHandler; -using Poco::Net::InvalidCertificateHandler; -using Poco::Net::RejectCertificateHandler; -using Poco::SharedPtr; - -namespace ServerConfig { - -#define SESSION_TIMEOUT_DEFAULT 10 - - Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; - ObfusArray* g_ServerCryptoKey = nullptr; - ObfusArray* g_ServerKeySeed = nullptr; -// std::string g_ServerAdminPublic; - UniLib::controller::CPUSheduler* g_CPUScheduler = nullptr; - UniLib::controller::CPUSheduler* g_CryptoCPUScheduler = nullptr; - Context::Ptr g_SSL_CLient_Context = nullptr; - Poco::Util::Timer g_CronJobsTimer; - EmailAccount g_EmailAccount; - int g_SessionTimeout = SESSION_TIMEOUT_DEFAULT; - std::string g_serverPath; - int g_serverPort = 0; - Languages g_default_locale; - std::string g_php_serverPath; - std::string g_php_serverHost; - int g_phpServerPort; - Poco::Mutex g_TimeMutex; - int g_FakeLoginSleepTime = 820; - std::string g_versionString = ""; - bool g_disableEmail = false; - ServerSetupType g_ServerSetupType = SERVER_TYPE_PRODUCTION; - std::string g_gRPCRelayServerFullURL; - MemoryBin* g_CryptoAppSecret = nullptr; - AllowUnsecure g_AllowUnsecureFlags = NOT_UNSECURE; - -#ifdef __linux__ -#include -#include -#include -#include -#include -#include -#endif //#ifdef __linux__ - - std::string getHostIpString() - { -#ifdef __linux__ - struct ifaddrs * ifAddrStruct = NULL; - struct ifaddrs * ifa = NULL; - void * tmpAddrPtr = NULL; - - getifaddrs(&ifAddrStruct); - std::string ipAddressString; - - for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) { - continue; - } - if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 - // is a valid IP4 Address - tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - char addressBuffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); - ipAddressString = addressBuffer; - printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); - } - else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 - // is a valid IP6 Address - tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - char addressBuffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); - printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); - } - } - if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); - return ipAddressString; -#else //__linux__ - std::string ipAddressString = ""; - auto host = Poco::Net::DNS::thisHost(); - for (auto it = host.addresses().begin(); it != host.addresses().end(); it++) { - auto ipAddress = *it; - if (!ipAddress.isIPv4Compatible() && !ipAddress.isIPv4Mapped()) { - continue; - } - if (ipAddress.isLoopback()) { - continue; - } - ipAddressString = ipAddress.toString(); - //isIPv4Compatible - //!isLoopback - //printf("ipaddress: %s\n", ipAddressString.data()); - break; - //break; - } - return ipAddressString; -#endif // __linux__ - } - - bool replaceZeroIPWithLocalhostIP(std::string& url) - { - auto pos = url.find("0.0.0.0", 0); - if (pos != std::string::npos) { - std::string ipAddressString = getHostIpString(); - if ("" != ipAddressString) { - url.replace(pos, 7, ipAddressString); - } - } - - //printf("ipaddress: %s\n", ipAddress.data()); - - return true; - } - - ServerSetupType getServerSetupTypeFromString(const std::string& serverSetupTypeString) { - if ("test" == serverSetupTypeString) { - return SERVER_TYPE_TEST; - } - if ("staging" == serverSetupTypeString) { - return SERVER_TYPE_STAGING; - } - if ("production" == serverSetupTypeString) { - return SERVER_TYPE_PRODUCTION; - } - return SERVER_TYPE_PRODUCTION; - } - - - bool loadMnemonicWordLists() - { - for (int i = 0; i < MNEMONIC_MAX; i++) { - int iResult = 0; - switch (i) { - case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER: - iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german, g_mnemonic_german_original_size, g_mnemonic_german_compressed_size); - if (iResult) { - printf("[%s] error init german mnemonic set, error nr: %d\n", __FUNCTION__, iResult); - return false; - } - g_Mnemonic_WordLists[i].printToFile("de_words.txt"); - break; - case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES: - iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german2, g_mnemonic_german2_original_size, g_mnemonic_german2_compressed_size); - if (iResult) { - printf("[%s] error init german mnemonic set 2, error nr: %d\n", __FUNCTION__, iResult); - return false; - } - g_Mnemonic_WordLists[i].printToFile("de_words2.txt"); - break; - case MNEMONIC_BIP0039_SORTED_ORDER: - iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_bip0039, g_mnemonic_bip0039_original_size, g_mnemonic_bip0039_compressed_size); - if (iResult) { - printf("[%s] error init bip0039 mnemonic set, error nr: %d\n", __FUNCTION__, iResult); - return false; - } - //g_Mnemonic_WordLists[i].printToFile("en_words.txt"); - break; - default: printf("[%s] unknown MnemonicType\n", __FUNCTION__); return false; - } - } - return true; - } - - bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg) - { - auto serverKey = cfg.getString("crypto.server_key"); - unsigned char key[crypto_shorthash_KEYBYTES]; - size_t realBinSize = 0; - NULLPAD_10; - if (sodium_hex2bin(key, crypto_shorthash_KEYBYTES, serverKey.data(), serverKey.size(), nullptr, &realBinSize, nullptr)) { - printf("[%s] serverKey isn't valid hex: %s\n", __FUNCTION__, serverKey.data()); - return false; - } - if (realBinSize != crypto_shorthash_KEYBYTES) { - printf("[%s] serverKey hasn't valid size, expecting: %u, get: %lu\n", - __FUNCTION__, crypto_shorthash_KEYBYTES, realBinSize); - return false; - } - g_ServerCryptoKey = new ObfusArray(realBinSize, key); - g_ServerKeySeed = new ObfusArray(9*8); - Poco::Int64 i1 = randombytes_random(); - Poco::Int64 i2 = randombytes_random(); - g_ServerKeySeed->put(0, i1 | (i2 << 8)); - - //g_ServerAdminPublic = cfg.getString("crypto.server_admin_public"); - - DISASM_FALSERET; - g_SessionTimeout = cfg.getInt("session.timeout", SESSION_TIMEOUT_DEFAULT); - g_serverPath = cfg.getString("loginServer.path", ""); - replaceZeroIPWithLocalhostIP(g_serverPath); - g_default_locale = LanguageManager::languageFromString(cfg.getString("loginServer.default_locale")); - g_serverPort = cfg.getInt("loginServer.port", 0); - g_phpServerPort = cfg.getInt("phpServer.port", 0); - // replace 0.0.0.0 with actual server ip - - g_php_serverPath = cfg.getString("phpServer.url", ""); - replaceZeroIPWithLocalhostIP(g_php_serverPath); - g_php_serverHost = cfg.getString("phpServer.host", ""); - replaceZeroIPWithLocalhostIP(g_php_serverHost); - //g_ServerSetupType - auto serverSetupTypeString = cfg.getString("ServerSetupType", ""); - g_ServerSetupType = getServerSetupTypeFromString(serverSetupTypeString); - - // app secret for encrypt user private keys - // TODO: encrypt with server admin key - auto app_secret_string = cfg.getString("crypto.app_secret", ""); - if ("" != app_secret_string) { - g_CryptoAppSecret = DataTypeConverter::hexToBin(app_secret_string); - } - //g_CryptoAppSecret - - g_gRPCRelayServerFullURL = cfg.getString("grpc.server", ""); - - // unsecure flags - //g_AllowUnsecureFlags - if (cfg.getInt("unsecure.allow_passwort_via_json_request", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_PASSWORD_REQUESTS); - } - if (cfg.getInt("unsecure.allow_auto_sign_transactions", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_AUTO_SIGN_TRANSACTIONS); - } - if (cfg.getInt("unsecure.allow_cors_all", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_CORS_ALL); - } - if (cfg.getInt("unsecure.allow_all_passwords", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_ALLOW_ALL_PASSWORDS); - } - - return true; - } - - bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg) - { - g_disableEmail = cfg.getBool("email.disable", false); - if (g_disableEmail) { - printf("Email is disabled!\n"); - } - else { - g_EmailAccount.sender = cfg.getString("email.sender"); - g_EmailAccount.username = cfg.getString("email.username"); - g_EmailAccount.password = cfg.getString("email.password"); - g_EmailAccount.url = cfg.getString("email.smtp.url"); - g_EmailAccount.port = cfg.getInt("email.smtp.port"); - } - DISASM_FALSERET; - //g_ServerKeySeed->put(3, DRRandom::r64()); - return true; - } - - bool initSSLClientContext() - { - SharedPtr pCert = new RejectCertificateHandler(false); // reject invalid certificates - /* - Context(Usage usage, - const std::string& certificateNameOrPath, - VerificationMode verMode = VERIFY_RELAXED, - int options = OPT_DEFAULTS, - const std::string& certificateStoreName = CERT_STORE_MY); - */ - try { -#ifdef POCO_NETSSL_WIN - g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "cacert.pem", Context::VERIFY_RELAXED, Context::OPT_DEFAULTS); -#else - - g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "", "", Poco::Path::config() + "grd_login/cacert.pem", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); -#endif - } catch(Poco::Exception& ex) { - printf("[ServerConfig::initSSLClientContext] error init ssl context, maybe no cacert.pem found?\nPlease make sure you have cacert.pem (CA/root certificates) next to binary from https://curl.haxx.se/docs/caextract.html\n"); - return false; - } - DISASM_FALSERET; - SSLManager::instance().initializeClient(0, pCert, g_SSL_CLient_Context); - - g_ServerKeySeed->put(5, DRRandom::r64()); - - return true; - } - - void unload() { - if (g_ServerCryptoKey) { - delete g_ServerCryptoKey; - } - if (g_ServerKeySeed) { - delete g_ServerKeySeed; - } - if (g_CPUScheduler) { - delete g_CPUScheduler; - } - - if (g_CryptoCPUScheduler) { - delete g_CryptoCPUScheduler; - } - if (g_CryptoAppSecret) { - MemoryManager::getInstance()->releaseMemory(g_CryptoAppSecret); - g_CryptoAppSecret = nullptr; - } - } - - void writeToFile(std::istream& datas, std::string fileName) - { - static Poco::Mutex mutex; - - mutex.lock(); - - Poco::FileOutputStream file(fileName, std::ios::out | std::ios::app); - - if (!file.good()) { - printf("[ServerConfig::writeToFile] error creating file with name: %s\n", fileName.data()); - mutex.unlock(); - return; - } - - Poco::LocalDateTime now; - - std::string dateTimeStr = Poco::DateTimeFormatter::format(now, Poco::DateTimeFormat::ISO8601_FORMAT); - file << dateTimeStr << std::endl; - - for (std::string line; std::getline(datas, line); ) { - file << line << std::endl; - } - file << std::endl; - file.close(); - mutex.unlock(); - } +#include "ServerConfig.h" +#include "Crypto/mnemonic_german.h" +#include "Crypto/mnemonic_german2.h" +#include "Crypto/mnemonic_bip0039.h" +#include "Crypto/DRRandom.h" +#include "lib/DataTypeConverter.h" +#include "sodium.h" + + +#include "Poco/Net/SSLManager.h" +#include "Poco/Net/KeyConsoleHandler.h" +#include "Poco/Net/RejectCertificateHandler.h" +#include "Poco/Net/DNS.h" +#include "Poco/SharedPtr.h" + +#include "Poco/Mutex.h" +#include "Poco/Path.h" +#include "Poco/FileStream.h" +#include "Poco/LocalDateTime.h" +#include "Poco/DateTimeFormat.h" +#include "Poco/DateTimeFormatter.h" + + +using Poco::Net::SSLManager; +using Poco::Net::Context; +using Poco::Net::KeyConsoleHandler; +using Poco::Net::PrivateKeyPassphraseHandler; +using Poco::Net::InvalidCertificateHandler; +using Poco::Net::RejectCertificateHandler; +using Poco::SharedPtr; + +namespace ServerConfig { + +#define SESSION_TIMEOUT_DEFAULT 10 + + Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; + ObfusArray* g_ServerCryptoKey = nullptr; + ObfusArray* g_ServerKeySeed = nullptr; +// std::string g_ServerAdminPublic; + UniLib::controller::CPUSheduler* g_CPUScheduler = nullptr; + UniLib::controller::CPUSheduler* g_CryptoCPUScheduler = nullptr; + Context::Ptr g_SSL_CLient_Context = nullptr; + Poco::Util::Timer g_CronJobsTimer; + EmailAccount g_EmailAccount; + int g_SessionTimeout = SESSION_TIMEOUT_DEFAULT; + std::string g_serverPath; + int g_serverPort = 0; + Languages g_default_locale; + std::string g_php_serverPath; + std::string g_php_serverHost; + int g_phpServerPort; + Poco::Mutex g_TimeMutex; + int g_FakeLoginSleepTime = 820; + std::string g_versionString = ""; + bool g_disableEmail = false; + ServerSetupType g_ServerSetupType = SERVER_TYPE_PRODUCTION; + std::string g_gRPCRelayServerFullURL; + MemoryBin* g_CryptoAppSecret = nullptr; + AllowUnsecure g_AllowUnsecureFlags = NOT_UNSECURE; + +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#endif //#ifdef __linux__ + + std::string getHostIpString() + { +#ifdef __linux__ + struct ifaddrs * ifAddrStruct = NULL; + struct ifaddrs * ifa = NULL; + void * tmpAddrPtr = NULL; + + getifaddrs(&ifAddrStruct); + std::string ipAddressString; + + for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) { + continue; + } + if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 + // is a valid IP4 Address + tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + char addressBuffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); + ipAddressString = addressBuffer; + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); + } + else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 + // is a valid IP6 Address + tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + char addressBuffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); + } + } + if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); + return ipAddressString; +#else //__linux__ + std::string ipAddressString = ""; + auto host = Poco::Net::DNS::thisHost(); + for (auto it = host.addresses().begin(); it != host.addresses().end(); it++) { + auto ipAddress = *it; + if (!ipAddress.isIPv4Compatible() && !ipAddress.isIPv4Mapped()) { + continue; + } + if (ipAddress.isLoopback()) { + continue; + } + ipAddressString = ipAddress.toString(); + //isIPv4Compatible + //!isLoopback + //printf("ipaddress: %s\n", ipAddressString.data()); + break; + //break; + } + return ipAddressString; +#endif // __linux__ + } + + bool replaceZeroIPWithLocalhostIP(std::string& url) + { + auto pos = url.find("0.0.0.0", 0); + if (pos != std::string::npos) { + std::string ipAddressString = getHostIpString(); + if ("" != ipAddressString) { + url.replace(pos, 7, ipAddressString); + } + } + + //printf("ipaddress: %s\n", ipAddress.data()); + + return true; + } + + ServerSetupType getServerSetupTypeFromString(const std::string& serverSetupTypeString) { + if ("test" == serverSetupTypeString) { + return SERVER_TYPE_TEST; + } + if ("staging" == serverSetupTypeString) { + return SERVER_TYPE_STAGING; + } + if ("production" == serverSetupTypeString) { + return SERVER_TYPE_PRODUCTION; + } + return SERVER_TYPE_PRODUCTION; + } + + + bool loadMnemonicWordLists() + { + for (int i = 0; i < MNEMONIC_MAX; i++) { + int iResult = 0; + switch (i) { + case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER: + iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german, g_mnemonic_german_original_size, g_mnemonic_german_compressed_size); + if (iResult) { + printf("[%s] error init german mnemonic set, error nr: %d\n", __FUNCTION__, iResult); + return false; + } + g_Mnemonic_WordLists[i].printToFile("de_words.txt"); + break; + case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES: + iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german2, g_mnemonic_german2_original_size, g_mnemonic_german2_compressed_size); + if (iResult) { + printf("[%s] error init german mnemonic set 2, error nr: %d\n", __FUNCTION__, iResult); + return false; + } + g_Mnemonic_WordLists[i].printToFile("de_words2.txt"); + break; + case MNEMONIC_BIP0039_SORTED_ORDER: + iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_bip0039, g_mnemonic_bip0039_original_size, g_mnemonic_bip0039_compressed_size); + if (iResult) { + printf("[%s] error init bip0039 mnemonic set, error nr: %d\n", __FUNCTION__, iResult); + return false; + } + //g_Mnemonic_WordLists[i].printToFile("en_words.txt"); + break; + default: printf("[%s] unknown MnemonicType\n", __FUNCTION__); return false; + } + } + return true; + } + + bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg) + { + auto serverKey = cfg.getString("crypto.server_key"); + unsigned char key[crypto_shorthash_KEYBYTES]; + size_t realBinSize = 0; + NULLPAD_10; + if (sodium_hex2bin(key, crypto_shorthash_KEYBYTES, serverKey.data(), serverKey.size(), nullptr, &realBinSize, nullptr)) { + printf("[%s] serverKey isn't valid hex: %s\n", __FUNCTION__, serverKey.data()); + return false; + } + if (realBinSize != crypto_shorthash_KEYBYTES) { + printf("[%s] serverKey hasn't valid size, expecting: %u, get: %lu\n", + __FUNCTION__, crypto_shorthash_KEYBYTES, realBinSize); + return false; + } + g_ServerCryptoKey = new ObfusArray(realBinSize, key); + g_ServerKeySeed = new ObfusArray(9*8); + Poco::Int64 i1 = randombytes_random(); + Poco::Int64 i2 = randombytes_random(); + g_ServerKeySeed->put(0, i1 | (i2 << 8)); + + //g_ServerAdminPublic = cfg.getString("crypto.server_admin_public"); + + DISASM_FALSERET; + g_SessionTimeout = cfg.getInt("session.timeout", SESSION_TIMEOUT_DEFAULT); + g_serverPath = cfg.getString("loginServer.path", ""); + replaceZeroIPWithLocalhostIP(g_serverPath); + g_default_locale = LanguageManager::languageFromString(cfg.getString("loginServer.default_locale")); + g_serverPort = cfg.getInt("loginServer.port", 0); + g_phpServerPort = cfg.getInt("phpServer.port", 0); + // replace 0.0.0.0 with actual server ip + + g_php_serverPath = cfg.getString("phpServer.url", ""); + replaceZeroIPWithLocalhostIP(g_php_serverPath); + g_php_serverHost = cfg.getString("phpServer.host", ""); + replaceZeroIPWithLocalhostIP(g_php_serverHost); + //g_ServerSetupType + auto serverSetupTypeString = cfg.getString("ServerSetupType", ""); + g_ServerSetupType = getServerSetupTypeFromString(serverSetupTypeString); + + // app secret for encrypt user private keys + // TODO: encrypt with server admin key + auto app_secret_string = cfg.getString("crypto.app_secret", ""); + if ("" != app_secret_string) { + g_CryptoAppSecret = DataTypeConverter::hexToBin(app_secret_string); + } + //g_CryptoAppSecret + + g_gRPCRelayServerFullURL = cfg.getString("grpc.server", ""); + + // unsecure flags + //g_AllowUnsecureFlags + if (cfg.getInt("unsecure.allow_passwort_via_json_request", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_PASSWORD_REQUESTS); + } + if (cfg.getInt("unsecure.allow_auto_sign_transactions", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_AUTO_SIGN_TRANSACTIONS); + } + if (cfg.getInt("unsecure.allow_cors_all", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_CORS_ALL); + } + if (cfg.getInt("unsecure.allow_all_passwords", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_ALLOW_ALL_PASSWORDS); + } + + return true; + } + + bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg) + { + g_disableEmail = cfg.getBool("email.disable", false); + if (g_disableEmail) { + printf("Email is disabled!\n"); + } + else { + g_EmailAccount.sender = cfg.getString("email.sender"); + g_EmailAccount.username = cfg.getString("email.username"); + g_EmailAccount.password = cfg.getString("email.password"); + g_EmailAccount.url = cfg.getString("email.smtp.url"); + g_EmailAccount.port = cfg.getInt("email.smtp.port"); + } + DISASM_FALSERET; + //g_ServerKeySeed->put(3, DRRandom::r64()); + return true; + } + + bool initSSLClientContext() + { + SharedPtr pCert = new RejectCertificateHandler(false); // reject invalid certificates + /* + Context(Usage usage, + const std::string& certificateNameOrPath, + VerificationMode verMode = VERIFY_RELAXED, + int options = OPT_DEFAULTS, + const std::string& certificateStoreName = CERT_STORE_MY); + */ + try { +#ifdef POCO_NETSSL_WIN + g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "cacert.pem", Context::VERIFY_RELAXED, Context::OPT_DEFAULTS); +#else + + g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "", "", Poco::Path::config() + "grd_login/cacert.pem", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); +#endif + } catch(Poco::Exception& ex) { + printf("[ServerConfig::initSSLClientContext] error init ssl context, maybe no cacert.pem found?\nPlease make sure you have cacert.pem (CA/root certificates) next to binary from https://curl.haxx.se/docs/caextract.html\n"); + return false; + } + DISASM_FALSERET; + SSLManager::instance().initializeClient(0, pCert, g_SSL_CLient_Context); + + g_ServerKeySeed->put(5, DRRandom::r64()); + + return true; + } + + void unload() { + if (g_ServerCryptoKey) { + delete g_ServerCryptoKey; + } + if (g_ServerKeySeed) { + delete g_ServerKeySeed; + } + if (g_CPUScheduler) { + delete g_CPUScheduler; + } + + if (g_CryptoCPUScheduler) { + delete g_CryptoCPUScheduler; + } + if (g_CryptoAppSecret) { + MemoryManager::getInstance()->releaseMemory(g_CryptoAppSecret); + g_CryptoAppSecret = nullptr; + } + } + + void writeToFile(std::istream& datas, std::string fileName) + { + static Poco::Mutex mutex; + + mutex.lock(); + + Poco::FileOutputStream file(fileName, std::ios::out | std::ios::app); + + if (!file.good()) { + printf("[ServerConfig::writeToFile] error creating file with name: %s\n", fileName.data()); + mutex.unlock(); + return; + } + + Poco::LocalDateTime now; + + std::string dateTimeStr = Poco::DateTimeFormatter::format(now, Poco::DateTimeFormat::ISO8601_FORMAT); + file << dateTimeStr << std::endl; + + for (std::string line; std::getline(datas, line); ) { + file << line << std::endl; + } + file << std::endl; + file.close(); + mutex.unlock(); + } } \ No newline at end of file diff --git a/login_server/src/cpp/ServerConfig.h b/login_server/src/cpp/ServerConfig.h index aef4ea28d..444fb524c 100644 --- a/login_server/src/cpp/ServerConfig.h +++ b/login_server/src/cpp/ServerConfig.h @@ -1,90 +1,90 @@ -#ifndef __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ -#define __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ - -#include "Crypto/mnemonic.h" -#include "Crypto/Obfus_array.h" -#include "Poco/Util/LayeredConfiguration.h" -#include "Poco/Net/Context.h" -#include "Poco/Types.h" -#include "Poco/Util/Timer.h" - -#include "tasks/CPUSheduler.h" - -#include "SingletonManager/LanguageManager.h" -#include "SingletonManager/MemoryManager.h" - -#define DISABLE_EMAIL - -namespace ServerConfig { - - enum Mnemonic_Types { - MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER, - MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES, - MNEMONIC_BIP0039_SORTED_ORDER, - MNEMONIC_MAX - }; - // depracted, moved to email manager - struct EmailAccount { - std::string sender; - std::string admin_receiver; - std::string username; - std::string password; - std::string url; - int port; - }; - - enum ServerSetupType { - SERVER_TYPE_TEST, - SERVER_TYPE_STAGING, - SERVER_TYPE_PRODUCTION - }; - - // used with bit-operators, so only use numbers with control exactly one bit (1,2,4,8,16...) - enum AllowUnsecure { - NOT_UNSECURE = 0, - UNSECURE_PASSWORD_REQUESTS = 1, - UNSECURE_AUTO_SIGN_TRANSACTIONS = 2, - UNSECURE_CORS_ALL = 4, - UNSECURE_ALLOW_ALL_PASSWORDS = 8 - }; - - - extern Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; - - extern ObfusArray* g_ServerCryptoKey; - extern ObfusArray* g_ServerKeySeed; - - //extern unsigned char g_ServerAdminPublic[]; - extern UniLib::controller::CPUSheduler* g_CPUScheduler; - extern UniLib::controller::CPUSheduler* g_CryptoCPUScheduler; - extern Poco::Net::Context::Ptr g_SSL_CLient_Context; - extern Poco::Util::Timer g_CronJobsTimer; - extern EmailAccount g_EmailAccount; - extern int g_SessionTimeout; - extern std::string g_serverPath; - extern int g_serverPort; - extern Languages g_default_locale; - extern std::string g_php_serverPath; - extern std::string g_php_serverHost; - extern int g_phpServerPort; - extern Poco::Mutex g_TimeMutex; - extern int g_FakeLoginSleepTime; - extern std::string g_versionString; - extern bool g_disableEmail; - extern ServerSetupType g_ServerSetupType; - extern std::string g_gRPCRelayServerFullURL; - extern MemoryBin* g_CryptoAppSecret; - extern AllowUnsecure g_AllowUnsecureFlags; - - bool loadMnemonicWordLists(); - bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg); - bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg); - bool initSSLClientContext(); - - - void writeToFile(std::istream& datas, std::string fileName); - - void unload(); -}; - +#ifndef __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ +#define __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ + +#include "Crypto/mnemonic.h" +#include "Crypto/Obfus_array.h" +#include "Poco/Util/LayeredConfiguration.h" +#include "Poco/Net/Context.h" +#include "Poco/Types.h" +#include "Poco/Util/Timer.h" + +#include "tasks/CPUSheduler.h" + +#include "SingletonManager/LanguageManager.h" +#include "SingletonManager/MemoryManager.h" + +#define DISABLE_EMAIL + +namespace ServerConfig { + + enum Mnemonic_Types { + MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER, + MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES, + MNEMONIC_BIP0039_SORTED_ORDER, + MNEMONIC_MAX + }; + // depracted, moved to email manager + struct EmailAccount { + std::string sender; + std::string admin_receiver; + std::string username; + std::string password; + std::string url; + int port; + }; + + enum ServerSetupType { + SERVER_TYPE_TEST, + SERVER_TYPE_STAGING, + SERVER_TYPE_PRODUCTION + }; + + // used with bit-operators, so only use numbers with control exactly one bit (1,2,4,8,16...) + enum AllowUnsecure { + NOT_UNSECURE = 0, + UNSECURE_PASSWORD_REQUESTS = 1, + UNSECURE_AUTO_SIGN_TRANSACTIONS = 2, + UNSECURE_CORS_ALL = 4, + UNSECURE_ALLOW_ALL_PASSWORDS = 8 + }; + + + extern Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; + + extern ObfusArray* g_ServerCryptoKey; + extern ObfusArray* g_ServerKeySeed; + + //extern unsigned char g_ServerAdminPublic[]; + extern UniLib::controller::CPUSheduler* g_CPUScheduler; + extern UniLib::controller::CPUSheduler* g_CryptoCPUScheduler; + extern Poco::Net::Context::Ptr g_SSL_CLient_Context; + extern Poco::Util::Timer g_CronJobsTimer; + extern EmailAccount g_EmailAccount; + extern int g_SessionTimeout; + extern std::string g_serverPath; + extern int g_serverPort; + extern Languages g_default_locale; + extern std::string g_php_serverPath; + extern std::string g_php_serverHost; + extern int g_phpServerPort; + extern Poco::Mutex g_TimeMutex; + extern int g_FakeLoginSleepTime; + extern std::string g_versionString; + extern bool g_disableEmail; + extern ServerSetupType g_ServerSetupType; + extern std::string g_gRPCRelayServerFullURL; + extern MemoryBin* g_CryptoAppSecret; + extern AllowUnsecure g_AllowUnsecureFlags; + + bool loadMnemonicWordLists(); + bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg); + bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg); + bool initSSLClientContext(); + + + void writeToFile(std::istream& datas, std::string fileName); + + void unload(); +}; + #endif //__GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ \ No newline at end of file diff --git a/login_server/src/cpp/SingletonManager/SessionManager.cpp b/login_server/src/cpp/SingletonManager/SessionManager.cpp index 743f11d19..e583c8eaf 100644 --- a/login_server/src/cpp/SingletonManager/SessionManager.cpp +++ b/login_server/src/cpp/SingletonManager/SessionManager.cpp @@ -1,626 +1,626 @@ -#include "SessionManager.h" -#include "ErrorManager.h" -#include "../ServerConfig.h" -#include "../Crypto/DRRandom.h" -#include "../controller/EmailVerificationCode.h" - -#include - - -SessionManager* SessionManager::getInstance() -{ - static SessionManager only; - return &only; -} - -SessionManager::SessionManager() - : mInitalized(false), mDeadLockedSessionCount(0) -{ - -} - -SessionManager::~SessionManager() -{ - if (mInitalized) { - deinitalize(); - } -} - - -bool SessionManager::init() -{ - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::init] exception timout mutex: %s\n", ex.displayText().data()); - return false; - } - //mWorkingMutex.lock(); - int i; - DISASM_MISALIGN; - for (i = 0; i < VALIDATE_MAX; i++) { - switch (i) { - //case VALIDATE_NAME: mValidations[i] = new Poco::RegularExpression("/^[a-zA-Z_ -]{3,}$/"); break; - case VALIDATE_NAME: mValidations[i] = new Poco::RegularExpression("^[^<>&;]{3,}$"); break; - case VALIDATE_EMAIL: mValidations[i] = new Poco::RegularExpression("^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"); break; - case VALIDATE_PASSWORD: mValidations[i] = new Poco::RegularExpression("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&+-_])[A-Za-z0-9@$!%*?&+-_]{8,}$"); break; - case VALIDATE_PASSPHRASE: mValidations[i] = new Poco::RegularExpression("^(?:[a-z]* ){23}[a-z]*\s*$"); break; - case VALIDATE_HAS_NUMBER: mValidations[i] = new Poco::RegularExpression(".*[0-9].*"); break; - case VALIDATE_HAS_SPECIAL_CHARACTER: mValidations[i] = new Poco::RegularExpression(".*[@$!%*?&+-].*"); break; - case VALIDATE_HAS_UPPERCASE_LETTER: - mValidations[i] = new Poco::RegularExpression(".*[A-Z].*"); - ServerConfig::g_ServerKeySeed->put(i, DRRandom::r64()); - break; - case VALIDATE_HAS_LOWERCASE_LETTER: mValidations[i] = new Poco::RegularExpression(".*[a-z].*"); break; - default: printf("[SessionManager::%s] unknown validation type\n", __FUNCTION__); - } - } - - - mInitalized = true; - mWorkingMutex.unlock(); - return true; -} - -void SessionManager::deinitalize() -{ - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::deinitalize] exception timout mutex: %s\n", ex.displayText().data()); - return; - } - //mWorkingMutex.lock(); - - while (mEmptyRequestStack.size()) { - mEmptyRequestStack.pop(); - } - - for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { - delete it->second; - } - mRequestSessionMap.clear(); - - for (int i = 0; i < VALIDATE_MAX; i++) { - delete mValidations[i]; - } - - printf("[SessionManager::deinitalize] count of dead locked sessions: %d\n", mDeadLockedSessionCount); - - mInitalized = false; - mWorkingMutex.unlock(); -} - -bool SessionManager::isValid(const std::string& subject, SessionValidationTypes validationType) -{ - if (validationType >= VALIDATE_MAX) { - return false; - } - return *mValidations[validationType] == subject; -} - -int SessionManager::generateNewUnusedHandle() -{ - int newHandle = 0; - int maxTrys = 0; - do { - newHandle = randombytes_random(); - maxTrys++; - } while (mRequestSessionMap.find(newHandle) != mRequestSessionMap.end() && maxTrys < 100); - - if (maxTrys >= 100 || 0 == newHandle) { - auto em = ErrorManager::getInstance(); - em->addError(new ParamError("SessionManager::generateNewUnusedHandle", "can't find new handle, have already ", std::to_string(mRequestSessionMap.size()))); - em->sendErrorsAsEmail(); - //printf("[SessionManager::%s] can't find new handle, have already: %d", - //__FUNCTION__, mRequestSessionMap.size()); - return 0; - } - return newHandle; -} - -Session* SessionManager::getNewSession(int* handle) -{ - const static char* functionName = "SessionManager::getNewSession"; - if (!mInitalized) { - printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); - return nullptr; - } - - // first check if we have any timeouted session to directly reuse it - checkTimeoutSession(); - - // lock - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[%s] exception timout mutex: %s\n", functionName, ex.displayText().data()); - return nullptr; - } - //mWorkingMutex.lock(); - - //UniLib::controller::TaskPtr checkSessionTimeout(new CheckSessionTimeouted); - //checkSessionTimeout->scheduleTask(checkSessionTimeout); - - // check if we have an existing session ready to use - while (mEmptyRequestStack.size() > 0) { - int local_handle = mEmptyRequestStack.top(); - mEmptyRequestStack.pop(); - auto resultIt = mRequestSessionMap.find(local_handle); - if (resultIt != mRequestSessionMap.end()) { - Session* result = resultIt->second; - // check if dead locked - if (result->tryLock()) { - result->unlock(); - if (!result->isActive()) { - result->reset(); - //mWorkingMutex.unlock(); - - if (handle) { - *handle = local_handle; - } - result->setActive(true); - mWorkingMutex.unlock(); - return result; - } - } - else { - ErrorList errors; - errors.addError(new Error(functionName, "found dead locked session, keeping in memory without reference")); - errors.addError(new ParamError(functionName, "last succeeded lock:", result->getLastSucceededLock().data())); - errors.sendErrorsAsEmail(); - - mRequestSessionMap.erase(local_handle); - } - - } - } - - // else create new RequestSession Object - // calculate random handle - // check if already exist, if get new - int newHandle = generateNewUnusedHandle(); - if (!newHandle) { - mWorkingMutex.unlock(); - return nullptr; - } - - auto requestSession = new Session(newHandle); - mRequestSessionMap.insert(std::pair(newHandle, requestSession)); - - requestSession->setActive(true); - //mWorkingMutex.unlock(); - - if (handle) { - *handle = newHandle; - } - //printf("[SessionManager::getNewSession] handle: %ld, sum: %u\n", newHandle, mRequestSessionMap.size()); - mWorkingMutex.unlock(); - return requestSession; - - - //return nullptr; -} - -bool SessionManager::releaseSession(int requestHandleSession) -{ - if (!mInitalized) { - printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); - return false; - } - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::releaseSession] exception timout mutex: %s\n", ex.displayText().data()); - return false; - } - //mWorkingMutex.lock(); - - auto it = mRequestSessionMap.find(requestHandleSession); - if (it == mRequestSessionMap.end()) { - //printf("[SessionManager::releaseRequestSession] requestSession with handle: %d not found\n", requestHandleSession); - mWorkingMutex.unlock(); - return false; - } - Session* session = it->second; - - // delete session, not reuse as workaround for server freeze bug - mRequestSessionMap.erase(requestHandleSession); - delete session; - mWorkingMutex.unlock(); - return true; - - // check if dead locked - if (session->tryLock()) { - session->unlock(); - session->reset(); - session->setActive(false); - } - else { - ErrorList errors; - errors.addError(new Error("SessionManager::releaseSession", "found dead locked session")); - errors.sendErrorsAsEmail(); - mRequestSessionMap.erase(requestHandleSession); - delete session; - mWorkingMutex.unlock(); - return true; - } - - // change request handle we don't want session hijacking - - // hardcoded disabled session max - if (mEmptyRequestStack.size() > 100) { - mRequestSessionMap.erase(requestHandleSession); - delete session; - mWorkingMutex.unlock(); - return true; - } - - int newHandle = generateNewUnusedHandle(); - //printf("[SessionManager::releseSession] oldHandle: %ld, newHandle: %ld\n", requestHandleSession, newHandle); - // erase after generating new number to prevent to getting the same number again - mRequestSessionMap.erase(requestHandleSession); - - if (!newHandle) { - delete session; - mWorkingMutex.unlock(); - return true; - } - - session->setHandle(newHandle); - mRequestSessionMap.insert(std::pair(newHandle, session)); - mEmptyRequestStack.push(newHandle); - - mWorkingMutex.unlock(); - return true; -} - -bool SessionManager::isExist(int requestHandleSession) -{ - static const char* function_name = "SessionManager::isExist"; - if (!mInitalized) { - printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); - return false; - } - bool result = false; - //mWorkingMutex.lock(); - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::isExist] exception timout mutex: %s\n", ex.displayText().data()); - return false; - } - auto it = mRequestSessionMap.find(requestHandleSession); - if (it != mRequestSessionMap.end()) { - result = true; - int iResult = it->second->isActive(); - if (-1 == iResult) { - auto em = ErrorManager::getInstance(); - em->addError(new Error(function_name, "session return locked")); - em->sendErrorsAsEmail(); - } - if (0 == iResult) { - printf("[SessionManager::isExist] session isn't active\n"); - } - } - mWorkingMutex.unlock(); - return result; -} - -Session* SessionManager::getSession(const Poco::Net::HTTPServerRequest& request) -{ - // check if user has valid session - Poco::Net::NameValueCollection cookies; - request.getCookies(cookies); - - int session_id = 0; - - try { - session_id = atoi(cookies.get("GRADIDO_LOGIN").data()); - return getSession(session_id); - } - catch (...) {} - - return nullptr; -} - -Session* SessionManager::getSession(int handle) -{ - static const char* function_name = "SessionManager::getSession"; - if (!mInitalized) { - printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); - return nullptr; - } - if (0 == handle) return nullptr; - Session* result = nullptr; - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::getSession] exception timout mutex: %s\n", ex.displayText().data()); - return result; - } - //mWorkingMutex.lock(); - auto it = mRequestSessionMap.find(handle); - if (it != mRequestSessionMap.end()) { - result = it->second; - int iResult = result->isActive(); - if (iResult == -1) { - auto em = ErrorManager::getInstance(); - em->addError(new Error(function_name, "session is locked")); - em->sendErrorsAsEmail(); - mWorkingMutex.unlock(); - return nullptr; - } - if (0 == iResult) { - //printf("[SessionManager::getSession] session isn't active\n"); - mWorkingMutex.unlock(); - return nullptr; - } - //result->setActive(true); - result->updateTimeout(); - } - //printf("[SessionManager::getSession] handle: %ld\n", handle); - mWorkingMutex.unlock(); - return result; -} - -Session* SessionManager::findByEmailVerificationCode(const Poco::UInt64& emailVerificationCode) -{ - - auto email_verification = controller::EmailVerificationCode::load(emailVerificationCode); - if (email_verification.isNull()) return nullptr; - auto email_verification_model = email_verification->getModel(); - assert(email_verification_model && email_verification_model->getUserId() > 0); - - auto session = findByUserId(email_verification_model->getUserId()); - if (session) { - session->setEmailVerificationCodeObject(email_verification); - } - - return session; -} - -Session* SessionManager::findByUserId(int userId) -{ - assert(userId > 0); - static const char* function_name = "SessionManager::findByUserId"; - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::findByUserId] exception timout mutex: %s\n", ex.displayText().data()); - return nullptr; - } - //mWorkingMutex.lock(); - for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { - while (it->second->isDeadLocked()) - { - it = mRequestSessionMap.erase(it); - mDeadLockedSessionCount++; - auto em = ErrorManager::getInstance(); - em->addError(new ParamError("SessionManager::findByUserId", "new dead locked session found, sum dead lock sessions:", mDeadLockedSessionCount)); - em->sendErrorsAsEmail(); - } - auto user = it->second->getNewUser(); - auto em = ErrorManager::getInstance(); - if(!user) continue; - if (!user->getModel()) { - em->addError(new Error(function_name, "user getModel return nullptr")); - em->addError(new ParamError(function_name, "user id: ", userId)); - em->sendErrorsAsEmail(); - continue; - } - if (!user->getModel()->getID()) { - em->addError(new Error(function_name, "user id is zero")); - em->addError(new ParamError(function_name, "user id: ", userId)); - em->sendErrorsAsEmail(); - continue; - } - //assert(user->getModel() && user->getModel()->getID()); - if (userId == user->getModel()->getID()) { - mWorkingMutex.unlock(); - return it->second; - } - } - mWorkingMutex.unlock(); - return nullptr; -} - -std::vector SessionManager::findAllByUserId(int userId) -{ - assert(userId > 0); - std::vector result; - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::findAllByUserId] exception timout mutex: %s\n", ex.displayText().data()); - //mWorkingMutex.unlock(); - return result; - } - //mWorkingMutex.lock(); - for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { - if (it->second->isDeadLocked()) { - it = mRequestSessionMap.erase(it); - mDeadLockedSessionCount++; - } - auto user = it->second->getNewUser(); - if (userId == user->getModel()->getID()) { - //return it->second; - result.push_back(it->second); - } - } - //mWorkingMutex.unlock(); - mWorkingMutex.unlock(); - return result; -} - -Session* SessionManager::findByEmail(const std::string& email) -{ - assert(email.size() > 0); - - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException &ex) { - printf("[SessionManager::findByEmail] exception timout mutex: %s\n", ex.displayText().data()); - //mWorkingMutex.unlock(); - return nullptr; - } - //mWorkingMutex.lock(); - for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { - if (it->second->isDeadLocked()) { - it = mRequestSessionMap.erase(it); - mDeadLockedSessionCount++; - } - auto user = it->second->getNewUser(); -if (email == user->getModel()->getEmail()) { - return it->second; -} - } - mWorkingMutex.unlock(); - return nullptr; -} - -void SessionManager::checkTimeoutSession() -{ - - try { - //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); - mWorkingMutex.tryLock(500); - } - catch (Poco::TimeoutException& ex) { - printf("[SessionManager::checkTimeoutSession] exception timeout mutex: %s\n", ex.displayText().data()); - return; - } - //mWorkingMutex.lock(); - auto now = Poco::DateTime(); - // random variance within 10 seconds for timeout to make it harder to get information and hack the server - auto timeout = Poco::Timespan(ServerConfig::g_SessionTimeout * 60, randombytes_random() % 10000000); - //auto timeout = Poco::Timespan(1, 0); - std::stack toRemove; - for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { - - if (it->second->tryLock()) { - // skip already disabled sessions - if (!it->second->isActive()) { - it->second->unlock(); - continue; - } - } - else { - // skip dead locked sessions - continue; - } - - Poco::Timespan timeElapsed(now - it->second->getLastActivity()); - it->second->unlock(); - if (timeElapsed > timeout) { - toRemove.push(it->first); - } - } - mWorkingMutex.unlock(); - - while (toRemove.size() > 0) { - int handle = toRemove.top(); - toRemove.pop(); - releaseSession(handle); - } - -} - -void SessionManager::deleteLoginCookies(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response, Session* activeSession/* = nullptr*/) -{ - Poco::Net::NameValueCollection cookies; - request.getCookies(cookies); - // go from first login cookie - for (auto it = cookies.find("GRADIDO_LOGIN"); it != cookies.end(); it++) { - // break if no login any more - if (it->first != "GRADIDO_LOGIN") break; - // skip if it is from the active session - if (activeSession) { - try { - int session_id = atoi(it->second.data()); - if (activeSession->tryLock()) { - bool session_id_is_handle = session_id == activeSession->getHandle(); - activeSession->unlock(); - if (session_id_is_handle) continue; - } - } - catch (...) {} - } - // delete cookie - auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", it->second); - keks.setPath("/"); - // max age of 0 delete cookie - keks.setMaxAge(0); - response.addCookie(keks); - } - // delete also cake php session cookie - for (auto it = cookies.find("CAKEPHP"); it != cookies.end(); it++) { - if (it->first != "CAKEPHP") break; - // delete cookie - auto keks = Poco::Net::HTTPCookie("CAKEPHP", it->second); - keks.setPath("/"); - // max age of 0 delete cookie - keks.setMaxAge(0); - response.addCookie(keks); - //printf("remove PHP Kekse\n"); - } - - - //session_id = atoi(cookies.get("GRADIDO_LOGIN").data()); -} - -bool SessionManager::checkPwdValidation(const std::string& pwd, ErrorList* errorReciver) -{ - if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_ALLOW_ALL_PASSWORDS) == ServerConfig::UNSECURE_ALLOW_ALL_PASSWORDS) { - return true; - } - - if (!isValid(pwd, VALIDATE_PASSWORD)) { - errorReciver->addError(new Error("Passwort", "Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) ein!")); - - // @$!%*?&+- - if (pwd.size() < 8) { - errorReciver->addError(new Error("Passwort", "Dein Passwort ist zu kurz!")); - } - else if (!isValid(pwd, VALIDATE_HAS_LOWERCASE_LETTER)) { - errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Kleinbuchstaben!")); - } - else if (!isValid(pwd, VALIDATE_HAS_UPPERCASE_LETTER)) { - errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Großbuchstaben!")); - } - else if (!isValid(pwd, VALIDATE_HAS_NUMBER)) { - errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Zahlen!")); - } - else if (!isValid(pwd, VALIDATE_HAS_SPECIAL_CHARACTER)) { - errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Sonderzeichen (@$!%*?&+-)!")); - } - - return false; - } - return true; -} - - -int CheckSessionTimeouted::run() -{ - SessionManager::getInstance()->checkTimeoutSession(); - return 0; -} +#include "SessionManager.h" +#include "ErrorManager.h" +#include "../ServerConfig.h" +#include "../Crypto/DRRandom.h" +#include "../controller/EmailVerificationCode.h" + +#include + + +SessionManager* SessionManager::getInstance() +{ + static SessionManager only; + return &only; +} + +SessionManager::SessionManager() + : mInitalized(false), mDeadLockedSessionCount(0) +{ + +} + +SessionManager::~SessionManager() +{ + if (mInitalized) { + deinitalize(); + } +} + + +bool SessionManager::init() +{ + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::init] exception timout mutex: %s\n", ex.displayText().data()); + return false; + } + //mWorkingMutex.lock(); + int i; + DISASM_MISALIGN; + for (i = 0; i < VALIDATE_MAX; i++) { + switch (i) { + //case VALIDATE_NAME: mValidations[i] = new Poco::RegularExpression("/^[a-zA-Z_ -]{3,}$/"); break; + case VALIDATE_NAME: mValidations[i] = new Poco::RegularExpression("^[^<>&;]{3,}$"); break; + case VALIDATE_EMAIL: mValidations[i] = new Poco::RegularExpression("^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"); break; + case VALIDATE_PASSWORD: mValidations[i] = new Poco::RegularExpression("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&+-_])[A-Za-z0-9@$!%*?&+-_]{8,}$"); break; + case VALIDATE_PASSPHRASE: mValidations[i] = new Poco::RegularExpression("^(?:[a-z]* ){23}[a-z]*\s*$"); break; + case VALIDATE_HAS_NUMBER: mValidations[i] = new Poco::RegularExpression(".*[0-9].*"); break; + case VALIDATE_HAS_SPECIAL_CHARACTER: mValidations[i] = new Poco::RegularExpression(".*[@$!%*?&+-].*"); break; + case VALIDATE_HAS_UPPERCASE_LETTER: + mValidations[i] = new Poco::RegularExpression(".*[A-Z].*"); + ServerConfig::g_ServerKeySeed->put(i, DRRandom::r64()); + break; + case VALIDATE_HAS_LOWERCASE_LETTER: mValidations[i] = new Poco::RegularExpression(".*[a-z].*"); break; + default: printf("[SessionManager::%s] unknown validation type\n", __FUNCTION__); + } + } + + + mInitalized = true; + mWorkingMutex.unlock(); + return true; +} + +void SessionManager::deinitalize() +{ + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::deinitalize] exception timout mutex: %s\n", ex.displayText().data()); + return; + } + //mWorkingMutex.lock(); + + while (mEmptyRequestStack.size()) { + mEmptyRequestStack.pop(); + } + + for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { + delete it->second; + } + mRequestSessionMap.clear(); + + for (int i = 0; i < VALIDATE_MAX; i++) { + delete mValidations[i]; + } + + printf("[SessionManager::deinitalize] count of dead locked sessions: %d\n", mDeadLockedSessionCount); + + mInitalized = false; + mWorkingMutex.unlock(); +} + +bool SessionManager::isValid(const std::string& subject, SessionValidationTypes validationType) +{ + if (validationType >= VALIDATE_MAX) { + return false; + } + return *mValidations[validationType] == subject; +} + +int SessionManager::generateNewUnusedHandle() +{ + int newHandle = 0; + int maxTrys = 0; + do { + newHandle = randombytes_random(); + maxTrys++; + } while (mRequestSessionMap.find(newHandle) != mRequestSessionMap.end() && maxTrys < 100); + + if (maxTrys >= 100 || 0 == newHandle) { + auto em = ErrorManager::getInstance(); + em->addError(new ParamError("SessionManager::generateNewUnusedHandle", "can't find new handle, have already ", std::to_string(mRequestSessionMap.size()))); + em->sendErrorsAsEmail(); + //printf("[SessionManager::%s] can't find new handle, have already: %d", + //__FUNCTION__, mRequestSessionMap.size()); + return 0; + } + return newHandle; +} + +Session* SessionManager::getNewSession(int* handle) +{ + const static char* functionName = "SessionManager::getNewSession"; + if (!mInitalized) { + printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); + return nullptr; + } + + // first check if we have any timeouted session to directly reuse it + checkTimeoutSession(); + + // lock + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[%s] exception timout mutex: %s\n", functionName, ex.displayText().data()); + return nullptr; + } + //mWorkingMutex.lock(); + + //UniLib::controller::TaskPtr checkSessionTimeout(new CheckSessionTimeouted); + //checkSessionTimeout->scheduleTask(checkSessionTimeout); + + // check if we have an existing session ready to use + while (mEmptyRequestStack.size() > 0) { + int local_handle = mEmptyRequestStack.top(); + mEmptyRequestStack.pop(); + auto resultIt = mRequestSessionMap.find(local_handle); + if (resultIt != mRequestSessionMap.end()) { + Session* result = resultIt->second; + // check if dead locked + if (result->tryLock()) { + result->unlock(); + if (!result->isActive()) { + result->reset(); + //mWorkingMutex.unlock(); + + if (handle) { + *handle = local_handle; + } + result->setActive(true); + mWorkingMutex.unlock(); + return result; + } + } + else { + ErrorList errors; + errors.addError(new Error(functionName, "found dead locked session, keeping in memory without reference")); + errors.addError(new ParamError(functionName, "last succeeded lock:", result->getLastSucceededLock().data())); + errors.sendErrorsAsEmail(); + + mRequestSessionMap.erase(local_handle); + } + + } + } + + // else create new RequestSession Object + // calculate random handle + // check if already exist, if get new + int newHandle = generateNewUnusedHandle(); + if (!newHandle) { + mWorkingMutex.unlock(); + return nullptr; + } + + auto requestSession = new Session(newHandle); + mRequestSessionMap.insert(std::pair(newHandle, requestSession)); + + requestSession->setActive(true); + //mWorkingMutex.unlock(); + + if (handle) { + *handle = newHandle; + } + //printf("[SessionManager::getNewSession] handle: %ld, sum: %u\n", newHandle, mRequestSessionMap.size()); + mWorkingMutex.unlock(); + return requestSession; + + + //return nullptr; +} + +bool SessionManager::releaseSession(int requestHandleSession) +{ + if (!mInitalized) { + printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); + return false; + } + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::releaseSession] exception timout mutex: %s\n", ex.displayText().data()); + return false; + } + //mWorkingMutex.lock(); + + auto it = mRequestSessionMap.find(requestHandleSession); + if (it == mRequestSessionMap.end()) { + //printf("[SessionManager::releaseRequestSession] requestSession with handle: %d not found\n", requestHandleSession); + mWorkingMutex.unlock(); + return false; + } + Session* session = it->second; + + // delete session, not reuse as workaround for server freeze bug + mRequestSessionMap.erase(requestHandleSession); + delete session; + mWorkingMutex.unlock(); + return true; + + // check if dead locked + if (session->tryLock()) { + session->unlock(); + session->reset(); + session->setActive(false); + } + else { + ErrorList errors; + errors.addError(new Error("SessionManager::releaseSession", "found dead locked session")); + errors.sendErrorsAsEmail(); + mRequestSessionMap.erase(requestHandleSession); + delete session; + mWorkingMutex.unlock(); + return true; + } + + // change request handle we don't want session hijacking + + // hardcoded disabled session max + if (mEmptyRequestStack.size() > 100) { + mRequestSessionMap.erase(requestHandleSession); + delete session; + mWorkingMutex.unlock(); + return true; + } + + int newHandle = generateNewUnusedHandle(); + //printf("[SessionManager::releseSession] oldHandle: %ld, newHandle: %ld\n", requestHandleSession, newHandle); + // erase after generating new number to prevent to getting the same number again + mRequestSessionMap.erase(requestHandleSession); + + if (!newHandle) { + delete session; + mWorkingMutex.unlock(); + return true; + } + + session->setHandle(newHandle); + mRequestSessionMap.insert(std::pair(newHandle, session)); + mEmptyRequestStack.push(newHandle); + + mWorkingMutex.unlock(); + return true; +} + +bool SessionManager::isExist(int requestHandleSession) +{ + static const char* function_name = "SessionManager::isExist"; + if (!mInitalized) { + printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); + return false; + } + bool result = false; + //mWorkingMutex.lock(); + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::isExist] exception timout mutex: %s\n", ex.displayText().data()); + return false; + } + auto it = mRequestSessionMap.find(requestHandleSession); + if (it != mRequestSessionMap.end()) { + result = true; + int iResult = it->second->isActive(); + if (-1 == iResult) { + auto em = ErrorManager::getInstance(); + em->addError(new Error(function_name, "session return locked")); + em->sendErrorsAsEmail(); + } + if (0 == iResult) { + printf("[SessionManager::isExist] session isn't active\n"); + } + } + mWorkingMutex.unlock(); + return result; +} + +Session* SessionManager::getSession(const Poco::Net::HTTPServerRequest& request) +{ + // check if user has valid session + Poco::Net::NameValueCollection cookies; + request.getCookies(cookies); + + int session_id = 0; + + try { + session_id = atoi(cookies.get("GRADIDO_LOGIN").data()); + return getSession(session_id); + } + catch (...) {} + + return nullptr; +} + +Session* SessionManager::getSession(int handle) +{ + static const char* function_name = "SessionManager::getSession"; + if (!mInitalized) { + printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); + return nullptr; + } + if (0 == handle) return nullptr; + Session* result = nullptr; + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::getSession] exception timout mutex: %s\n", ex.displayText().data()); + return result; + } + //mWorkingMutex.lock(); + auto it = mRequestSessionMap.find(handle); + if (it != mRequestSessionMap.end()) { + result = it->second; + int iResult = result->isActive(); + if (iResult == -1) { + auto em = ErrorManager::getInstance(); + em->addError(new Error(function_name, "session is locked")); + em->sendErrorsAsEmail(); + mWorkingMutex.unlock(); + return nullptr; + } + if (0 == iResult) { + //printf("[SessionManager::getSession] session isn't active\n"); + mWorkingMutex.unlock(); + return nullptr; + } + //result->setActive(true); + result->updateTimeout(); + } + //printf("[SessionManager::getSession] handle: %ld\n", handle); + mWorkingMutex.unlock(); + return result; +} + +Session* SessionManager::findByEmailVerificationCode(const Poco::UInt64& emailVerificationCode) +{ + + auto email_verification = controller::EmailVerificationCode::load(emailVerificationCode); + if (email_verification.isNull()) return nullptr; + auto email_verification_model = email_verification->getModel(); + assert(email_verification_model && email_verification_model->getUserId() > 0); + + auto session = findByUserId(email_verification_model->getUserId()); + if (session) { + session->setEmailVerificationCodeObject(email_verification); + } + + return session; +} + +Session* SessionManager::findByUserId(int userId) +{ + assert(userId > 0); + static const char* function_name = "SessionManager::findByUserId"; + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::findByUserId] exception timout mutex: %s\n", ex.displayText().data()); + return nullptr; + } + //mWorkingMutex.lock(); + for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { + while (it->second->isDeadLocked()) + { + it = mRequestSessionMap.erase(it); + mDeadLockedSessionCount++; + auto em = ErrorManager::getInstance(); + em->addError(new ParamError("SessionManager::findByUserId", "new dead locked session found, sum dead lock sessions:", mDeadLockedSessionCount)); + em->sendErrorsAsEmail(); + } + auto user = it->second->getNewUser(); + auto em = ErrorManager::getInstance(); + if(!user) continue; + if (!user->getModel()) { + em->addError(new Error(function_name, "user getModel return nullptr")); + em->addError(new ParamError(function_name, "user id: ", userId)); + em->sendErrorsAsEmail(); + continue; + } + if (!user->getModel()->getID()) { + em->addError(new Error(function_name, "user id is zero")); + em->addError(new ParamError(function_name, "user id: ", userId)); + em->sendErrorsAsEmail(); + continue; + } + //assert(user->getModel() && user->getModel()->getID()); + if (userId == user->getModel()->getID()) { + mWorkingMutex.unlock(); + return it->second; + } + } + mWorkingMutex.unlock(); + return nullptr; +} + +std::vector SessionManager::findAllByUserId(int userId) +{ + assert(userId > 0); + std::vector result; + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::findAllByUserId] exception timout mutex: %s\n", ex.displayText().data()); + //mWorkingMutex.unlock(); + return result; + } + //mWorkingMutex.lock(); + for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { + if (it->second->isDeadLocked()) { + it = mRequestSessionMap.erase(it); + mDeadLockedSessionCount++; + } + auto user = it->second->getNewUser(); + if (userId == user->getModel()->getID()) { + //return it->second; + result.push_back(it->second); + } + } + //mWorkingMutex.unlock(); + mWorkingMutex.unlock(); + return result; +} + +Session* SessionManager::findByEmail(const std::string& email) +{ + assert(email.size() > 0); + + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException &ex) { + printf("[SessionManager::findByEmail] exception timout mutex: %s\n", ex.displayText().data()); + //mWorkingMutex.unlock(); + return nullptr; + } + //mWorkingMutex.lock(); + for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { + if (it->second->isDeadLocked()) { + it = mRequestSessionMap.erase(it); + mDeadLockedSessionCount++; + } + auto user = it->second->getNewUser(); +if (email == user->getModel()->getEmail()) { + return it->second; +} + } + mWorkingMutex.unlock(); + return nullptr; +} + +void SessionManager::checkTimeoutSession() +{ + + try { + //Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); + mWorkingMutex.tryLock(500); + } + catch (Poco::TimeoutException& ex) { + printf("[SessionManager::checkTimeoutSession] exception timeout mutex: %s\n", ex.displayText().data()); + return; + } + //mWorkingMutex.lock(); + auto now = Poco::DateTime(); + // random variance within 10 seconds for timeout to make it harder to get information and hack the server + auto timeout = Poco::Timespan(ServerConfig::g_SessionTimeout * 60, randombytes_random() % 10000000); + //auto timeout = Poco::Timespan(1, 0); + std::stack toRemove; + for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { + + if (it->second->tryLock()) { + // skip already disabled sessions + if (!it->second->isActive()) { + it->second->unlock(); + continue; + } + } + else { + // skip dead locked sessions + continue; + } + + Poco::Timespan timeElapsed(now - it->second->getLastActivity()); + it->second->unlock(); + if (timeElapsed > timeout) { + toRemove.push(it->first); + } + } + mWorkingMutex.unlock(); + + while (toRemove.size() > 0) { + int handle = toRemove.top(); + toRemove.pop(); + releaseSession(handle); + } + +} + +void SessionManager::deleteLoginCookies(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response, Session* activeSession/* = nullptr*/) +{ + Poco::Net::NameValueCollection cookies; + request.getCookies(cookies); + // go from first login cookie + for (auto it = cookies.find("GRADIDO_LOGIN"); it != cookies.end(); it++) { + // break if no login any more + if (it->first != "GRADIDO_LOGIN") break; + // skip if it is from the active session + if (activeSession) { + try { + int session_id = atoi(it->second.data()); + if (activeSession->tryLock()) { + bool session_id_is_handle = session_id == activeSession->getHandle(); + activeSession->unlock(); + if (session_id_is_handle) continue; + } + } + catch (...) {} + } + // delete cookie + auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", it->second); + keks.setPath("/"); + // max age of 0 delete cookie + keks.setMaxAge(0); + response.addCookie(keks); + } + // delete also cake php session cookie + for (auto it = cookies.find("CAKEPHP"); it != cookies.end(); it++) { + if (it->first != "CAKEPHP") break; + // delete cookie + auto keks = Poco::Net::HTTPCookie("CAKEPHP", it->second); + keks.setPath("/"); + // max age of 0 delete cookie + keks.setMaxAge(0); + response.addCookie(keks); + //printf("remove PHP Kekse\n"); + } + + + //session_id = atoi(cookies.get("GRADIDO_LOGIN").data()); +} + +bool SessionManager::checkPwdValidation(const std::string& pwd, ErrorList* errorReciver) +{ + if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_ALLOW_ALL_PASSWORDS) == ServerConfig::UNSECURE_ALLOW_ALL_PASSWORDS) { + return true; + } + + if (!isValid(pwd, VALIDATE_PASSWORD)) { + errorReciver->addError(new Error("Passwort", "Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) ein!")); + + // @$!%*?&+- + if (pwd.size() < 8) { + errorReciver->addError(new Error("Passwort", "Dein Passwort ist zu kurz!")); + } + else if (!isValid(pwd, VALIDATE_HAS_LOWERCASE_LETTER)) { + errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Kleinbuchstaben!")); + } + else if (!isValid(pwd, VALIDATE_HAS_UPPERCASE_LETTER)) { + errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Großbuchstaben!")); + } + else if (!isValid(pwd, VALIDATE_HAS_NUMBER)) { + errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Zahlen!")); + } + else if (!isValid(pwd, VALIDATE_HAS_SPECIAL_CHARACTER)) { + errorReciver->addError(new Error("Passwort", "Dein Passwort enthält keine Sonderzeichen (@$!%*?&+-)!")); + } + + return false; + } + return true; +} + + +int CheckSessionTimeouted::run() +{ + SessionManager::getInstance()->checkTimeoutSession(); + return 0; +} diff --git a/login_server/src/cpp/lib/Error.h b/login_server/src/cpp/lib/Error.h index 2a4d79e85..7988a9345 100644 --- a/login_server/src/cpp/lib/Error.h +++ b/login_server/src/cpp/lib/Error.h @@ -1,63 +1,63 @@ -/*! -* -* \author: einhornimmond -* -* \date: 07.03.19 -* -* \brief: error data -*/ - -#ifndef DR_LUA_WEB_MODULE_ERROR_ERROR_H -#define DR_LUA_WEB_MODULE_ERROR_ERROR_H - -#include -#include - -class Error -{ -public: - Error(const char* functionName, const char* message); - ~Error(); - - const char* getFunctionName() { return mFunctionName.data(); } - const char* getMessage() { return mMessage.data(); } - virtual std::string getString(bool withNewline = true); - virtual std::string getHtmlString(); - - - -protected: - std::string mFunctionName; - std::string mMessage; -}; - -class ParamError : public Error -{ -public: - ParamError(const char* functionName, const char* message, const char* param) - : Error(functionName, message), mParam(param) {} - ParamError(const char* functionName, const char* message, const std::string& param) - : Error(functionName, message), mParam(param) {} - - ParamError(const char* functioName, const char* message, int param) - : Error(functioName, message) { - std::stringstream ss; - ss << param; - mParam = ss.str(); - } - - virtual std::string getString(bool withNewline = true); - virtual std::string getHtmlString(); -protected: - std::string mParam; -}; - - - -class IErrorCollection -{ -public: - virtual void addError(Error*, bool log = true) = 0; -}; - -#endif // DR_LUA_WEB_MODULE_ERROR_ERROR_H +/*! +* +* \author: einhornimmond +* +* \date: 07.03.19 +* +* \brief: error data +*/ + +#ifndef DR_LUA_WEB_MODULE_ERROR_ERROR_H +#define DR_LUA_WEB_MODULE_ERROR_ERROR_H + +#include +#include + +class Error +{ +public: + Error(const char* functionName, const char* message); + ~Error(); + + const char* getFunctionName() { return mFunctionName.data(); } + const char* getMessage() { return mMessage.data(); } + virtual std::string getString(bool withNewline = true); + virtual std::string getHtmlString(); + + + +protected: + std::string mFunctionName; + std::string mMessage; +}; + +class ParamError : public Error +{ +public: + ParamError(const char* functionName, const char* message, const char* param) + : Error(functionName, message), mParam(param) {} + ParamError(const char* functionName, const char* message, const std::string& param) + : Error(functionName, message), mParam(param) {} + + ParamError(const char* functioName, const char* message, int param) + : Error(functioName, message) { + std::stringstream ss; + ss << param; + mParam = ss.str(); + } + + virtual std::string getString(bool withNewline = true); + virtual std::string getHtmlString(); +protected: + std::string mParam; +}; + + + +class IErrorCollection +{ +public: + virtual void addError(Error*, bool log = true) = 0; +}; + +#endif // DR_LUA_WEB_MODULE_ERROR_ERROR_H diff --git a/login_server/src/cpp/lib/ErrorList.cpp b/login_server/src/cpp/lib/ErrorList.cpp index a663b67b2..32c408acb 100644 --- a/login_server/src/cpp/lib/ErrorList.cpp +++ b/login_server/src/cpp/lib/ErrorList.cpp @@ -1,201 +1,201 @@ -#include "ErrorList.h" - -#include "../ServerConfig.h" - -//#include "Poco/Net/MailMessage.h" -#include "Poco/Net/MediaType.h" - -#include "../SingletonManager/EmailManager.h" - -SendErrorMessage::~SendErrorMessage() -{ - if (mMessage) { - delete mMessage; - mMessage = nullptr; - } -} - -int SendErrorMessage::run() -{ - if (ServerConfig::g_disableEmail) return 0; - - auto mailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port); - mailClientSession->login(); - mailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context); - - - mailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password); - - try { - mMessage->setSender(ServerConfig::g_EmailAccount.sender); - mailClientSession->sendMessage(*mMessage); - mailClientSession->close(); - } - catch (Poco::Exception& exc) { - printf("[SendErrorMessage::%s] error sending error message to admin: %s\n", - __FUNCTION__, exc.displayText().data()); - return -1; - } - return 0; -} - -// ------------------------------------------------------------------------------------ - - -ErrorList::ErrorList() - : mLogging(Poco::Logger::get("errorLog")) -{ - -} - -ErrorList::~ErrorList() -{ - while (mErrorStack.size() > 0) { - delete mErrorStack.top(); - mErrorStack.pop(); - } -} - -void ErrorList::addError(Error* error, bool log/* = true */) -{ - - if (log) { - std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S"); - mLogging.error("%s [ErrorList::addError] %s", dateTimeString, error->getString(false)); - - } - mErrorStack.push(error); -} - -Error* ErrorList::getLastError() -{ - if (mErrorStack.size() == 0) { - return nullptr; - } - - Error* error = mErrorStack.top(); - if (error) { - mErrorStack.pop(); - } - - return error; -} - -void ErrorList::clearErrors() -{ - while (mErrorStack.size()) { - auto error = mErrorStack.top(); - if (error) { - delete error; - } - mErrorStack.pop(); - } -} - - -int ErrorList::getErrors(ErrorList* send) -{ - Error* error = nullptr; - int iCount = 0; - while (error = send->getLastError()) { - addError(error, false); - iCount++; - } - return iCount; -} - -void ErrorList::printErrors() -{ - while (mErrorStack.size() > 0) { - auto error = mErrorStack.top(); - mErrorStack.pop(); - printf(error->getString().data()); - delete error; - } -} - -std::vector ErrorList::getErrorsArray() -{ - std::vector result; - result.reserve(mErrorStack.size()); - - while (mErrorStack.size() > 0) { - auto error = mErrorStack.top(); - mErrorStack.pop(); - //result->add(error->getString()); - result.push_back(error->getString()); - delete error; - } - return result; -} - -std::string ErrorList::getErrorsHtml() -{ - std::string res; - res = "
    "; - while (mErrorStack.size() > 0) { - auto error = mErrorStack.top(); - mErrorStack.pop(); - res += "
  • "; - res += error->getHtmlString(); - res += "
  • "; - delete error; - } - res += "
"; - return res; -} - -std::string ErrorList::getErrorsHtmlNewFormat() -{ - std::string html; - - while (mErrorStack.size() > 0) { - auto error = std::unique_ptr(mErrorStack.top()); - mErrorStack.pop(); - html += "
"; - html += "report_problem"; - html += ""; - html += error->getHtmlString(); - html += ""; - html += "
"; - } - return html; -} -/* - -*/ - - -void ErrorList::sendErrorsAsEmail(std::string rawHtml/* = ""*/) -{ - auto em = EmailManager::getInstance(); - /*auto message = new Poco::Net::MailMessage(); - message->setSender("gradido_loginServer@gradido.net"); - message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, "***REMOVED***")); - message->setSubject("Error from Gradido Login Server"); - */ - std::string content; - while (mErrorStack.size() > 0) { - auto error = mErrorStack.top(); - mErrorStack.pop(); - content += error->getString(); - delete error; - } - auto email = new model::Email(content, model::EMAIL_ERROR); - - //message->addContent(new Poco::Net::StringPartSource(content)); - if (rawHtml != "") { - Poco::Net::MediaType mt("text", "html"); - mt.setParameter("charset", "utf-8"); - - email->addContent(new Poco::Net::StringPartSource(rawHtml, mt.toString())); - } - em->addEmail(email); - - //UniLib::controller::TaskPtr sendErrorMessageTask(new SendErrorMessage(message, ServerConfig::g_CPUScheduler)); - //sendErrorMessageTask->scheduleTask(sendErrorMessageTask); - +#include "ErrorList.h" + +#include "../ServerConfig.h" + +//#include "Poco/Net/MailMessage.h" +#include "Poco/Net/MediaType.h" + +#include "../SingletonManager/EmailManager.h" + +SendErrorMessage::~SendErrorMessage() +{ + if (mMessage) { + delete mMessage; + mMessage = nullptr; + } +} + +int SendErrorMessage::run() +{ + if (ServerConfig::g_disableEmail) return 0; + + auto mailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port); + mailClientSession->login(); + mailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context); + + + mailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password); + + try { + mMessage->setSender(ServerConfig::g_EmailAccount.sender); + mailClientSession->sendMessage(*mMessage); + mailClientSession->close(); + } + catch (Poco::Exception& exc) { + printf("[SendErrorMessage::%s] error sending error message to admin: %s\n", + __FUNCTION__, exc.displayText().data()); + return -1; + } + return 0; +} + +// ------------------------------------------------------------------------------------ + + +ErrorList::ErrorList() + : mLogging(Poco::Logger::get("errorLog")) +{ + +} + +ErrorList::~ErrorList() +{ + while (mErrorStack.size() > 0) { + delete mErrorStack.top(); + mErrorStack.pop(); + } +} + +void ErrorList::addError(Error* error, bool log/* = true */) +{ + + if (log) { + std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S"); + mLogging.error("%s [ErrorList::addError] %s", dateTimeString, error->getString(false)); + + } + mErrorStack.push(error); +} + +Error* ErrorList::getLastError() +{ + if (mErrorStack.size() == 0) { + return nullptr; + } + + Error* error = mErrorStack.top(); + if (error) { + mErrorStack.pop(); + } + + return error; +} + +void ErrorList::clearErrors() +{ + while (mErrorStack.size()) { + auto error = mErrorStack.top(); + if (error) { + delete error; + } + mErrorStack.pop(); + } +} + + +int ErrorList::getErrors(ErrorList* send) +{ + Error* error = nullptr; + int iCount = 0; + while (error = send->getLastError()) { + addError(error, false); + iCount++; + } + return iCount; +} + +void ErrorList::printErrors() +{ + while (mErrorStack.size() > 0) { + auto error = mErrorStack.top(); + mErrorStack.pop(); + printf(error->getString().data()); + delete error; + } +} + +std::vector ErrorList::getErrorsArray() +{ + std::vector result; + result.reserve(mErrorStack.size()); + + while (mErrorStack.size() > 0) { + auto error = mErrorStack.top(); + mErrorStack.pop(); + //result->add(error->getString()); + result.push_back(error->getString()); + delete error; + } + return result; +} + +std::string ErrorList::getErrorsHtml() +{ + std::string res; + res = "
    "; + while (mErrorStack.size() > 0) { + auto error = mErrorStack.top(); + mErrorStack.pop(); + res += "
  • "; + res += error->getHtmlString(); + res += "
  • "; + delete error; + } + res += "
"; + return res; +} + +std::string ErrorList::getErrorsHtmlNewFormat() +{ + std::string html; + + while (mErrorStack.size() > 0) { + auto error = std::unique_ptr(mErrorStack.top()); + mErrorStack.pop(); + html += "
"; + html += "report_problem"; + html += ""; + html += error->getHtmlString(); + html += ""; + html += "
"; + } + return html; +} +/* + +*/ + + +void ErrorList::sendErrorsAsEmail(std::string rawHtml/* = ""*/) +{ + auto em = EmailManager::getInstance(); + /*auto message = new Poco::Net::MailMessage(); + message->setSender("gradido_loginServer@gradido.net"); + message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, "***REMOVED***")); + message->setSubject("Error from Gradido Login Server"); + */ + std::string content; + while (mErrorStack.size() > 0) { + auto error = mErrorStack.top(); + mErrorStack.pop(); + content += error->getString(); + delete error; + } + auto email = new model::Email(content, model::EMAIL_ERROR); + + //message->addContent(new Poco::Net::StringPartSource(content)); + if (rawHtml != "") { + Poco::Net::MediaType mt("text", "html"); + mt.setParameter("charset", "utf-8"); + + email->addContent(new Poco::Net::StringPartSource(rawHtml, mt.toString())); + } + em->addEmail(email); + + //UniLib::controller::TaskPtr sendErrorMessageTask(new SendErrorMessage(message, ServerConfig::g_CPUScheduler)); + //sendErrorMessageTask->scheduleTask(sendErrorMessageTask); + } \ No newline at end of file diff --git a/login_server/src/cpp/lib/ErrorList.h b/login_server/src/cpp/lib/ErrorList.h index d2913a241..1e937ebfe 100644 --- a/login_server/src/cpp/lib/ErrorList.h +++ b/login_server/src/cpp/lib/ErrorList.h @@ -1,76 +1,76 @@ -/*! -* -* \author: einhornimmond -* -* \date: 07.03.19 -* -* \brief: error -*/ - -#ifndef DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H -#define DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H - -#include "Error.h" -#include - -#include "../tasks/CPUTask.h" - -#include "Poco/Net/SecureSMTPClientSession.h" -#include "Poco/Net/StringPartSource.h" -#include "Poco/Logger.h" -#include "Poco/JSON/Array.h" - -class ErrorList : public IErrorCollection -{ -public: - ErrorList(); - ~ErrorList(); - - // push error, error will be deleted in deconstructor - virtual void addError(Error* error, bool log = true); - - // return error on top of stack, please delete after using - Error* getLastError(); - - inline size_t errorCount() { return mErrorStack.size(); } - - // delete all errors - void clearErrors(); - - static int moveErrors(ErrorList* recv, ErrorList* send) { - return recv->getErrors(send); - } - int getErrors(ErrorList* send); - - void printErrors(); - std::string getErrorsHtml(); - std::string getErrorsHtmlNewFormat(); - - std::vector getErrorsArray(); - - void sendErrorsAsEmail(std::string rawHtml = ""); - -protected: - std::stack mErrorStack; - // poco logging - Poco::Logger& mLogging; -}; - -class SendErrorMessage : public UniLib::controller::CPUTask -{ -public: - SendErrorMessage(Poco::Net::MailMessage* message, UniLib::controller::CPUSheduler* scheduler) - : UniLib::controller::CPUTask(scheduler), mMessage(message) {} - - ~SendErrorMessage(); - - virtual int run(); - const char* getResourceType() const { return "SendErrorMessage"; }; - - -protected: - Poco::Net::MailMessage* mMessage; - -}; - -#endif // DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H +/*! +* +* \author: einhornimmond +* +* \date: 07.03.19 +* +* \brief: error +*/ + +#ifndef DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H +#define DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H + +#include "Error.h" +#include + +#include "../tasks/CPUTask.h" + +#include "Poco/Net/SecureSMTPClientSession.h" +#include "Poco/Net/StringPartSource.h" +#include "Poco/Logger.h" +#include "Poco/JSON/Array.h" + +class ErrorList : public IErrorCollection +{ +public: + ErrorList(); + ~ErrorList(); + + // push error, error will be deleted in deconstructor + virtual void addError(Error* error, bool log = true); + + // return error on top of stack, please delete after using + Error* getLastError(); + + inline size_t errorCount() { return mErrorStack.size(); } + + // delete all errors + void clearErrors(); + + static int moveErrors(ErrorList* recv, ErrorList* send) { + return recv->getErrors(send); + } + int getErrors(ErrorList* send); + + void printErrors(); + std::string getErrorsHtml(); + std::string getErrorsHtmlNewFormat(); + + std::vector getErrorsArray(); + + void sendErrorsAsEmail(std::string rawHtml = ""); + +protected: + std::stack mErrorStack; + // poco logging + Poco::Logger& mLogging; +}; + +class SendErrorMessage : public UniLib::controller::CPUTask +{ +public: + SendErrorMessage(Poco::Net::MailMessage* message, UniLib::controller::CPUSheduler* scheduler) + : UniLib::controller::CPUTask(scheduler), mMessage(message) {} + + ~SendErrorMessage(); + + virtual int run(); + const char* getResourceType() const { return "SendErrorMessage"; }; + + +protected: + Poco::Net::MailMessage* mMessage; + +}; + +#endif // DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H diff --git a/login_server/src/cpp/main.cpp b/login_server/src/cpp/main.cpp index be08da820..54de9810b 100644 --- a/login_server/src/cpp/main.cpp +++ b/login_server/src/cpp/main.cpp @@ -1,57 +1,57 @@ -#include "Gradido_LoginServer.h" -#include - -#include "proto/gradido/TransactionBody.pb.h" - -#include "model/User.h" -#include "model/Session.h" -#include "lib/Profiler.h" -#include "ServerConfig.h" -#include "ImportantTests.h" - -#include "model/table/User.h" -#include "model/table/EmailOptIn.h" - -#include "Poco/DateTimeParser.h" - -#ifndef _TEST_BUILD - - -int main(int argc, char** argv) -{ - GOOGLE_PROTOBUF_VERIFY_VERSION; - if (sodium_init() < 0) { - /* panic! the library couldn't be initialized, it is not safe to use */ - printf("error initializing sodium, early exit\n"); - return -1; - } - - std::string dateTimeString = __DATE__; - //printf("Building date time string: %s\n", dateTimeString.data()); - std::string formatString("%b %d %Y"); - int timeZone = 0; - - Poco::DateTime buildDateTime = Poco::DateTimeParser::parse(formatString, dateTimeString, timeZone); - ServerConfig::g_versionString = Poco::DateTimeFormatter::format(buildDateTime, "0.%y.%m.%d"); - //ServerConfig::g_versionString = "0.20.KW13.02"; - printf("Version: %s\n", ServerConfig::g_versionString.data()); - printf("User size: %d Bytes, Session size: %d Bytes\n", sizeof(User), sizeof(Session)); - printf("model sizes: User: %d Bytes, EmailOptIn: %d Bytes\n", sizeof(model::table::User), sizeof(model::table::EmailOptIn)); - - // load word lists - if (!ServerConfig::loadMnemonicWordLists()) { - //printf("[Gradido_LoginServer::%s] error loading mnemonic Word List\n", __FUNCTION__); - printf("[Gradido_LoginServer::main] error loading mnemonic Word List"); - return -2; - } - - if (!ImportantTests::passphraseGenerationAndTransformation()) { - printf("test passphrase generation and transformation failed\n"); - return -3; - } - - Gradido_LoginServer app; - app.setUnixOptions(true); - return app.run(argc, argv); -} +#include "Gradido_LoginServer.h" +#include + +#include "proto/gradido/TransactionBody.pb.h" + +#include "model/User.h" +#include "model/Session.h" +#include "lib/Profiler.h" +#include "ServerConfig.h" +#include "ImportantTests.h" + +#include "model/table/User.h" +#include "model/table/EmailOptIn.h" + +#include "Poco/DateTimeParser.h" + +#ifndef _TEST_BUILD + + +int main(int argc, char** argv) +{ + GOOGLE_PROTOBUF_VERIFY_VERSION; + if (sodium_init() < 0) { + /* panic! the library couldn't be initialized, it is not safe to use */ + printf("error initializing sodium, early exit\n"); + return -1; + } + + std::string dateTimeString = __DATE__; + //printf("Building date time string: %s\n", dateTimeString.data()); + std::string formatString("%b %d %Y"); + int timeZone = 0; + + Poco::DateTime buildDateTime = Poco::DateTimeParser::parse(formatString, dateTimeString, timeZone); + ServerConfig::g_versionString = Poco::DateTimeFormatter::format(buildDateTime, "0.%y.%m.%d"); + //ServerConfig::g_versionString = "0.20.KW13.02"; + printf("Version: %s\n", ServerConfig::g_versionString.data()); + printf("User size: %d Bytes, Session size: %d Bytes\n", sizeof(User), sizeof(Session)); + printf("model sizes: User: %d Bytes, EmailOptIn: %d Bytes\n", sizeof(model::table::User), sizeof(model::table::EmailOptIn)); + + // load word lists + if (!ServerConfig::loadMnemonicWordLists()) { + //printf("[Gradido_LoginServer::%s] error loading mnemonic Word List\n", __FUNCTION__); + printf("[Gradido_LoginServer::main] error loading mnemonic Word List"); + return -2; + } + + if (!ImportantTests::passphraseGenerationAndTransformation()) { + printf("test passphrase generation and transformation failed\n"); + return -3; + } + + Gradido_LoginServer app; + app.setUnixOptions(true); + return app.run(argc, argv); +} #endif \ No newline at end of file diff --git a/login_server/src/cpp/model/Session.cpp b/login_server/src/cpp/model/Session.cpp index 86fad389c..2e135fb90 100644 --- a/login_server/src/cpp/model/Session.cpp +++ b/login_server/src/cpp/model/Session.cpp @@ -1,1323 +1,1323 @@ -#include "Session.h" -#include "../lib/Profiler.h" -#include "../ServerConfig.h" - -#include "Poco/RegularExpression.h" -#include "Poco/Net/StringPartSource.h" -#include "Poco/Net/MediaType.h" - -#include "../SingletonManager/SessionManager.h" -#include "../SingletonManager/ConnectionManager.h" -#include "../SingletonManager/ErrorManager.h" -#include "../SingletonManager/EmailManager.h" -#include "../SingletonManager/SingletonTaskObserver.h" - -#include "../tasks/PrepareEmailTask.h" -#include "../tasks/SendEmailTask.h" -#include "../tasks/SigningTransaction.h" -#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h" -#include "../tasks/VerificationEmailResendTask.h" - -#include "../lib/JsonRequest.h" - -#include "../Crypto/Passphrase.h" - - -#include "../controller/User.h" -#include "../controller/UserBackups.h" -#include "../controller/EmailVerificationCode.h" - -#include "table/ModelBase.h" - - -#include "sodium.h" - -using namespace Poco::Data::Keywords; - -int WriteEmailVerification::run() -{ - auto em = ErrorManager::getInstance(); - - mEmailVerificationCode->getModel()->setUserId(mUser->getDBId()); - auto emailVerificationModel = mEmailVerificationCode->getModel(); - emailVerificationModel->setUserId(mUser->getDBId()); - if (!emailVerificationModel->insertIntoDB(true) || emailVerificationModel->errorCount() > 0) { - emailVerificationModel->sendErrorsAsEmail(); - return -1; - } - - return 0; -} - -// --------------------------------------------------------------------------------------------------------------- - -int WritePassphraseIntoDB::run() -{ - Profiler timeUsed; - - // TODO: encrypt passphrase, need server admin crypto box pubkey - //int crypto_box_seal(unsigned char *c, const unsigned char *m, - //unsigned long long mlen, const unsigned char *pk); - size_t mlen = mPassphrase.size(); - size_t crypto_size = crypto_box_SEALBYTES + mlen; - - auto em = ErrorManager::getInstance(); - - auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); - Poco::Data::Statement insert(dbSession); - insert << "INSERT INTO user_backups (user_id, passphrase) VALUES(?,?)", - use(mUserId), use(mPassphrase); - try { - if (insert.execute() != 1) { - em->addError(new ParamError("WritePassphraseIntoDB::run", "inserting passphrase for user failed", std::to_string(mUserId))); - em->sendErrorsAsEmail(); - } - } - catch (Poco::Exception& ex) { - em->addError(new ParamError("WritePassphraseIntoDB::run", "insert passphrase mysql error", ex.displayText().data())); - em->sendErrorsAsEmail(); - } - - //printf("[WritePassphraseIntoDB] timeUsed: %s\n", timeUsed.string().data()); - return 0; -} - - -// -------------------------------------------------------------------------------------------------------------- - -Session::Session(int handle) - : mHandleId(handle), mSessionUser(nullptr), mState(SESSION_STATE_EMPTY), mActive(false) -{ - -} - -Session::~Session() -{ - //printf("[Session::~Session] \n"); - if (tryLock()) { - unlock(); - reset(); - } - - - //printf("[Session::~Session] finished \n"); -} - - -void Session::reset() -{ - //printf("[Session::reset]\n"); - lock("Session::reset"); - std::unique_lock _lock(mSharedMutex); - mSessionUser.assign(nullptr); - mNewUser.assign(nullptr); - mEmailVerificationCodeObject.assign(nullptr); - - // watch out - //updateTimeout(); - mLastActivity = Poco::DateTime(); - - mState = SESSION_STATE_EMPTY; - - mPassphrase = ""; - mLastExternReferer = ""; - mClientLoginIP = Poco::Net::IPAddress(); - unlock(); - - // reset transactions - mCurrentActiveProcessingTransaction = nullptr; - mProcessingTransactions.clear(); - - //printf("[Session::reset] finished\n"); -} - -int Session::isActive() -{ - int ret = 0; - try { - mWorkMutex.tryLock(100); - } - catch (Poco::TimeoutException &ex) { - return -1; - } - ret = (int)mActive; - unlock(); - return ret; - -} - -bool Session::isDeadLocked() -{ - try { - mWorkMutex.tryLock(200); - unlock(); - return false; - } - catch (Poco::Exception& ex) { - - } - return true; -} - -bool Session::setActive(bool active) -{ - try { - mWorkMutex.tryLock(100); - } - catch (Poco::TimeoutException &ex) { - return false; - } - mActive = active; - unlock(); - return true; -} - -void Session::updateTimeout() -{ - lock("Session::updateTimeout"); - mLastActivity = Poco::DateTime(); - unlock(); -} - -Poco::AutoPtr Session::getEmailVerificationCodeObject() -{ - lock("Session::getEmailVerificationCodeObject"); - std::shared_lock _lock(mSharedMutex); - auto ret = mEmailVerificationCodeObject; - unlock(); - return ret; -} - -bool Session::adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email) -{ - Profiler usedTime; - - if (mNewUser->getModel()->getRole() != model::table::ROLE_ADMIN) { - addError(new Error(gettext("Benutzer"), gettext("Eingeloggter Benutzer ist kein Admin")), false); - return false; - } - - auto sm = SessionManager::getInstance(); - if (!sm->isValid(first_name, VALIDATE_NAME)) { - addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(last_name, VALIDATE_NAME)) { - addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(email, VALIDATE_EMAIL)) { - addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); - return false; - } - - - // check if user with that email already exist - if (mNewUser->getModel()->isExistInDB("email", email)) { - addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); - return false; - } - - auto newUser = controller::User::create(email, first_name, last_name); - updateTimeout(); - - - auto newUserModel = newUser->getModel(); - if (!newUserModel->insertIntoDB(true)) { - addError(new Error(gettext("Benutzer"), gettext("Fehler beim speichern!"))); - return false; - } - - auto email_verification_code = controller::EmailVerificationCode::create(newUserModel->getID(), model::table::EMAIL_OPT_IN_REGISTER); - if (!email_verification_code->getModel()->insertIntoDB(false)) { - addError(new Error(gettext("Email Verification Code"), gettext("Fehler beim speichern!"))); - return false; - } - - EmailManager::getInstance()->addEmail(new model::Email(email_verification_code, newUser, model::EMAIL_ADMIN_USER_VERIFICATION_CODE)); - - std::unique_lock _lock(mSharedMutex); - mEmailVerificationCodeObject = email_verification_code; - - - return true; -} -// -bool Session::createUser(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password) -{ - Profiler usedTime; - auto sm = SessionManager::getInstance(); - if (!sm->isValid(first_name, VALIDATE_NAME)) { - addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(last_name, VALIDATE_NAME)) { - addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(email, VALIDATE_EMAIL)) { - addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); - return false; - } - if (!sm->checkPwdValidation(password, this)) { - return false; - } - /*if (passphrase.size() > 0 && !sm->isValid(passphrase, VALIDATE_PASSPHRASE)) { - addError(new Error("Merkspruch", "Der Merkspruch ist nicht gültig, er besteht aus 24 Wörtern, mit Komma getrennt.")); - return false; - } - if (passphrase.size() == 0) { - //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); - } - else { - //mPassphrase = passphrase; - }*/ - - // check if user with that email already exist - - auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); - Poco::Data::Statement select(dbConnection); - select << "SELECT email from users where email = ?;", useRef(email); - try { - if (select.execute() > 0) { - addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); - return false; - } - } - catch (Poco::Exception& exc) { - printf("mysql exception: %s\n", exc.displayText().data()); - } - - mSessionUser = new User(email.data(), first_name.data(), last_name.data()); - mNewUser = controller::User::create(email, first_name, last_name); - updateTimeout(); - - // Prepare E-Mail - //UniLib::controller::TaskPtr prepareEmail(new PrepareEmailTask(ServerConfig::g_CPUScheduler)); - //prepareEmail->scheduleTask(prepareEmail); - - // create user crypto key - UniLib::controller::TaskPtr cryptoKeyTask(new UserCreateCryptoKey(mSessionUser, mNewUser, password, ServerConfig::g_CryptoCPUScheduler)); - cryptoKeyTask->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_CRYPTO_KEY_GENERATED, this)); - cryptoKeyTask->scheduleTask(cryptoKeyTask); - - // depends on crypto key, write user record into db - UniLib::controller::TaskPtr writeUserIntoDB(new UserWriteIntoDB(mSessionUser, ServerConfig::g_CPUScheduler, 1)); - writeUserIntoDB->setParentTaskPtrInArray(cryptoKeyTask, 0); - writeUserIntoDB->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_USER_WRITTEN, this)); - writeUserIntoDB->scheduleTask(writeUserIntoDB); - - std::unique_lock _lock(mSharedMutex); - mEmailVerificationCodeObject = controller::EmailVerificationCode::create(model::table::EMAIL_OPT_IN_REGISTER); - UniLib::controller::TaskPtr writeEmailVerification(new WriteEmailVerification(mSessionUser, mEmailVerificationCodeObject, ServerConfig::g_CPUScheduler, 1)); - - writeEmailVerification->setParentTaskPtrInArray(writeUserIntoDB, 0); - writeEmailVerification->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, this)); - writeEmailVerification->scheduleTask(writeEmailVerification); - - - /*printf("LastName: %s\n", last_name.data()); - for (int i = 0; i < last_name.size(); i++) { - char c = last_name.data()[i]; - //printf("%d ", c); - } - //printf("\n\n"); - */ - - // depends on writeUser because need user_id, write email verification into db - /*auto message = new Poco::Net::MailMessage; - Poco::Net::MediaType mt("text", "plain"); - mt.setParameter("charset", "utf-8"); - message->setContentType(mt); - - message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, email)); - message->setSubject(gettext("Gradido: E-Mail Verification")); - std::stringstream ss; - ss << "Hallo " << first_name << " " << last_name << "," << std::endl << std::endl; - ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Gradido registriert. " << std::endl; - ss << "Wenn du es warst, klicke bitte auf den Link: " << ServerConfig::g_serverPath << "/checkEmail/" << mEmailVerificationCode << std::endl; - //ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl; - ss << "oder kopiere den obigen Link in Dein Browserfenster." << std::endl; - ss << std::endl; - ss << "Mit freundlichen " << u8"Grüßen" << std::endl; - ss << "Dario, Gradido Server Admin" << std::endl; - - - message->addContent(new Poco::Net::StringPartSource(ss.str())); - */ - //UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1)); - //Email(AutoPtr emailVerification, AutoPtr user, EmailType type); - UniLib::controller::TaskPtr sendEmail(new SendEmailTask(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_VERIFICATION_CODE), ServerConfig::g_CPUScheduler, 1)); - //sendEmail->setParentTaskPtrInArray(prepareEmail, 0); - sendEmail->setParentTaskPtrInArray(writeEmailVerification, 0); - sendEmail->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_SEND, this)); - sendEmail->scheduleTask(sendEmail); - - // write user into db - // generate and write email verification into db - // send email - - //printf("[Session::createUser] time: %s\n", usedTime.string().data()); - - return true; -} - -bool Session::createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password) -{ - std::unique_lock _lock(mSharedMutex); - static const char* function_name = "Session::createUserDirect"; - auto sm = SessionManager::getInstance(); - auto em = ErrorManager::getInstance(); - auto email_manager = EmailManager::getInstance(); - - if (!sm->isValid(first_name, VALIDATE_NAME)) { - addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(last_name, VALIDATE_NAME)) { - addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(email, VALIDATE_EMAIL)) { - addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); - return false; - } - if (!sm->checkPwdValidation(password, this)) { - return false; - } - - // check if email already exist - auto user = controller::User::create(); - if (user->load(email) >= 1) { - addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits ein Konto")), false); - return false; - } - - // user - mNewUser = controller::User::create(email, first_name, last_name); - auto user_model = mNewUser->getModel(); - user_model->insertIntoDB(true); - auto user_id = user_model->getID(); - - - // one retry in case of connection error - if (!user_id) { - user_model->insertIntoDB(true); - auto user_id = user_model->getID(); - if (!user_id) { - em->addError(new ParamError(function_name, "error saving new user in db, after one retry with email", email)); - em->sendErrorsAsEmail(); - addError(new Error(gettext("Server"), gettext("Fehler beim speichen des Kontos bitte versuche es später noch einmal")), false); - return false; - } - } - - generateKeys(true, true); - - // calculate encryption key, could need some time, will save encrypted privkey to db - UniLib::controller::TaskPtr create_authenticated_encrypten_key = new AuthenticatedEncryptionCreateKeyTask(mNewUser, password); - create_authenticated_encrypten_key->scheduleTask(create_authenticated_encrypten_key); - - // email verification code - auto email_verification = controller::EmailVerificationCode::create(user_id, model::table::EMAIL_OPT_IN_REGISTER_DIRECT); - email_verification->getModel()->insertIntoDB(false); - mEmailVerificationCodeObject = email_verification; - - auto _7days_later = Poco::DateTime() + Poco::Timespan(7, 0, 0, 0, 0); - ServerConfig::g_CronJobsTimer.schedule(new VerificationEmailResendTimerTask(user_id), Poco::Timestamp(_7days_later.timestamp())); - - email_manager->addEmail(new model::Email(email_verification, mNewUser, model::EMAIL_USER_VERIFICATION_CODE)); - - return true; -} - -bool Session::ifUserExist(const std::string& email) -{ - auto em = ErrorManager::getInstance(); - const char* funcName = "Session::ifUserExist"; - auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); - Poco::Data::Statement select(dbConnection); - bool emailChecked = false; - int userId = 0; - select << "SELECT email_checked, id from users where email = ? and email_checked = 1", - into(emailChecked), into(userId), useRef(email); - - try { - if(select.execute() == 1) return true; - } - catch (Poco::Exception& ex) { - em->addError(new ParamError(funcName, "select user from email verification code mysql error ", ex.displayText().data())); - em->sendErrorsAsEmail(); - } - return false; -} - -int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) -{ - const static char* funcName = "Session::updateEmailVerification"; - Poco::ScopedLock _lock(mWorkMutex); - // new mutex, will replace the Poco Mutex complete in the future - std::unique_lock _lock_shared(mSharedMutex); - Profiler usedTime; - - auto em = ErrorManager::getInstance(); - if (mEmailVerificationCodeObject.isNull()) { - em->addError(new Error(funcName, "email verification object is zero")); - em->sendErrorsAsEmail(); - - return -2; - } - auto email_verification_code_model = mEmailVerificationCodeObject->getModel(); - assert(email_verification_code_model); - if(email_verification_code_model->getCode() == emailVerificationCode) { - if (mSessionUser && mSessionUser->getDBId() == 0) { - //addError(new Error("E-Mail Verification", "Benutzer wurde nicht richtig gespeichert, bitte wende dich an den Server-Admin")); - em->addError(new Error(funcName, "user exist with 0 as id")); - em->sendErrorsAsEmail(); - - //return false; - return -2; - } - - // load correct user from db - if (mNewUser.isNull() || !mNewUser->getModel() || mNewUser->getModel()->getID() != email_verification_code_model->getUserId()) { - mNewUser = controller::User::create(); - if (1 != mNewUser->load(email_verification_code_model->getUserId())) { - em->addError(new ParamError(funcName, "user load didn't return 1 with user_id ", email_verification_code_model->getUserId())); - em->sendErrorsAsEmail(); - - return -2; - } - } - - auto user_model = mNewUser->getModel(); - assert(user_model); - bool first_email_activation = false; - auto verification_type = email_verification_code_model->getType(); - if (model::table::EMAIL_OPT_IN_REGISTER == verification_type || - model::table::EMAIL_OPT_IN_EMPTY == verification_type || - model::table::EMAIL_OPT_IN_REGISTER_DIRECT == verification_type) { - first_email_activation = true; - } - if (first_email_activation && user_model->isEmailChecked()) { - mSessionUser = new User(mNewUser); - addError(new Error(gettext("E-Mail Verification"), gettext("Du hast dein Konto bereits aktiviert!")), false); - - return 1; - } - if (first_email_activation) { - user_model->setEmailChecked(true); - - user_model->updateIntoDB("email_checked", 1); - if (user_model->errorCount() > 0) { - user_model->sendErrorsAsEmail(); - } - - // no find all active sessions - - updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); - return 0; - } - - if (email_verification_code_model->getType() == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { - - if (mEmailVerificationCodeObject->deleteFromDB()) { - mEmailVerificationCodeObject.assign(nullptr); - } - else { - em->getErrors(mEmailVerificationCodeObject->getModel()); - em->addError(new Error(funcName, "error deleting email verification code")); - em->sendErrorsAsEmail(); - return -2; - } - updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); - return 0; - } - - em->addError(new Error(funcName, "invalid code path")); - em->sendErrorsAsEmail(); - - return -2; - - /*if (updated_rows == 1) { - Poco::Data::Statement delete_row(dbConnection); - delete_row << "DELETE FROM email_opt_in where verification_code = ?", use(emailVerificationCode); - if (delete_row.execute() != 1) { - em->addError(new Error(funcName, "delete from email_opt_in entry didn't work as expected, please check db")); - em->sendErrorsAsEmail(); - } - if (mSessionUser) { - mSessionUser->setEmailChecked(); - mSessionUser->setLanguage(getLanguage()); - } - updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); - //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - unlock(); - return true; - } - else { - em->addError(new ParamError(funcName, "update user work not like expected, updated row count", updated_rows)); - em->sendErrorsAsEmail(); - }*/ - - - } - else { - addError(new Error(gettext("E-Mail Verification"), gettext("Falscher Code für aktiven Login"))); - //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - - return -1; - } - //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - - return 0; -} - - -int Session::sendResetPasswordEmail(Poco::AutoPtr user, bool passphraseMemorized) -{ - mNewUser = user; - mSessionUser = new User(user); - auto em = EmailManager::getInstance(); - - std::unique_lock _lock(mSharedMutex); - - // creating email verification code also for user without passphrase - // first check if already exist - // check if email was already send shortly before - bool frequent_resend = false; - bool email_already_send = false; - - mEmailVerificationCodeObject = controller::EmailVerificationCode::load(user->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); - if (mEmailVerificationCodeObject.isNull()) { - mEmailVerificationCodeObject = controller::EmailVerificationCode::create(mNewUser->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); - mEmailVerificationCodeObject->getModel()->insertIntoDB(false); - } - else { - email_already_send = true; - } - auto email_verification_model = mEmailVerificationCodeObject->getModel(); - if (email_already_send) { - auto time_elapsed = Poco::DateTime() - email_verification_model->getUpdated(); - if (time_elapsed.totalHours() < 1) { - frequent_resend = true; - } - } - - if (!frequent_resend) { - if (passphraseMemorized) { - em->addEmail(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_RESET_PASSWORD)); - } - else { - em->addEmail(new model::Email(user, model::EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE)); - } - } - - if (frequent_resend) return 2; - if (email_already_send) return 1; - - return 0; -} - -int Session::comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource) -{ - KeyPair keys; - static const char* functionName = "Session::comparePassphraseWithSavedKeys"; - if (!wordSource) { - addError(new Error(functionName, "wordSource is empty")); - sendErrorsAsEmail(); - return -2; - } - if (!keys.generateFromPassphrase(inputPassphrase.data(), wordSource)) { - addError(new ParamError(functionName, "invalid passphrase", inputPassphrase)); - if (!mNewUser.isNull() && mNewUser->getModel()) { - addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); - } - sendErrorsAsEmail(); - addError(new Error(gettext("Passphrase"), gettext("Deine Passphrase ist ungütig")), false); - return 0; - } - auto userModel = mNewUser->getModel(); - auto existingPublic = userModel->getPublicKey(); - if (!existingPublic) { - userModel->loadFromDB("email", userModel->getEmail()); - existingPublic = userModel->getPublicKey(); - if (!existingPublic) { - addError(new Error(functionName, "cannot load existing public key from db")); - addError(new ParamError(functionName, "user email", userModel->getEmail())); - sendErrorsAsEmail(); - addError(new Error(gettext("Passphrase"), gettext("Ein Fehler trat auf, bitte versuche es erneut")), false); - return -1; - } - } - if (0 == memcmp(userModel->getPublicKey(), keys.getPublicKey(), crypto_sign_PUBLICKEYBYTES)) { - mPassphrase = inputPassphrase; - return 1; - } - addError(new Error(gettext("Passphrase"), gettext("Das ist nicht die richtige Passphrase.")), false); - return 0; -} - -bool Session::startProcessingTransaction(const std::string& proto_message_base64, bool autoSign/* = false*/) -{ - static const char* funcName = "Session::startProcessingTransaction"; - lock(funcName); - HASH hs = ProcessingTransaction::calculateHash(proto_message_base64); - // check if it is already running or waiting - for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { - if (it->isNull()) { - it = mProcessingTransactions.erase(it); - } - if (hs == (*it)->getHash()) { - addError(new Error(funcName, "transaction already in list")); - unlock(); - return false; - } - } - if (mSessionUser.isNull() || !mSessionUser->getEmail()) { - addError(new Error(funcName, "user is zero")); - unlock(); - return false; - } - - Poco::AutoPtr processorTask( - new ProcessingTransaction( - proto_message_base64, - DRMakeStringHash(mSessionUser->getEmail()), - mSessionUser->getLanguage()) - ); - if (autoSign && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) == ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) { - if (processorTask->run() != 0) { - getErrors(processorTask); - unlock(); - return false; - } - Poco::AutoPtr signingTransaction(new SigningTransaction(processorTask, mNewUser)); - //signingTransaction->scheduleTask(signingTransaction); - if (signingTransaction->run() != 0) { - getErrors(signingTransaction); - unlock(); - return false; - } - - } - else { - processorTask->scheduleTask(processorTask); - mProcessingTransactions.push_back(processorTask); - } - unlock(); - return true; - -} - -Poco::AutoPtr Session::getNextReadyTransaction(size_t* working/* = nullptr*/) -{ - lock("Session::getNextReadyTransaction"); - if (working) { - *working = 0; - } - else if (!mCurrentActiveProcessingTransaction.isNull()) - { - unlock(); - return mCurrentActiveProcessingTransaction; - } - for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { - if (working && !(*it)->isTaskFinished()) { - (*working)++; - } - if (mCurrentActiveProcessingTransaction.isNull() && (*it)->isTaskFinished()) { - if (!working) { - mCurrentActiveProcessingTransaction = *it; - unlock(); - return mCurrentActiveProcessingTransaction; - } - // no early exit - else { - mCurrentActiveProcessingTransaction = *it; - } - - } - } - unlock(); - return mCurrentActiveProcessingTransaction; -} - -bool Session::finalizeTransaction(bool sign, bool reject) -{ - int result = -1; - lock("Session::finalizeTransaction"); - if (mCurrentActiveProcessingTransaction.isNull()) { - unlock(); - return false; - } - mProcessingTransactions.remove(mCurrentActiveProcessingTransaction); - - if (!reject) { - if (sign) { - Poco::AutoPtr signingTransaction(new SigningTransaction(mCurrentActiveProcessingTransaction, mNewUser)); - //signingTransaction->scheduleTask(signingTransaction); - result = signingTransaction->run(); - } - } - mCurrentActiveProcessingTransaction.assign(nullptr); - unlock(); - return result == 0; -} - -size_t Session::getProcessingTransactionCount() -{ - size_t count = 0; - lock("Session::getProcessingTransactionCount"); - - for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { - - (*it)->lock(); - if ((*it)->errorCount() > 0) { - (*it)->sendErrorsAsEmail(); - (*it)->unlock(); - it = mProcessingTransactions.erase(it); - if (it == mProcessingTransactions.end()) break; - } - else { - (*it)->unlock(); - } - - } - count = mProcessingTransactions.size(); - unlock(); - return count; -} - -bool Session::isPwdValid(const std::string& pwd) -{ - if (mSessionUser) { - return mSessionUser->validatePwd(pwd, this); - } - return false; -} - -UserStates Session::loadUser(const std::string& email, const std::string& password) -{ - static const char* functionName = "Session::loadUser"; - auto observer = SingletonTaskObserver::getInstance(); - if (email != "") { - if (observer->getTaskCount(email, TASK_OBSERVER_PASSWORD_CREATION) > 0) { - return USER_PASSWORD_ENCRYPTION_IN_PROCESS; - } - } - //Profiler usedTime; - //printf("before lock\n"); - lock(functionName); - //printf("locked \n"); - if (!mSessionUser.isNull() && mSessionUser->getEmail() != email) { - mSessionUser.assign(nullptr); - mNewUser.assign(nullptr); - //printf("user nullptr assigned\n"); - } - //printf("after checking if session user is null\n"); - //if (!mSessionUser) { - if (mNewUser.isNull()) { - //printf("new user is null\n"); - mNewUser = controller::User::create(); - //printf("new user created\n"); - // load user for email only once from db - mNewUser->load(email); - //printf("load new user from db with email: %s\n", email.data()); - mSessionUser = new User(mNewUser); - //mSessionUser = new User(email.data()); - - //printf("user loaded from email\n"); - } - //printf("before get model\n"); - auto user_model = mNewUser->getModel(); - if (user_model && user_model->isDisabled()) { - return USER_DISABLED; - } - //printf("before if login\n"); - if (!mSessionUser.isNull() && mSessionUser->getUserState() >= USER_LOADED_FROM_DB) { - //printf("before login\n"); - int loginResult = 0; - int exitCount = 0; - do { - loginResult = mNewUser->login(password); - Poco::Thread::sleep(100); - exitCount++; - } while (-3 == loginResult && exitCount < 15); - if (exitCount > 1) { - addError(new ParamError(functionName, "login succeed, retrys: ", exitCount)); - addError(new ParamError(functionName, "email: ", email)); - sendErrorsAsEmail(); - } - - if (exitCount >= 15) - { - auto running_password_creations = observer->getTasksCount(TASK_OBSERVER_PASSWORD_CREATION); - - addError(new ParamError(functionName, "login failed after 15 retrys and 100 ms sleep between, currently running passwort creation tasks: ", running_password_creations)); - addError(new ParamError(functionName, "email: ", email)); - sendErrorsAsEmail(); - return USER_PASSWORD_ENCRYPTION_IN_PROCESS; - } - - //printf("new user login with result: %d\n", loginResult); - - if (-1 == loginResult) { - addError(new Error(functionName, "error in user data set, saved pubkey didn't match extracted pubkey from private key")); - addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); - sendErrorsAsEmail(); - //unlock(); - //return USER_KEYS_DONT_MATCH; - } - if (0 == loginResult) { - unlock(); - return USER_PASSWORD_INCORRECT; - } - // error decrypting private key - if (-2 == loginResult) { - // check if we have access to the passphrase, if so we can reencrypt the private key - printf("try reencrypting key\n"); - auto user_model = mNewUser->getModel(); - auto user_backups = controller::UserBackups::load(user_model->getID()); - for (auto it = user_backups.begin(); it != user_backups.end(); it++) { - auto key = std::unique_ptr((*it)->createGradidoKeyPair()); - if (key->isTheSame(user_model->getPublicKey())) - { - - // set valid key pair - if (1 == mNewUser->setGradidoKeyPair(key.release())) { - // save new encrypted private key - user_model->updatePrivkey(); - } - else { - auto em = ErrorManager::getInstance(); - em->addError(new Error(functionName, "error reencrypt private key")); - em->addError(new ParamError(functionName, "for user with email", user_model->getEmail())); - em->sendErrorsAsEmail(); - } - break; - } - } - } - // can be removed if session user isn't used any more - // don't calculate password two times anymore - mSessionUser->login(mNewUser); - //printf("after old user login\n"); - /*if (mNewUser->getModel()->getPasswordHashed() && !mSessionUser->validatePwd(password, this)) { - unlock(); - return USER_PASSWORD_INCORRECT; - }*/ - } - else { - //printf("before sleep\n"); - User::fakeCreateCryptoKey(); - } - - /*if (!mSessionUser->validatePwd(password, this)) { - addError(new Error("Login", "E-Mail oder Passwort nicht korrekt, bitte versuche es erneut!")); - unlock(); - return false; - } - if (!mSessionUser->isEmailChecked()) { - addError(new Error("Account", "E-Mail Adresse wurde noch nicht bestätigt, hast du schon eine E-Mail erhalten?")); - unlock(); - return false; - }*/ - //printf("before detect session state\n"); - detectSessionState(); - unlock(); - //printf("before return user state\n"); - return mSessionUser->getUserState(); -} - -bool Session::deleteUser() -{ - lock("Session::deleteUser"); - bool bResult = false; - if(mSessionUser) { - JsonRequest phpServerRequest(ServerConfig::g_php_serverHost, 443); - Poco::Net::NameValueCollection payload; - payload.add("user", std::string(mSessionUser->getPublicKeyHex())); - //auto ret = phpServerRequest.request("userDelete", payload); - JsonRequestReturn ret = JSON_REQUEST_RETURN_OK; - if (ret == JSON_REQUEST_RETURN_ERROR) { - addError(new Error("Session::deleteUser", "php server error")); - getErrors(&phpServerRequest); - sendErrorsAsEmail(); - } - else if (ret == JSON_REQUEST_RETURN_OK) { - bResult = mSessionUser->deleteFromDB(); - } - else { - addError(new Error(gettext("Benutzer"), gettext("Konnte Community Server nicht erreichen. E-Mail an den Admin ist raus."))); - unlock(); - return false; - } - } - if(!bResult) { - addError(new Error(gettext("Benutzer"), gettext("Fehler beim Löschen des Accounts. Bitte logge dich erneut ein und versuche es nochmal."))); - } - unlock(); - return bResult; -} - -void Session::setLanguage(Languages lang) -{ - //printf("[Session::setLanguage] new language: %d\n", lang); - lock("Session::setLanguage"); - if (mLanguageCatalog.isNull() || mLanguageCatalog->getLanguage() != lang) { - auto lm = LanguageManager::getInstance(); - mLanguageCatalog = lm->getFreeCatalog(lang); - } - unlock(); -} - -Languages Session::getLanguage() -{ - Languages lang = LANG_NULL; - lock("Session::getLanguage"); - if (!mLanguageCatalog.isNull()) { - lang = mLanguageCatalog->getLanguage(); - } - unlock(); - return lang; -} - - -/* -SESSION_STATE_CRYPTO_KEY_GENERATED, -SESSION_STATE_USER_WRITTEN, -SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, -SESSION_STATE_EMAIL_VERIFICATION_SEND, -SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED, -SESSION_STATE_PASSPHRASE_GENERATED, -SESSION_STATE_PASSPHRASE_SHOWN, -SESSION_STATE_PASSPHRASE_WRITTEN, -SESSION_STATE_KEY_PAIR_GENERATED, -SESSION_STATE_KEY_PAIR_WRITTEN, -SESSION_STATE_COUNT -*/ -void Session::detectSessionState() -{ - if (mSessionUser.isNull() || !mSessionUser->hasCryptoKey()) { - return; - } - UserStates userState = mSessionUser->getUserState(); - - int checkEmail = -1, resetPasswd = -1; - try { - auto emailVerificationCodeObjects = controller::EmailVerificationCode::load(mSessionUser->getDBId()); - - for (int i = 0; i < emailVerificationCodeObjects.size(); i++) { - auto type = emailVerificationCodeObjects[i]->getModel()->getType(); - if (type == model::table::EMAIL_OPT_IN_EMPTY || type == model::table::EMAIL_OPT_IN_REGISTER) { - checkEmail = i; - } - else if (type == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { - resetPasswd = i; - } - } - std::unique_lock _lock_shared(mSharedMutex); - if (resetPasswd != -1) { - mEmailVerificationCodeObject = emailVerificationCodeObjects[resetPasswd]; - } - else if (checkEmail != -1) { - mEmailVerificationCodeObject = emailVerificationCodeObjects[checkEmail]; - } - - } - catch (Poco::Exception& ex) { - printf("[Session::detectSessionState] exception: %s\n", ex.displayText().data()); - //return; - } - - if (userState <= USER_EMAIL_NOT_ACTIVATED) { - - if (checkEmail != -1) { - updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); - return; - } - - updateState(SESSION_STATE_USER_WRITTEN); - return; - } - - if (USER_NO_KEYS == userState) { - - auto user_id = mSessionUser->getDBId(); - auto userBackups = controller::UserBackups::load(user_id); - - // check passphrase, only possible while passphrase isn't crypted in db - bool correctPassphraseFound = false; - // always trigger SESSION_STATE_PASSPHRASE_WRITTEN, else lost of data possible - bool cryptedPassphrase = userBackups.size() > 0; - for (auto it = userBackups.begin(); it != userBackups.end(); it++) { - KeyPair keys; - auto passphrase = (*it)->getModel()->getPassphrase(); - Mnemonic* wordSource = nullptr; - if (User::validatePassphrase(passphrase, &wordSource)) { - if (keys.generateFromPassphrase((*it)->getModel()->getPassphrase().data(), wordSource)) { - if (sodium_memcmp(mSessionUser->getPublicKey(), keys.getPublicKey(), ed25519_pubkey_SIZE) == 0) { - correctPassphraseFound = true; - break; - } - } - } - else { - cryptedPassphrase = true; - } - } - /* - auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); - Poco::Data::Statement select(dbConnection); - Poco::Nullable passphrase; - - select << "SELECT passphrase from user_backups where user_id = ?;", - into(passphrase), use(user_id); - try { - if (select.execute() == 1 && !passphrase.isNull()) { - //KeyPair keys;keys.generateFromPassphrase(passphrase.value().rawContent()) - updateState(SESSION_STATE_PASSPHRASE_WRITTEN); - return; - } - } - catch (Poco::Exception& exc) { - printf("[Session::detectSessionState] 2 mysql exception: %s\n", exc.displayText().data()); - }*/ - if (correctPassphraseFound || cryptedPassphrase) { - updateState(SESSION_STATE_PASSPHRASE_WRITTEN); - return; - } - if (mPassphrase != "") { - updateState(SESSION_STATE_PASSPHRASE_GENERATED); - return; - } - updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); - return; - } - - updateState(SESSION_STATE_KEY_PAIR_WRITTEN); - - if (resetPasswd != -1) { - // don't go to reset password screen after login, only throw checkEmail - //updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); - return; - } - -} - -Poco::Net::HTTPCookie Session::getLoginCookie() -{ - auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", std::to_string(mHandleId)); - // prevent reading or changing cookie with js -// keks.setHttpOnly(); - - keks.setPath("/"); - // send cookie only via https, on linux, except in test builds -#ifndef WIN32 - if (ServerConfig::SERVER_TYPE_PRODUCTION == ServerConfig::g_ServerSetupType || - ServerConfig::SERVER_TYPE_STAGING == ServerConfig::g_ServerSetupType) { - keks.setSecure(true); - } -#endif - - return keks; -} - -bool Session::loadFromEmailVerificationCode(Poco::UInt64 emailVerificationCode) -{ - Profiler usedTime; - auto em = ErrorManager::getInstance(); - std::unique_lock _lock(mSharedMutex); - mEmailVerificationCodeObject = controller::EmailVerificationCode::load(emailVerificationCode); - if (mEmailVerificationCodeObject.isNull()) { - addError(new Error(gettext("E-Mail Verification"), gettext("Konnte kein passendes Konto finden."))); - return false; - } - - mNewUser = controller::User::create(); - assert(mEmailVerificationCodeObject->getModel() && mEmailVerificationCodeObject->getModel()->getUserId()); - mNewUser->load(mEmailVerificationCodeObject->getModel()->getUserId()); - if (mNewUser->getModel()->errorCount() > 0) { - mNewUser->getModel()->sendErrorsAsEmail(); - addError(new Error(gettext("E-Mail Verification"), gettext("Fehler beim laden des Benutzers."))); - return false; - } - mSessionUser = new User(mNewUser); - mSessionUser->setLanguage(getLanguage()); - - auto verificationType = mEmailVerificationCodeObject->getModel()->getType(); - if (verificationType == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { - updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); - } - else { - updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); - } - - return true; -} - -void Session::updateState(SessionStates newState) -{ - lock("Session::updateState"); - if (!mActive) { - unlock(); - return; - } - updateTimeout(); - //printf("[%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState)); - if (newState > mState) { - mState = newState; - } - - unlock(); -} - -const char* Session::getSessionStateString() -{ - SessionStates state; - lock("Session::getSessionStateString"); - state = mState; - unlock(); - return translateSessionStateToString(state); -} - - -const char* Session::translateSessionStateToString(SessionStates state) -{ - switch (state) { - case SESSION_STATE_EMPTY: return "uninitalized"; - case SESSION_STATE_CRYPTO_KEY_GENERATED: return "crpyto key generated"; - case SESSION_STATE_USER_WRITTEN: return "User saved"; - case SESSION_STATE_EMAIL_VERIFICATION_WRITTEN: return "E-Mail verification code saved"; - case SESSION_STATE_EMAIL_VERIFICATION_SEND: return "Verification E-Mail sended"; - case SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED: return "Verification Code checked"; - case SESSION_STATE_PASSPHRASE_GENERATED: return "Passphrase generated"; - case SESSION_STATE_PASSPHRASE_SHOWN: return "Passphrase shown"; - case SESSION_STATE_PASSPHRASE_WRITTEN: return "Passphrase written"; - case SESSION_STATE_KEY_PAIR_GENERATED: return "Gradido Address created"; - case SESSION_STATE_KEY_PAIR_WRITTEN: return "Gradido Address saved"; - case SESSION_STATE_RESET_PASSWORD_REQUEST: return "Passwort reset requested"; - case SESSION_STATE_RESET_PASSWORD_SUCCEED: return "Passwort reset succeeded"; - default: return "unknown"; - } - - return "error"; -} - - -/* -bool Session::useOrGeneratePassphrase(const std::string& passphase) -{ - if (passphase != "" && User::validatePassphrase(passphase)) { - // passphrase is valid - setPassphrase(passphase); - updateState(SESSION_STATE_PASSPHRASE_SHOWN); - return true; - } - else { - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); - updateState(SESSION_STATE_PASSPHRASE_GENERATED); - return true; - } -} -*/ -bool Session::generatePassphrase() -{ - if (mNewUser.isNull()) return false; - - auto lang = getLanguage(); - if (lang == LANG_EN) { - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); - } - else { - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); - } - //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); - updateState(SESSION_STATE_PASSPHRASE_GENERATED); - return true; -} - -bool Session::generateKeys(bool savePrivkey, bool savePassphrase) -{ - if (mNewUser.isNull()) { - addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); - return false; - } - static const char* function_name = "Session::generateKeys"; - auto lang = getLanguage(); - auto user_model = mNewUser->getModel(); - auto mnemonic_type = ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; - /*if (LANG_DE == lang) { - mnemonic_type = ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES; - }*/ - - auto passphrase = Passphrase::generate(&ServerConfig::g_Mnemonic_WordLists[mnemonic_type]); - if (!passphrase) { - addError(new ParamError(function_name, "Error generating passphrase with mnemonic: ", mnemonic_type)); - addError(new ParamError(function_name, "user email: ", mNewUser->getModel()->getEmail())); - sendErrorsAsEmail(); - addError(new Error(gettext("Benutzer"), gettext("Fehler beim generieren der Passphrase, der Admin bekommt eine E-Mail. "))); - return false; - } - - if (savePassphrase) { - auto user_backup = controller::UserBackups::create(user_model->getID(), passphrase->getString(), mnemonic_type); - // sync version - //user_backup->getModel()->insertIntoDB(false); - - // async version - UniLib::controller::TaskPtr save_user_backup_task = new model::table::ModelInsertTask(user_backup->getModel(), false, true); - save_user_backup_task->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); - save_user_backup_task->scheduleTask(save_user_backup_task); - } - - // keys - auto gradido_key_pair = KeyPairEd25519::create(passphrase); - auto set_key_result = mNewUser->setGradidoKeyPair(gradido_key_pair); - size_t result_save_key = 0; - if (1 == set_key_result && savePrivkey) { - // save public key and private key in db - result_save_key = user_model->updatePubkeyAndPrivkey(); - } - else { - // save public key in db - result_save_key = user_model->updatePublickey(); - } - if (!result_save_key) { - user_model->addError(new Error(function_name, "Error saving new generated pubkey")); - user_model->addError(new ParamError(function_name, "e-mail: ", user_model->getEmail())); - user_model->sendErrorsAsEmail(); - //addError(new Error(gettext("Benutzer"), gettext("Fehler beim Speichern der Keys, der Admin bekommt eine E-Mail. Evt. nochmal versuchen oder abwarten!"))); - return false; - } - return true; - /* - - bool validUser = true; - if (mSessionUser) { - if (!mSessionUser->generateKeys(savePrivkey, mPassphrase, this)) { - validUser = false; - } - else { - if (savePassphrase) { - //printf("[Session::generateKeys] create save passphrase task\n"); - UniLib::controller::TaskPtr savePassphrase(new WritePassphraseIntoDB(mSessionUser->getDBId(), mPassphrase)); - savePassphrase->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); - savePassphrase->scheduleTask(savePassphrase); - } - } - } - else { - validUser = false; - } - if (!validUser) { - addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); - return false; - } - // delete passphrase after all went well - mPassphrase.clear(); - - return true; - */ -} +#include "Session.h" +#include "../lib/Profiler.h" +#include "../ServerConfig.h" + +#include "Poco/RegularExpression.h" +#include "Poco/Net/StringPartSource.h" +#include "Poco/Net/MediaType.h" + +#include "../SingletonManager/SessionManager.h" +#include "../SingletonManager/ConnectionManager.h" +#include "../SingletonManager/ErrorManager.h" +#include "../SingletonManager/EmailManager.h" +#include "../SingletonManager/SingletonTaskObserver.h" + +#include "../tasks/PrepareEmailTask.h" +#include "../tasks/SendEmailTask.h" +#include "../tasks/SigningTransaction.h" +#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h" +#include "../tasks/VerificationEmailResendTask.h" + +#include "../lib/JsonRequest.h" + +#include "../Crypto/Passphrase.h" + + +#include "../controller/User.h" +#include "../controller/UserBackups.h" +#include "../controller/EmailVerificationCode.h" + +#include "table/ModelBase.h" + + +#include "sodium.h" + +using namespace Poco::Data::Keywords; + +int WriteEmailVerification::run() +{ + auto em = ErrorManager::getInstance(); + + mEmailVerificationCode->getModel()->setUserId(mUser->getDBId()); + auto emailVerificationModel = mEmailVerificationCode->getModel(); + emailVerificationModel->setUserId(mUser->getDBId()); + if (!emailVerificationModel->insertIntoDB(true) || emailVerificationModel->errorCount() > 0) { + emailVerificationModel->sendErrorsAsEmail(); + return -1; + } + + return 0; +} + +// --------------------------------------------------------------------------------------------------------------- + +int WritePassphraseIntoDB::run() +{ + Profiler timeUsed; + + // TODO: encrypt passphrase, need server admin crypto box pubkey + //int crypto_box_seal(unsigned char *c, const unsigned char *m, + //unsigned long long mlen, const unsigned char *pk); + size_t mlen = mPassphrase.size(); + size_t crypto_size = crypto_box_SEALBYTES + mlen; + + auto em = ErrorManager::getInstance(); + + auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement insert(dbSession); + insert << "INSERT INTO user_backups (user_id, passphrase) VALUES(?,?)", + use(mUserId), use(mPassphrase); + try { + if (insert.execute() != 1) { + em->addError(new ParamError("WritePassphraseIntoDB::run", "inserting passphrase for user failed", std::to_string(mUserId))); + em->sendErrorsAsEmail(); + } + } + catch (Poco::Exception& ex) { + em->addError(new ParamError("WritePassphraseIntoDB::run", "insert passphrase mysql error", ex.displayText().data())); + em->sendErrorsAsEmail(); + } + + //printf("[WritePassphraseIntoDB] timeUsed: %s\n", timeUsed.string().data()); + return 0; +} + + +// -------------------------------------------------------------------------------------------------------------- + +Session::Session(int handle) + : mHandleId(handle), mSessionUser(nullptr), mState(SESSION_STATE_EMPTY), mActive(false) +{ + +} + +Session::~Session() +{ + //printf("[Session::~Session] \n"); + if (tryLock()) { + unlock(); + reset(); + } + + + //printf("[Session::~Session] finished \n"); +} + + +void Session::reset() +{ + //printf("[Session::reset]\n"); + lock("Session::reset"); + std::unique_lock _lock(mSharedMutex); + mSessionUser.assign(nullptr); + mNewUser.assign(nullptr); + mEmailVerificationCodeObject.assign(nullptr); + + // watch out + //updateTimeout(); + mLastActivity = Poco::DateTime(); + + mState = SESSION_STATE_EMPTY; + + mPassphrase = ""; + mLastExternReferer = ""; + mClientLoginIP = Poco::Net::IPAddress(); + unlock(); + + // reset transactions + mCurrentActiveProcessingTransaction = nullptr; + mProcessingTransactions.clear(); + + //printf("[Session::reset] finished\n"); +} + +int Session::isActive() +{ + int ret = 0; + try { + mWorkMutex.tryLock(100); + } + catch (Poco::TimeoutException &ex) { + return -1; + } + ret = (int)mActive; + unlock(); + return ret; + +} + +bool Session::isDeadLocked() +{ + try { + mWorkMutex.tryLock(200); + unlock(); + return false; + } + catch (Poco::Exception& ex) { + + } + return true; +} + +bool Session::setActive(bool active) +{ + try { + mWorkMutex.tryLock(100); + } + catch (Poco::TimeoutException &ex) { + return false; + } + mActive = active; + unlock(); + return true; +} + +void Session::updateTimeout() +{ + lock("Session::updateTimeout"); + mLastActivity = Poco::DateTime(); + unlock(); +} + +Poco::AutoPtr Session::getEmailVerificationCodeObject() +{ + lock("Session::getEmailVerificationCodeObject"); + std::shared_lock _lock(mSharedMutex); + auto ret = mEmailVerificationCodeObject; + unlock(); + return ret; +} + +bool Session::adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email) +{ + Profiler usedTime; + + if (mNewUser->getModel()->getRole() != model::table::ROLE_ADMIN) { + addError(new Error(gettext("Benutzer"), gettext("Eingeloggter Benutzer ist kein Admin")), false); + return false; + } + + auto sm = SessionManager::getInstance(); + if (!sm->isValid(first_name, VALIDATE_NAME)) { + addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(last_name, VALIDATE_NAME)) { + addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(email, VALIDATE_EMAIL)) { + addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); + return false; + } + + + // check if user with that email already exist + if (mNewUser->getModel()->isExistInDB("email", email)) { + addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); + return false; + } + + auto newUser = controller::User::create(email, first_name, last_name); + updateTimeout(); + + + auto newUserModel = newUser->getModel(); + if (!newUserModel->insertIntoDB(true)) { + addError(new Error(gettext("Benutzer"), gettext("Fehler beim speichern!"))); + return false; + } + + auto email_verification_code = controller::EmailVerificationCode::create(newUserModel->getID(), model::table::EMAIL_OPT_IN_REGISTER); + if (!email_verification_code->getModel()->insertIntoDB(false)) { + addError(new Error(gettext("Email Verification Code"), gettext("Fehler beim speichern!"))); + return false; + } + + EmailManager::getInstance()->addEmail(new model::Email(email_verification_code, newUser, model::EMAIL_ADMIN_USER_VERIFICATION_CODE)); + + std::unique_lock _lock(mSharedMutex); + mEmailVerificationCodeObject = email_verification_code; + + + return true; +} +// +bool Session::createUser(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password) +{ + Profiler usedTime; + auto sm = SessionManager::getInstance(); + if (!sm->isValid(first_name, VALIDATE_NAME)) { + addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(last_name, VALIDATE_NAME)) { + addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(email, VALIDATE_EMAIL)) { + addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); + return false; + } + if (!sm->checkPwdValidation(password, this)) { + return false; + } + /*if (passphrase.size() > 0 && !sm->isValid(passphrase, VALIDATE_PASSPHRASE)) { + addError(new Error("Merkspruch", "Der Merkspruch ist nicht gültig, er besteht aus 24 Wörtern, mit Komma getrennt.")); + return false; + } + if (passphrase.size() == 0) { + //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); + } + else { + //mPassphrase = passphrase; + }*/ + + // check if user with that email already exist + + auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement select(dbConnection); + select << "SELECT email from users where email = ?;", useRef(email); + try { + if (select.execute() > 0) { + addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); + return false; + } + } + catch (Poco::Exception& exc) { + printf("mysql exception: %s\n", exc.displayText().data()); + } + + mSessionUser = new User(email.data(), first_name.data(), last_name.data()); + mNewUser = controller::User::create(email, first_name, last_name); + updateTimeout(); + + // Prepare E-Mail + //UniLib::controller::TaskPtr prepareEmail(new PrepareEmailTask(ServerConfig::g_CPUScheduler)); + //prepareEmail->scheduleTask(prepareEmail); + + // create user crypto key + UniLib::controller::TaskPtr cryptoKeyTask(new UserCreateCryptoKey(mSessionUser, mNewUser, password, ServerConfig::g_CryptoCPUScheduler)); + cryptoKeyTask->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_CRYPTO_KEY_GENERATED, this)); + cryptoKeyTask->scheduleTask(cryptoKeyTask); + + // depends on crypto key, write user record into db + UniLib::controller::TaskPtr writeUserIntoDB(new UserWriteIntoDB(mSessionUser, ServerConfig::g_CPUScheduler, 1)); + writeUserIntoDB->setParentTaskPtrInArray(cryptoKeyTask, 0); + writeUserIntoDB->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_USER_WRITTEN, this)); + writeUserIntoDB->scheduleTask(writeUserIntoDB); + + std::unique_lock _lock(mSharedMutex); + mEmailVerificationCodeObject = controller::EmailVerificationCode::create(model::table::EMAIL_OPT_IN_REGISTER); + UniLib::controller::TaskPtr writeEmailVerification(new WriteEmailVerification(mSessionUser, mEmailVerificationCodeObject, ServerConfig::g_CPUScheduler, 1)); + + writeEmailVerification->setParentTaskPtrInArray(writeUserIntoDB, 0); + writeEmailVerification->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, this)); + writeEmailVerification->scheduleTask(writeEmailVerification); + + + /*printf("LastName: %s\n", last_name.data()); + for (int i = 0; i < last_name.size(); i++) { + char c = last_name.data()[i]; + //printf("%d ", c); + } + //printf("\n\n"); + */ + + // depends on writeUser because need user_id, write email verification into db + /*auto message = new Poco::Net::MailMessage; + Poco::Net::MediaType mt("text", "plain"); + mt.setParameter("charset", "utf-8"); + message->setContentType(mt); + + message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, email)); + message->setSubject(gettext("Gradido: E-Mail Verification")); + std::stringstream ss; + ss << "Hallo " << first_name << " " << last_name << "," << std::endl << std::endl; + ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Gradido registriert. " << std::endl; + ss << "Wenn du es warst, klicke bitte auf den Link: " << ServerConfig::g_serverPath << "/checkEmail/" << mEmailVerificationCode << std::endl; + //ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl; + ss << "oder kopiere den obigen Link in Dein Browserfenster." << std::endl; + ss << std::endl; + ss << "Mit freundlichen " << u8"Grüßen" << std::endl; + ss << "Dario, Gradido Server Admin" << std::endl; + + + message->addContent(new Poco::Net::StringPartSource(ss.str())); + */ + //UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1)); + //Email(AutoPtr emailVerification, AutoPtr user, EmailType type); + UniLib::controller::TaskPtr sendEmail(new SendEmailTask(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_VERIFICATION_CODE), ServerConfig::g_CPUScheduler, 1)); + //sendEmail->setParentTaskPtrInArray(prepareEmail, 0); + sendEmail->setParentTaskPtrInArray(writeEmailVerification, 0); + sendEmail->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_SEND, this)); + sendEmail->scheduleTask(sendEmail); + + // write user into db + // generate and write email verification into db + // send email + + //printf("[Session::createUser] time: %s\n", usedTime.string().data()); + + return true; +} + +bool Session::createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password) +{ + std::unique_lock _lock(mSharedMutex); + static const char* function_name = "Session::createUserDirect"; + auto sm = SessionManager::getInstance(); + auto em = ErrorManager::getInstance(); + auto email_manager = EmailManager::getInstance(); + + if (!sm->isValid(first_name, VALIDATE_NAME)) { + addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(last_name, VALIDATE_NAME)) { + addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(email, VALIDATE_EMAIL)) { + addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); + return false; + } + if (!sm->checkPwdValidation(password, this)) { + return false; + } + + // check if email already exist + auto user = controller::User::create(); + if (user->load(email) >= 1) { + addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits ein Konto")), false); + return false; + } + + // user + mNewUser = controller::User::create(email, first_name, last_name); + auto user_model = mNewUser->getModel(); + user_model->insertIntoDB(true); + auto user_id = user_model->getID(); + + + // one retry in case of connection error + if (!user_id) { + user_model->insertIntoDB(true); + auto user_id = user_model->getID(); + if (!user_id) { + em->addError(new ParamError(function_name, "error saving new user in db, after one retry with email", email)); + em->sendErrorsAsEmail(); + addError(new Error(gettext("Server"), gettext("Fehler beim speichen des Kontos bitte versuche es später noch einmal")), false); + return false; + } + } + + generateKeys(true, true); + + // calculate encryption key, could need some time, will save encrypted privkey to db + UniLib::controller::TaskPtr create_authenticated_encrypten_key = new AuthenticatedEncryptionCreateKeyTask(mNewUser, password); + create_authenticated_encrypten_key->scheduleTask(create_authenticated_encrypten_key); + + // email verification code + auto email_verification = controller::EmailVerificationCode::create(user_id, model::table::EMAIL_OPT_IN_REGISTER_DIRECT); + email_verification->getModel()->insertIntoDB(false); + mEmailVerificationCodeObject = email_verification; + + auto _7days_later = Poco::DateTime() + Poco::Timespan(7, 0, 0, 0, 0); + ServerConfig::g_CronJobsTimer.schedule(new VerificationEmailResendTimerTask(user_id), Poco::Timestamp(_7days_later.timestamp())); + + email_manager->addEmail(new model::Email(email_verification, mNewUser, model::EMAIL_USER_VERIFICATION_CODE)); + + return true; +} + +bool Session::ifUserExist(const std::string& email) +{ + auto em = ErrorManager::getInstance(); + const char* funcName = "Session::ifUserExist"; + auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement select(dbConnection); + bool emailChecked = false; + int userId = 0; + select << "SELECT email_checked, id from users where email = ? and email_checked = 1", + into(emailChecked), into(userId), useRef(email); + + try { + if(select.execute() == 1) return true; + } + catch (Poco::Exception& ex) { + em->addError(new ParamError(funcName, "select user from email verification code mysql error ", ex.displayText().data())); + em->sendErrorsAsEmail(); + } + return false; +} + +int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) +{ + const static char* funcName = "Session::updateEmailVerification"; + Poco::ScopedLock _lock(mWorkMutex); + // new mutex, will replace the Poco Mutex complete in the future + std::unique_lock _lock_shared(mSharedMutex); + Profiler usedTime; + + auto em = ErrorManager::getInstance(); + if (mEmailVerificationCodeObject.isNull()) { + em->addError(new Error(funcName, "email verification object is zero")); + em->sendErrorsAsEmail(); + + return -2; + } + auto email_verification_code_model = mEmailVerificationCodeObject->getModel(); + assert(email_verification_code_model); + if(email_verification_code_model->getCode() == emailVerificationCode) { + if (mSessionUser && mSessionUser->getDBId() == 0) { + //addError(new Error("E-Mail Verification", "Benutzer wurde nicht richtig gespeichert, bitte wende dich an den Server-Admin")); + em->addError(new Error(funcName, "user exist with 0 as id")); + em->sendErrorsAsEmail(); + + //return false; + return -2; + } + + // load correct user from db + if (mNewUser.isNull() || !mNewUser->getModel() || mNewUser->getModel()->getID() != email_verification_code_model->getUserId()) { + mNewUser = controller::User::create(); + if (1 != mNewUser->load(email_verification_code_model->getUserId())) { + em->addError(new ParamError(funcName, "user load didn't return 1 with user_id ", email_verification_code_model->getUserId())); + em->sendErrorsAsEmail(); + + return -2; + } + } + + auto user_model = mNewUser->getModel(); + assert(user_model); + bool first_email_activation = false; + auto verification_type = email_verification_code_model->getType(); + if (model::table::EMAIL_OPT_IN_REGISTER == verification_type || + model::table::EMAIL_OPT_IN_EMPTY == verification_type || + model::table::EMAIL_OPT_IN_REGISTER_DIRECT == verification_type) { + first_email_activation = true; + } + if (first_email_activation && user_model->isEmailChecked()) { + mSessionUser = new User(mNewUser); + addError(new Error(gettext("E-Mail Verification"), gettext("Du hast dein Konto bereits aktiviert!")), false); + + return 1; + } + if (first_email_activation) { + user_model->setEmailChecked(true); + + user_model->updateIntoDB("email_checked", 1); + if (user_model->errorCount() > 0) { + user_model->sendErrorsAsEmail(); + } + + // no find all active sessions + + updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); + return 0; + } + + if (email_verification_code_model->getType() == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { + + if (mEmailVerificationCodeObject->deleteFromDB()) { + mEmailVerificationCodeObject.assign(nullptr); + } + else { + em->getErrors(mEmailVerificationCodeObject->getModel()); + em->addError(new Error(funcName, "error deleting email verification code")); + em->sendErrorsAsEmail(); + return -2; + } + updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); + return 0; + } + + em->addError(new Error(funcName, "invalid code path")); + em->sendErrorsAsEmail(); + + return -2; + + /*if (updated_rows == 1) { + Poco::Data::Statement delete_row(dbConnection); + delete_row << "DELETE FROM email_opt_in where verification_code = ?", use(emailVerificationCode); + if (delete_row.execute() != 1) { + em->addError(new Error(funcName, "delete from email_opt_in entry didn't work as expected, please check db")); + em->sendErrorsAsEmail(); + } + if (mSessionUser) { + mSessionUser->setEmailChecked(); + mSessionUser->setLanguage(getLanguage()); + } + updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); + //printf("[%s] time: %s\n", funcName, usedTime.string().data()); + unlock(); + return true; + } + else { + em->addError(new ParamError(funcName, "update user work not like expected, updated row count", updated_rows)); + em->sendErrorsAsEmail(); + }*/ + + + } + else { + addError(new Error(gettext("E-Mail Verification"), gettext("Falscher Code für aktiven Login"))); + //printf("[%s] time: %s\n", funcName, usedTime.string().data()); + + return -1; + } + //printf("[%s] time: %s\n", funcName, usedTime.string().data()); + + return 0; +} + + +int Session::sendResetPasswordEmail(Poco::AutoPtr user, bool passphraseMemorized) +{ + mNewUser = user; + mSessionUser = new User(user); + auto em = EmailManager::getInstance(); + + std::unique_lock _lock(mSharedMutex); + + // creating email verification code also for user without passphrase + // first check if already exist + // check if email was already send shortly before + bool frequent_resend = false; + bool email_already_send = false; + + mEmailVerificationCodeObject = controller::EmailVerificationCode::load(user->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); + if (mEmailVerificationCodeObject.isNull()) { + mEmailVerificationCodeObject = controller::EmailVerificationCode::create(mNewUser->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); + mEmailVerificationCodeObject->getModel()->insertIntoDB(false); + } + else { + email_already_send = true; + } + auto email_verification_model = mEmailVerificationCodeObject->getModel(); + if (email_already_send) { + auto time_elapsed = Poco::DateTime() - email_verification_model->getUpdated(); + if (time_elapsed.totalHours() < 1) { + frequent_resend = true; + } + } + + if (!frequent_resend) { + if (passphraseMemorized) { + em->addEmail(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_RESET_PASSWORD)); + } + else { + em->addEmail(new model::Email(user, model::EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE)); + } + } + + if (frequent_resend) return 2; + if (email_already_send) return 1; + + return 0; +} + +int Session::comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource) +{ + KeyPair keys; + static const char* functionName = "Session::comparePassphraseWithSavedKeys"; + if (!wordSource) { + addError(new Error(functionName, "wordSource is empty")); + sendErrorsAsEmail(); + return -2; + } + if (!keys.generateFromPassphrase(inputPassphrase.data(), wordSource)) { + addError(new ParamError(functionName, "invalid passphrase", inputPassphrase)); + if (!mNewUser.isNull() && mNewUser->getModel()) { + addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); + } + sendErrorsAsEmail(); + addError(new Error(gettext("Passphrase"), gettext("Deine Passphrase ist ungütig")), false); + return 0; + } + auto userModel = mNewUser->getModel(); + auto existingPublic = userModel->getPublicKey(); + if (!existingPublic) { + userModel->loadFromDB("email", userModel->getEmail()); + existingPublic = userModel->getPublicKey(); + if (!existingPublic) { + addError(new Error(functionName, "cannot load existing public key from db")); + addError(new ParamError(functionName, "user email", userModel->getEmail())); + sendErrorsAsEmail(); + addError(new Error(gettext("Passphrase"), gettext("Ein Fehler trat auf, bitte versuche es erneut")), false); + return -1; + } + } + if (0 == memcmp(userModel->getPublicKey(), keys.getPublicKey(), crypto_sign_PUBLICKEYBYTES)) { + mPassphrase = inputPassphrase; + return 1; + } + addError(new Error(gettext("Passphrase"), gettext("Das ist nicht die richtige Passphrase.")), false); + return 0; +} + +bool Session::startProcessingTransaction(const std::string& proto_message_base64, bool autoSign/* = false*/) +{ + static const char* funcName = "Session::startProcessingTransaction"; + lock(funcName); + HASH hs = ProcessingTransaction::calculateHash(proto_message_base64); + // check if it is already running or waiting + for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { + if (it->isNull()) { + it = mProcessingTransactions.erase(it); + } + if (hs == (*it)->getHash()) { + addError(new Error(funcName, "transaction already in list")); + unlock(); + return false; + } + } + if (mSessionUser.isNull() || !mSessionUser->getEmail()) { + addError(new Error(funcName, "user is zero")); + unlock(); + return false; + } + + Poco::AutoPtr processorTask( + new ProcessingTransaction( + proto_message_base64, + DRMakeStringHash(mSessionUser->getEmail()), + mSessionUser->getLanguage()) + ); + if (autoSign && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) == ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) { + if (processorTask->run() != 0) { + getErrors(processorTask); + unlock(); + return false; + } + Poco::AutoPtr signingTransaction(new SigningTransaction(processorTask, mNewUser)); + //signingTransaction->scheduleTask(signingTransaction); + if (signingTransaction->run() != 0) { + getErrors(signingTransaction); + unlock(); + return false; + } + + } + else { + processorTask->scheduleTask(processorTask); + mProcessingTransactions.push_back(processorTask); + } + unlock(); + return true; + +} + +Poco::AutoPtr Session::getNextReadyTransaction(size_t* working/* = nullptr*/) +{ + lock("Session::getNextReadyTransaction"); + if (working) { + *working = 0; + } + else if (!mCurrentActiveProcessingTransaction.isNull()) + { + unlock(); + return mCurrentActiveProcessingTransaction; + } + for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { + if (working && !(*it)->isTaskFinished()) { + (*working)++; + } + if (mCurrentActiveProcessingTransaction.isNull() && (*it)->isTaskFinished()) { + if (!working) { + mCurrentActiveProcessingTransaction = *it; + unlock(); + return mCurrentActiveProcessingTransaction; + } + // no early exit + else { + mCurrentActiveProcessingTransaction = *it; + } + + } + } + unlock(); + return mCurrentActiveProcessingTransaction; +} + +bool Session::finalizeTransaction(bool sign, bool reject) +{ + int result = -1; + lock("Session::finalizeTransaction"); + if (mCurrentActiveProcessingTransaction.isNull()) { + unlock(); + return false; + } + mProcessingTransactions.remove(mCurrentActiveProcessingTransaction); + + if (!reject) { + if (sign) { + Poco::AutoPtr signingTransaction(new SigningTransaction(mCurrentActiveProcessingTransaction, mNewUser)); + //signingTransaction->scheduleTask(signingTransaction); + result = signingTransaction->run(); + } + } + mCurrentActiveProcessingTransaction.assign(nullptr); + unlock(); + return result == 0; +} + +size_t Session::getProcessingTransactionCount() +{ + size_t count = 0; + lock("Session::getProcessingTransactionCount"); + + for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { + + (*it)->lock(); + if ((*it)->errorCount() > 0) { + (*it)->sendErrorsAsEmail(); + (*it)->unlock(); + it = mProcessingTransactions.erase(it); + if (it == mProcessingTransactions.end()) break; + } + else { + (*it)->unlock(); + } + + } + count = mProcessingTransactions.size(); + unlock(); + return count; +} + +bool Session::isPwdValid(const std::string& pwd) +{ + if (mSessionUser) { + return mSessionUser->validatePwd(pwd, this); + } + return false; +} + +UserStates Session::loadUser(const std::string& email, const std::string& password) +{ + static const char* functionName = "Session::loadUser"; + auto observer = SingletonTaskObserver::getInstance(); + if (email != "") { + if (observer->getTaskCount(email, TASK_OBSERVER_PASSWORD_CREATION) > 0) { + return USER_PASSWORD_ENCRYPTION_IN_PROCESS; + } + } + //Profiler usedTime; + //printf("before lock\n"); + lock(functionName); + //printf("locked \n"); + if (!mSessionUser.isNull() && mSessionUser->getEmail() != email) { + mSessionUser.assign(nullptr); + mNewUser.assign(nullptr); + //printf("user nullptr assigned\n"); + } + //printf("after checking if session user is null\n"); + //if (!mSessionUser) { + if (mNewUser.isNull()) { + //printf("new user is null\n"); + mNewUser = controller::User::create(); + //printf("new user created\n"); + // load user for email only once from db + mNewUser->load(email); + //printf("load new user from db with email: %s\n", email.data()); + mSessionUser = new User(mNewUser); + //mSessionUser = new User(email.data()); + + //printf("user loaded from email\n"); + } + //printf("before get model\n"); + auto user_model = mNewUser->getModel(); + if (user_model && user_model->isDisabled()) { + return USER_DISABLED; + } + //printf("before if login\n"); + if (!mSessionUser.isNull() && mSessionUser->getUserState() >= USER_LOADED_FROM_DB) { + //printf("before login\n"); + int loginResult = 0; + int exitCount = 0; + do { + loginResult = mNewUser->login(password); + Poco::Thread::sleep(100); + exitCount++; + } while (-3 == loginResult && exitCount < 15); + if (exitCount > 1) { + addError(new ParamError(functionName, "login succeed, retrys: ", exitCount)); + addError(new ParamError(functionName, "email: ", email)); + sendErrorsAsEmail(); + } + + if (exitCount >= 15) + { + auto running_password_creations = observer->getTasksCount(TASK_OBSERVER_PASSWORD_CREATION); + + addError(new ParamError(functionName, "login failed after 15 retrys and 100 ms sleep between, currently running passwort creation tasks: ", running_password_creations)); + addError(new ParamError(functionName, "email: ", email)); + sendErrorsAsEmail(); + return USER_PASSWORD_ENCRYPTION_IN_PROCESS; + } + + //printf("new user login with result: %d\n", loginResult); + + if (-1 == loginResult) { + addError(new Error(functionName, "error in user data set, saved pubkey didn't match extracted pubkey from private key")); + addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); + sendErrorsAsEmail(); + //unlock(); + //return USER_KEYS_DONT_MATCH; + } + if (0 == loginResult) { + unlock(); + return USER_PASSWORD_INCORRECT; + } + // error decrypting private key + if (-2 == loginResult) { + // check if we have access to the passphrase, if so we can reencrypt the private key + printf("try reencrypting key\n"); + auto user_model = mNewUser->getModel(); + auto user_backups = controller::UserBackups::load(user_model->getID()); + for (auto it = user_backups.begin(); it != user_backups.end(); it++) { + auto key = std::unique_ptr((*it)->createGradidoKeyPair()); + if (key->isTheSame(user_model->getPublicKey())) + { + + // set valid key pair + if (1 == mNewUser->setGradidoKeyPair(key.release())) { + // save new encrypted private key + user_model->updatePrivkey(); + } + else { + auto em = ErrorManager::getInstance(); + em->addError(new Error(functionName, "error reencrypt private key")); + em->addError(new ParamError(functionName, "for user with email", user_model->getEmail())); + em->sendErrorsAsEmail(); + } + break; + } + } + } + // can be removed if session user isn't used any more + // don't calculate password two times anymore + mSessionUser->login(mNewUser); + //printf("after old user login\n"); + /*if (mNewUser->getModel()->getPasswordHashed() && !mSessionUser->validatePwd(password, this)) { + unlock(); + return USER_PASSWORD_INCORRECT; + }*/ + } + else { + //printf("before sleep\n"); + User::fakeCreateCryptoKey(); + } + + /*if (!mSessionUser->validatePwd(password, this)) { + addError(new Error("Login", "E-Mail oder Passwort nicht korrekt, bitte versuche es erneut!")); + unlock(); + return false; + } + if (!mSessionUser->isEmailChecked()) { + addError(new Error("Account", "E-Mail Adresse wurde noch nicht bestätigt, hast du schon eine E-Mail erhalten?")); + unlock(); + return false; + }*/ + //printf("before detect session state\n"); + detectSessionState(); + unlock(); + //printf("before return user state\n"); + return mSessionUser->getUserState(); +} + +bool Session::deleteUser() +{ + lock("Session::deleteUser"); + bool bResult = false; + if(mSessionUser) { + JsonRequest phpServerRequest(ServerConfig::g_php_serverHost, 443); + Poco::Net::NameValueCollection payload; + payload.add("user", std::string(mSessionUser->getPublicKeyHex())); + //auto ret = phpServerRequest.request("userDelete", payload); + JsonRequestReturn ret = JSON_REQUEST_RETURN_OK; + if (ret == JSON_REQUEST_RETURN_ERROR) { + addError(new Error("Session::deleteUser", "php server error")); + getErrors(&phpServerRequest); + sendErrorsAsEmail(); + } + else if (ret == JSON_REQUEST_RETURN_OK) { + bResult = mSessionUser->deleteFromDB(); + } + else { + addError(new Error(gettext("Benutzer"), gettext("Konnte Community Server nicht erreichen. E-Mail an den Admin ist raus."))); + unlock(); + return false; + } + } + if(!bResult) { + addError(new Error(gettext("Benutzer"), gettext("Fehler beim Löschen des Accounts. Bitte logge dich erneut ein und versuche es nochmal."))); + } + unlock(); + return bResult; +} + +void Session::setLanguage(Languages lang) +{ + //printf("[Session::setLanguage] new language: %d\n", lang); + lock("Session::setLanguage"); + if (mLanguageCatalog.isNull() || mLanguageCatalog->getLanguage() != lang) { + auto lm = LanguageManager::getInstance(); + mLanguageCatalog = lm->getFreeCatalog(lang); + } + unlock(); +} + +Languages Session::getLanguage() +{ + Languages lang = LANG_NULL; + lock("Session::getLanguage"); + if (!mLanguageCatalog.isNull()) { + lang = mLanguageCatalog->getLanguage(); + } + unlock(); + return lang; +} + + +/* +SESSION_STATE_CRYPTO_KEY_GENERATED, +SESSION_STATE_USER_WRITTEN, +SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, +SESSION_STATE_EMAIL_VERIFICATION_SEND, +SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED, +SESSION_STATE_PASSPHRASE_GENERATED, +SESSION_STATE_PASSPHRASE_SHOWN, +SESSION_STATE_PASSPHRASE_WRITTEN, +SESSION_STATE_KEY_PAIR_GENERATED, +SESSION_STATE_KEY_PAIR_WRITTEN, +SESSION_STATE_COUNT +*/ +void Session::detectSessionState() +{ + if (mSessionUser.isNull() || !mSessionUser->hasCryptoKey()) { + return; + } + UserStates userState = mSessionUser->getUserState(); + + int checkEmail = -1, resetPasswd = -1; + try { + auto emailVerificationCodeObjects = controller::EmailVerificationCode::load(mSessionUser->getDBId()); + + for (int i = 0; i < emailVerificationCodeObjects.size(); i++) { + auto type = emailVerificationCodeObjects[i]->getModel()->getType(); + if (type == model::table::EMAIL_OPT_IN_EMPTY || type == model::table::EMAIL_OPT_IN_REGISTER) { + checkEmail = i; + } + else if (type == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { + resetPasswd = i; + } + } + std::unique_lock _lock_shared(mSharedMutex); + if (resetPasswd != -1) { + mEmailVerificationCodeObject = emailVerificationCodeObjects[resetPasswd]; + } + else if (checkEmail != -1) { + mEmailVerificationCodeObject = emailVerificationCodeObjects[checkEmail]; + } + + } + catch (Poco::Exception& ex) { + printf("[Session::detectSessionState] exception: %s\n", ex.displayText().data()); + //return; + } + + if (userState <= USER_EMAIL_NOT_ACTIVATED) { + + if (checkEmail != -1) { + updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); + return; + } + + updateState(SESSION_STATE_USER_WRITTEN); + return; + } + + if (USER_NO_KEYS == userState) { + + auto user_id = mSessionUser->getDBId(); + auto userBackups = controller::UserBackups::load(user_id); + + // check passphrase, only possible while passphrase isn't crypted in db + bool correctPassphraseFound = false; + // always trigger SESSION_STATE_PASSPHRASE_WRITTEN, else lost of data possible + bool cryptedPassphrase = userBackups.size() > 0; + for (auto it = userBackups.begin(); it != userBackups.end(); it++) { + KeyPair keys; + auto passphrase = (*it)->getModel()->getPassphrase(); + Mnemonic* wordSource = nullptr; + if (User::validatePassphrase(passphrase, &wordSource)) { + if (keys.generateFromPassphrase((*it)->getModel()->getPassphrase().data(), wordSource)) { + if (sodium_memcmp(mSessionUser->getPublicKey(), keys.getPublicKey(), ed25519_pubkey_SIZE) == 0) { + correctPassphraseFound = true; + break; + } + } + } + else { + cryptedPassphrase = true; + } + } + /* + auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement select(dbConnection); + Poco::Nullable passphrase; + + select << "SELECT passphrase from user_backups where user_id = ?;", + into(passphrase), use(user_id); + try { + if (select.execute() == 1 && !passphrase.isNull()) { + //KeyPair keys;keys.generateFromPassphrase(passphrase.value().rawContent()) + updateState(SESSION_STATE_PASSPHRASE_WRITTEN); + return; + } + } + catch (Poco::Exception& exc) { + printf("[Session::detectSessionState] 2 mysql exception: %s\n", exc.displayText().data()); + }*/ + if (correctPassphraseFound || cryptedPassphrase) { + updateState(SESSION_STATE_PASSPHRASE_WRITTEN); + return; + } + if (mPassphrase != "") { + updateState(SESSION_STATE_PASSPHRASE_GENERATED); + return; + } + updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); + return; + } + + updateState(SESSION_STATE_KEY_PAIR_WRITTEN); + + if (resetPasswd != -1) { + // don't go to reset password screen after login, only throw checkEmail + //updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); + return; + } + +} + +Poco::Net::HTTPCookie Session::getLoginCookie() +{ + auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", std::to_string(mHandleId)); + // prevent reading or changing cookie with js +// keks.setHttpOnly(); + + keks.setPath("/"); + // send cookie only via https, on linux, except in test builds +#ifndef WIN32 + if (ServerConfig::SERVER_TYPE_PRODUCTION == ServerConfig::g_ServerSetupType || + ServerConfig::SERVER_TYPE_STAGING == ServerConfig::g_ServerSetupType) { + keks.setSecure(true); + } +#endif + + return keks; +} + +bool Session::loadFromEmailVerificationCode(Poco::UInt64 emailVerificationCode) +{ + Profiler usedTime; + auto em = ErrorManager::getInstance(); + std::unique_lock _lock(mSharedMutex); + mEmailVerificationCodeObject = controller::EmailVerificationCode::load(emailVerificationCode); + if (mEmailVerificationCodeObject.isNull()) { + addError(new Error(gettext("E-Mail Verification"), gettext("Konnte kein passendes Konto finden."))); + return false; + } + + mNewUser = controller::User::create(); + assert(mEmailVerificationCodeObject->getModel() && mEmailVerificationCodeObject->getModel()->getUserId()); + mNewUser->load(mEmailVerificationCodeObject->getModel()->getUserId()); + if (mNewUser->getModel()->errorCount() > 0) { + mNewUser->getModel()->sendErrorsAsEmail(); + addError(new Error(gettext("E-Mail Verification"), gettext("Fehler beim laden des Benutzers."))); + return false; + } + mSessionUser = new User(mNewUser); + mSessionUser->setLanguage(getLanguage()); + + auto verificationType = mEmailVerificationCodeObject->getModel()->getType(); + if (verificationType == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { + updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); + } + else { + updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); + } + + return true; +} + +void Session::updateState(SessionStates newState) +{ + lock("Session::updateState"); + if (!mActive) { + unlock(); + return; + } + updateTimeout(); + //printf("[%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState)); + if (newState > mState) { + mState = newState; + } + + unlock(); +} + +const char* Session::getSessionStateString() +{ + SessionStates state; + lock("Session::getSessionStateString"); + state = mState; + unlock(); + return translateSessionStateToString(state); +} + + +const char* Session::translateSessionStateToString(SessionStates state) +{ + switch (state) { + case SESSION_STATE_EMPTY: return "uninitalized"; + case SESSION_STATE_CRYPTO_KEY_GENERATED: return "crpyto key generated"; + case SESSION_STATE_USER_WRITTEN: return "User saved"; + case SESSION_STATE_EMAIL_VERIFICATION_WRITTEN: return "E-Mail verification code saved"; + case SESSION_STATE_EMAIL_VERIFICATION_SEND: return "Verification E-Mail sended"; + case SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED: return "Verification Code checked"; + case SESSION_STATE_PASSPHRASE_GENERATED: return "Passphrase generated"; + case SESSION_STATE_PASSPHRASE_SHOWN: return "Passphrase shown"; + case SESSION_STATE_PASSPHRASE_WRITTEN: return "Passphrase written"; + case SESSION_STATE_KEY_PAIR_GENERATED: return "Gradido Address created"; + case SESSION_STATE_KEY_PAIR_WRITTEN: return "Gradido Address saved"; + case SESSION_STATE_RESET_PASSWORD_REQUEST: return "Passwort reset requested"; + case SESSION_STATE_RESET_PASSWORD_SUCCEED: return "Passwort reset succeeded"; + default: return "unknown"; + } + + return "error"; +} + + +/* +bool Session::useOrGeneratePassphrase(const std::string& passphase) +{ + if (passphase != "" && User::validatePassphrase(passphase)) { + // passphrase is valid + setPassphrase(passphase); + updateState(SESSION_STATE_PASSPHRASE_SHOWN); + return true; + } + else { + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); + updateState(SESSION_STATE_PASSPHRASE_GENERATED); + return true; + } +} +*/ +bool Session::generatePassphrase() +{ + if (mNewUser.isNull()) return false; + + auto lang = getLanguage(); + if (lang == LANG_EN) { + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); + } + else { + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); + } + //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); + updateState(SESSION_STATE_PASSPHRASE_GENERATED); + return true; +} + +bool Session::generateKeys(bool savePrivkey, bool savePassphrase) +{ + if (mNewUser.isNull()) { + addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); + return false; + } + static const char* function_name = "Session::generateKeys"; + auto lang = getLanguage(); + auto user_model = mNewUser->getModel(); + auto mnemonic_type = ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; + /*if (LANG_DE == lang) { + mnemonic_type = ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES; + }*/ + + auto passphrase = Passphrase::generate(&ServerConfig::g_Mnemonic_WordLists[mnemonic_type]); + if (!passphrase) { + addError(new ParamError(function_name, "Error generating passphrase with mnemonic: ", mnemonic_type)); + addError(new ParamError(function_name, "user email: ", mNewUser->getModel()->getEmail())); + sendErrorsAsEmail(); + addError(new Error(gettext("Benutzer"), gettext("Fehler beim generieren der Passphrase, der Admin bekommt eine E-Mail. "))); + return false; + } + + if (savePassphrase) { + auto user_backup = controller::UserBackups::create(user_model->getID(), passphrase->getString(), mnemonic_type); + // sync version + //user_backup->getModel()->insertIntoDB(false); + + // async version + UniLib::controller::TaskPtr save_user_backup_task = new model::table::ModelInsertTask(user_backup->getModel(), false, true); + save_user_backup_task->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); + save_user_backup_task->scheduleTask(save_user_backup_task); + } + + // keys + auto gradido_key_pair = KeyPairEd25519::create(passphrase); + auto set_key_result = mNewUser->setGradidoKeyPair(gradido_key_pair); + size_t result_save_key = 0; + if (1 == set_key_result && savePrivkey) { + // save public key and private key in db + result_save_key = user_model->updatePubkeyAndPrivkey(); + } + else { + // save public key in db + result_save_key = user_model->updatePublickey(); + } + if (!result_save_key) { + user_model->addError(new Error(function_name, "Error saving new generated pubkey")); + user_model->addError(new ParamError(function_name, "e-mail: ", user_model->getEmail())); + user_model->sendErrorsAsEmail(); + //addError(new Error(gettext("Benutzer"), gettext("Fehler beim Speichern der Keys, der Admin bekommt eine E-Mail. Evt. nochmal versuchen oder abwarten!"))); + return false; + } + return true; + /* + + bool validUser = true; + if (mSessionUser) { + if (!mSessionUser->generateKeys(savePrivkey, mPassphrase, this)) { + validUser = false; + } + else { + if (savePassphrase) { + //printf("[Session::generateKeys] create save passphrase task\n"); + UniLib::controller::TaskPtr savePassphrase(new WritePassphraseIntoDB(mSessionUser->getDBId(), mPassphrase)); + savePassphrase->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); + savePassphrase->scheduleTask(savePassphrase); + } + } + } + else { + validUser = false; + } + if (!validUser) { + addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); + return false; + } + // delete passphrase after all went well + mPassphrase.clear(); + + return true; + */ +} diff --git a/login_server/src/cpp/model/Session.h b/login_server/src/cpp/model/Session.h index ec60b6fc3..e10beef89 100644 --- a/login_server/src/cpp/model/Session.h +++ b/login_server/src/cpp/model/Session.h @@ -1,282 +1,282 @@ -/*! -* -* \author: einhornimmond -* -* \date: 02.03.19 -* -* \brief: store session data -*/ - -#ifndef DR_LUA_WEB_MODULE_SESSION_SESSION_H -#define DR_LUA_WEB_MODULE_SESSION_SESSION_H - -#include "../lib/ErrorList.h" -#include "User.h" -#include "../controller/User.h" - -#include "../lib/MultithreadContainer.h" -#include "../tasks/ProcessingTransaction.h" - -#include "../SingletonManager/LanguageManager.h" - -#include "../controller/EmailVerificationCode.h" - -#include "Poco/Thread.h" -#include "Poco/Types.h" -#include "Poco/DateTime.h" -#include "Poco/Net/IPAddress.h" -#include "Poco/Net/HTTPCookie.h" - -#include - - -class WriteEmailVerification; - -enum SessionStates { - SESSION_STATE_EMPTY, - SESSION_STATE_CRYPTO_KEY_GENERATED, - SESSION_STATE_USER_WRITTEN, - SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, - SESSION_STATE_EMAIL_VERIFICATION_SEND, - SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED, - SESSION_STATE_PASSPHRASE_GENERATED, - SESSION_STATE_PASSPHRASE_SHOWN, - SESSION_STATE_PASSPHRASE_WRITTEN, - SESSION_STATE_KEY_PAIR_GENERATED, - SESSION_STATE_KEY_PAIR_WRITTEN, - SESSION_STATE_RESET_PASSWORD_REQUEST, - SESSION_STATE_RESET_PASSWORD_SUCCEED, - SESSION_STATE_COUNT -}; - -class SessionManager; - -class UpdateUserPasswordPage; -class PassphrasePage; -class RepairDefectPassphrase; - -class Session : public ErrorList, public UniLib::lib::MultithreadContainer -{ - friend WriteEmailVerification; - friend SessionManager; - friend UpdateUserPasswordPage; - friend PassphrasePage; - friend RepairDefectPassphrase; -public: - Session(int handle); - ~Session(); - - // get new model objects - Poco::AutoPtr getEmailVerificationCodeObject(); - - // set new model objects - inline void setUser(Poco::AutoPtr user) { mNewUser = user; } - inline Poco::AutoPtr getNewUser() { return mNewUser; } - - // ---------------- User functions ---------------------------- - // TODO: register state: written into db, mails sended, update state only if new state is higher as old state - // create User send e-mail activation link - bool createUser(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); - - //! \brief new register function, without showing user pubkeys, using controller/user - bool createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); - - - // adminRegister without passwort - bool adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email); - - // TODO: check if email exist and if not, fake waiting on password hashing with profiled times of real password hashing - UserStates loadUser(const std::string& email, const std::string& password); - bool ifUserExist(const std::string& email); - - inline void setUser(Poco::AutoPtr user) { mSessionUser = user; } - - - bool deleteUser(); - - Poco::AutoPtr getUser() { - return mSessionUser; - } - - // ------------------------- Email Verification Code functions ------------------------------- - - bool loadFromEmailVerificationCode(Poco::UInt64 emailVerificationCode); - - //! \return 1 = konto already exist - //! -1 = invalid code - //! -2 = critical error - //! 0 = ok - int updateEmailVerification(Poco::UInt64 emailVerificationCode); - - // called from page with same name - //! \return 1 = reset password email already send - //! \return 2 = reset password email already shortly before - //! \return 0 = ok - int sendResetPasswordEmail(Poco::AutoPtr user, bool passphraseMemorized); - // - //! \return 0 = not the same - //! \return 1 = same - //! \return -1 = error - //! \return -2 = critical error - int comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource); - - Poco::Net::HTTPCookie getLoginCookie(); - - - inline int getHandle() { return mHandleId; } - - // ------------------------ Passphrase functions ---------------------------- - - inline void setPassphrase(Poco::AutoPtr passphrase) { mNewPassphrase = passphrase; } - inline Poco::AutoPtr getPassphrase() { return mNewPassphrase; } - - inline void setPassphrase(const std::string& passphrase) { mPassphrase = passphrase; } - - inline const std::string& getOldPassphrase() { return mPassphrase; } - bool generatePassphrase(); - bool generateKeys(bool savePrivkey, bool savePassphrase); - - inline void setClientIp(Poco::Net::IPAddress ip) { mClientLoginIP = ip; } - inline Poco::Net::IPAddress getClientIp() { return mClientLoginIP; } - - inline bool isIPValid(Poco::Net::IPAddress ip) { return mClientLoginIP == ip; } - bool isPwdValid(const std::string& pwd); - void reset(); - - void updateState(SessionStates newState); - const char* getSessionStateString(); - inline SessionStates getSessionState() { SessionStates s; lock("Session::getSessionState"); s = mState; unlock(); return s; } - - inline Poco::UInt64 getEmailVerificationCode() { - std::shared_lock _lock(mSharedMutex); - if (mEmailVerificationCodeObject.isNull()) return 0; return mEmailVerificationCodeObject->getModel()->getCode(); - } - inline void setEmailVerificationCodeObject(Poco::AutoPtr emailVerficationObject) { - std::unique_lock _lock(mSharedMutex); - mEmailVerificationCodeObject = emailVerficationObject; - } - inline model::table::EmailOptInType getEmailVerificationType() { - std::shared_lock _lock(mSharedMutex); - if (mEmailVerificationCodeObject.isNull()) { - return model::table::EMAIL_OPT_IN_EMPTY; - } - return mEmailVerificationCodeObject->getModel()->getType(); - } - - //! \return -1 if session is locked - //! \return 1 if session is active - //! \return 0 - int isActive(); - //! \return false if session is locked - bool setActive(bool active); - - bool isDeadLocked(); - - inline Poco::DateTime getLastActivity() { return mLastActivity; } - - // ------------------------ transactions functions ---------------------------- - - //! \return true if succeed - bool startProcessingTransaction(const std::string& proto_message_base64, bool autoSign = false); - //! \param working if set will filled with transaction running - Poco::AutoPtr getNextReadyTransaction(size_t* working = nullptr); - bool finalizeTransaction(bool sign, bool reject); - size_t getProcessingTransactionCount(); - - inline LanguageCatalog* getLanguageCatalog() { return mLanguageCatalog.isNull() ? nullptr : mLanguageCatalog; } - void setLanguage(Languages lang); - inline void setLanguageCatalog(Poco::AutoPtr languageCatalog) { mLanguageCatalog = languageCatalog; } - Languages getLanguage(); - inline const char* gettext(const char* text) { if (mLanguageCatalog.isNull()) return text; return mLanguageCatalog->gettext(text); } - - // last referer - inline void setLastReferer(const std::string& lastReferer) { mLastExternReferer = lastReferer; } - inline const std::string& getLastReferer() const { return mLastExternReferer; } - -protected: - void updateTimeout(); - inline void setHandle(int newHandle) { mHandleId = newHandle; } - - void detectSessionState(); - static const char* translateSessionStateToString(SessionStates state); - - inline const std::string& getPassphrase() const { return mPassphrase; } - - -private: - int mHandleId; - Poco::AutoPtr mSessionUser; - Poco::AutoPtr mNewUser; - std::string mPassphrase; - Poco::AutoPtr mNewPassphrase; - Poco::DateTime mLastActivity; - Poco::Net::IPAddress mClientLoginIP; - std::string mLastExternReferer; - Poco::AutoPtr mEmailVerificationCodeObject; - std::shared_mutex mSharedMutex; - - - SessionStates mState; - - bool mActive; - std::list> mProcessingTransactions; - Poco::AutoPtr mCurrentActiveProcessingTransaction; - - Poco::AutoPtr mLanguageCatalog; -}; - - -class WriteEmailVerification : public UniLib::controller::CPUTask -{ -public: - WriteEmailVerification(Poco::AutoPtr user, Poco::AutoPtr emailVerificationCode, UniLib::controller::CPUSheduler* cpuScheduler, size_t taskDependenceCount = 0) - : UniLib::controller::CPUTask(cpuScheduler, taskDependenceCount), mUser(user), mEmailVerificationCode(emailVerificationCode) { -#ifdef _UNI_LIB_DEBUG - setName(user->getEmail()); -#endif - } - - virtual const char* getResourceType() const { return "WriteEmailVerification"; }; - virtual int run(); - -private: - Poco::AutoPtr mUser; - Poco::AutoPtr mEmailVerificationCode; - -}; - -class WritePassphraseIntoDB : public UniLib::controller::CPUTask -{ -public: - WritePassphraseIntoDB(int userId, const std::string& passphrase) - : mUserId(userId), mPassphrase(passphrase) { -#ifdef _UNI_LIB_DEBUG - setName(std::to_string(userId).data()); -#endif - } - - - virtual int run(); - virtual const char* getResourceType() const { return "WritePassphraseIntoDB"; }; - -protected: - int mUserId; - std::string mPassphrase; -}; - -class SessionStateUpdateCommand : public UniLib::controller::Command -{ -public: - SessionStateUpdateCommand(SessionStates state, Session* session) - : mState(state), mSession(session) {} - virtual int taskFinished(UniLib::controller::Task* task) { - mSession->updateState(mState); - return 0; - } - -protected: - SessionStates mState; - Session* mSession; -}; - -#endif // DR_LUA_WEB_MODULE_SESSION_SESSION_H +/*! +* +* \author: einhornimmond +* +* \date: 02.03.19 +* +* \brief: store session data +*/ + +#ifndef DR_LUA_WEB_MODULE_SESSION_SESSION_H +#define DR_LUA_WEB_MODULE_SESSION_SESSION_H + +#include "../lib/ErrorList.h" +#include "User.h" +#include "../controller/User.h" + +#include "../lib/MultithreadContainer.h" +#include "../tasks/ProcessingTransaction.h" + +#include "../SingletonManager/LanguageManager.h" + +#include "../controller/EmailVerificationCode.h" + +#include "Poco/Thread.h" +#include "Poco/Types.h" +#include "Poco/DateTime.h" +#include "Poco/Net/IPAddress.h" +#include "Poco/Net/HTTPCookie.h" + +#include + + +class WriteEmailVerification; + +enum SessionStates { + SESSION_STATE_EMPTY, + SESSION_STATE_CRYPTO_KEY_GENERATED, + SESSION_STATE_USER_WRITTEN, + SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, + SESSION_STATE_EMAIL_VERIFICATION_SEND, + SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED, + SESSION_STATE_PASSPHRASE_GENERATED, + SESSION_STATE_PASSPHRASE_SHOWN, + SESSION_STATE_PASSPHRASE_WRITTEN, + SESSION_STATE_KEY_PAIR_GENERATED, + SESSION_STATE_KEY_PAIR_WRITTEN, + SESSION_STATE_RESET_PASSWORD_REQUEST, + SESSION_STATE_RESET_PASSWORD_SUCCEED, + SESSION_STATE_COUNT +}; + +class SessionManager; + +class UpdateUserPasswordPage; +class PassphrasePage; +class RepairDefectPassphrase; + +class Session : public ErrorList, public UniLib::lib::MultithreadContainer +{ + friend WriteEmailVerification; + friend SessionManager; + friend UpdateUserPasswordPage; + friend PassphrasePage; + friend RepairDefectPassphrase; +public: + Session(int handle); + ~Session(); + + // get new model objects + Poco::AutoPtr getEmailVerificationCodeObject(); + + // set new model objects + inline void setUser(Poco::AutoPtr user) { mNewUser = user; } + inline Poco::AutoPtr getNewUser() { return mNewUser; } + + // ---------------- User functions ---------------------------- + // TODO: register state: written into db, mails sended, update state only if new state is higher as old state + // create User send e-mail activation link + bool createUser(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); + + //! \brief new register function, without showing user pubkeys, using controller/user + bool createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); + + + // adminRegister without passwort + bool adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email); + + // TODO: check if email exist and if not, fake waiting on password hashing with profiled times of real password hashing + UserStates loadUser(const std::string& email, const std::string& password); + bool ifUserExist(const std::string& email); + + inline void setUser(Poco::AutoPtr user) { mSessionUser = user; } + + + bool deleteUser(); + + Poco::AutoPtr getUser() { + return mSessionUser; + } + + // ------------------------- Email Verification Code functions ------------------------------- + + bool loadFromEmailVerificationCode(Poco::UInt64 emailVerificationCode); + + //! \return 1 = konto already exist + //! -1 = invalid code + //! -2 = critical error + //! 0 = ok + int updateEmailVerification(Poco::UInt64 emailVerificationCode); + + // called from page with same name + //! \return 1 = reset password email already send + //! \return 2 = reset password email already shortly before + //! \return 0 = ok + int sendResetPasswordEmail(Poco::AutoPtr user, bool passphraseMemorized); + // + //! \return 0 = not the same + //! \return 1 = same + //! \return -1 = error + //! \return -2 = critical error + int comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource); + + Poco::Net::HTTPCookie getLoginCookie(); + + + inline int getHandle() { return mHandleId; } + + // ------------------------ Passphrase functions ---------------------------- + + inline void setPassphrase(Poco::AutoPtr passphrase) { mNewPassphrase = passphrase; } + inline Poco::AutoPtr getPassphrase() { return mNewPassphrase; } + + inline void setPassphrase(const std::string& passphrase) { mPassphrase = passphrase; } + + inline const std::string& getOldPassphrase() { return mPassphrase; } + bool generatePassphrase(); + bool generateKeys(bool savePrivkey, bool savePassphrase); + + inline void setClientIp(Poco::Net::IPAddress ip) { mClientLoginIP = ip; } + inline Poco::Net::IPAddress getClientIp() { return mClientLoginIP; } + + inline bool isIPValid(Poco::Net::IPAddress ip) { return mClientLoginIP == ip; } + bool isPwdValid(const std::string& pwd); + void reset(); + + void updateState(SessionStates newState); + const char* getSessionStateString(); + inline SessionStates getSessionState() { SessionStates s; lock("Session::getSessionState"); s = mState; unlock(); return s; } + + inline Poco::UInt64 getEmailVerificationCode() { + std::shared_lock _lock(mSharedMutex); + if (mEmailVerificationCodeObject.isNull()) return 0; return mEmailVerificationCodeObject->getModel()->getCode(); + } + inline void setEmailVerificationCodeObject(Poco::AutoPtr emailVerficationObject) { + std::unique_lock _lock(mSharedMutex); + mEmailVerificationCodeObject = emailVerficationObject; + } + inline model::table::EmailOptInType getEmailVerificationType() { + std::shared_lock _lock(mSharedMutex); + if (mEmailVerificationCodeObject.isNull()) { + return model::table::EMAIL_OPT_IN_EMPTY; + } + return mEmailVerificationCodeObject->getModel()->getType(); + } + + //! \return -1 if session is locked + //! \return 1 if session is active + //! \return 0 + int isActive(); + //! \return false if session is locked + bool setActive(bool active); + + bool isDeadLocked(); + + inline Poco::DateTime getLastActivity() { return mLastActivity; } + + // ------------------------ transactions functions ---------------------------- + + //! \return true if succeed + bool startProcessingTransaction(const std::string& proto_message_base64, bool autoSign = false); + //! \param working if set will filled with transaction running + Poco::AutoPtr getNextReadyTransaction(size_t* working = nullptr); + bool finalizeTransaction(bool sign, bool reject); + size_t getProcessingTransactionCount(); + + inline LanguageCatalog* getLanguageCatalog() { return mLanguageCatalog.isNull() ? nullptr : mLanguageCatalog; } + void setLanguage(Languages lang); + inline void setLanguageCatalog(Poco::AutoPtr languageCatalog) { mLanguageCatalog = languageCatalog; } + Languages getLanguage(); + inline const char* gettext(const char* text) { if (mLanguageCatalog.isNull()) return text; return mLanguageCatalog->gettext(text); } + + // last referer + inline void setLastReferer(const std::string& lastReferer) { mLastExternReferer = lastReferer; } + inline const std::string& getLastReferer() const { return mLastExternReferer; } + +protected: + void updateTimeout(); + inline void setHandle(int newHandle) { mHandleId = newHandle; } + + void detectSessionState(); + static const char* translateSessionStateToString(SessionStates state); + + inline const std::string& getPassphrase() const { return mPassphrase; } + + +private: + int mHandleId; + Poco::AutoPtr mSessionUser; + Poco::AutoPtr mNewUser; + std::string mPassphrase; + Poco::AutoPtr mNewPassphrase; + Poco::DateTime mLastActivity; + Poco::Net::IPAddress mClientLoginIP; + std::string mLastExternReferer; + Poco::AutoPtr mEmailVerificationCodeObject; + std::shared_mutex mSharedMutex; + + + SessionStates mState; + + bool mActive; + std::list> mProcessingTransactions; + Poco::AutoPtr mCurrentActiveProcessingTransaction; + + Poco::AutoPtr mLanguageCatalog; +}; + + +class WriteEmailVerification : public UniLib::controller::CPUTask +{ +public: + WriteEmailVerification(Poco::AutoPtr user, Poco::AutoPtr emailVerificationCode, UniLib::controller::CPUSheduler* cpuScheduler, size_t taskDependenceCount = 0) + : UniLib::controller::CPUTask(cpuScheduler, taskDependenceCount), mUser(user), mEmailVerificationCode(emailVerificationCode) { +#ifdef _UNI_LIB_DEBUG + setName(user->getEmail()); +#endif + } + + virtual const char* getResourceType() const { return "WriteEmailVerification"; }; + virtual int run(); + +private: + Poco::AutoPtr mUser; + Poco::AutoPtr mEmailVerificationCode; + +}; + +class WritePassphraseIntoDB : public UniLib::controller::CPUTask +{ +public: + WritePassphraseIntoDB(int userId, const std::string& passphrase) + : mUserId(userId), mPassphrase(passphrase) { +#ifdef _UNI_LIB_DEBUG + setName(std::to_string(userId).data()); +#endif + } + + + virtual int run(); + virtual const char* getResourceType() const { return "WritePassphraseIntoDB"; }; + +protected: + int mUserId; + std::string mPassphrase; +}; + +class SessionStateUpdateCommand : public UniLib::controller::Command +{ +public: + SessionStateUpdateCommand(SessionStates state, Session* session) + : mState(state), mSession(session) {} + virtual int taskFinished(UniLib::controller::Task* task) { + mSession->updateState(mState); + return 0; + } + +protected: + SessionStates mState; + Session* mSession; +}; + +#endif // DR_LUA_WEB_MODULE_SESSION_SESSION_H diff --git a/login_server/src/cpp/model/TransactionCreation.cpp b/login_server/src/cpp/model/TransactionCreation.cpp index a04c424c7..6e8e38369 100644 --- a/login_server/src/cpp/model/TransactionCreation.cpp +++ b/login_server/src/cpp/model/TransactionCreation.cpp @@ -1,72 +1,72 @@ -#include "TransactionCreation.h" -#include "Poco/DateTimeFormatter.h" -#include - -TransactionCreation::TransactionCreation(const std::string& memo, const model::messages::gradido::TransactionCreation& protoCreation) - : TransactionBase(memo), mProtoCreation(protoCreation), mReceiverUser(nullptr) -{ - memset(mReceiverPublicHex, 0, 65); -} - -TransactionCreation::~TransactionCreation() -{ - if (mReceiverUser) { - delete mReceiverUser; - mReceiverUser = nullptr; - } -} - -int TransactionCreation::prepare() -{ - const static char functionName[] = { "TransactionCreation::prepare" }; - if (!mProtoCreation.has_receiveramount()) { - addError(new Error(functionName, "hasn't receiver amount")); - return -1; - } - auto receiverAmount = mProtoCreation.receiveramount(); - - if (receiverAmount.amount() <= 0) { - addError(new Error(functionName, "amount must be > 0")); - return -4; - } - if (receiverAmount.amount() > 10000000) { - addError(new Error(functionName, "amount must be <= 1000 GDD")); - return -5; - } - - auto receiverPublic = receiverAmount.ed25519_receiver_pubkey(); - if (receiverPublic.size() != 32) { - addError(new Error(functionName, "receiver public invalid (size not 32)")); - return -2; - } - mReceiverUser = new User((const unsigned char*)receiverPublic.data()); - getErrors(mReceiverUser); - if (mReceiverUser->getUserState() == USER_EMPTY) { - sodium_bin2hex(mReceiverPublicHex, 65, (const unsigned char*)receiverPublic.data(), receiverPublic.size()); - delete mReceiverUser; - mReceiverUser = nullptr; - } - else { - memcpy(mReceiverPublicHex, mReceiverUser->getPublicKeyHex().data(), 64); - // uncomment because not correctly working - /*if (!mReceiverUser->validateIdentHash(mProtoCreation.ident_hash())) { - addError(new Error(functionName, "ident hash isn't the same")); - addError(new ParamError(functionName, "hash calculated from email: ", mReceiverUser->getEmail())); - addError(new ParamError(functionName, "hash: ", std::to_string(mProtoCreation.ident_hash()))); - return -3; - }*/ - } - // - - - return 0; -} - -std::string TransactionCreation::getTargetDateString() -{ - // proto format is seconds, poco timestamp format is microseconds - Poco::Timestamp pocoStamp(mProtoCreation.target_date().seconds() * 1000*1000); - //Poco::DateTime(pocoStamp); - return Poco::DateTimeFormatter::format(pocoStamp, "%d. %b %y"); -} - +#include "TransactionCreation.h" +#include "Poco/DateTimeFormatter.h" +#include + +TransactionCreation::TransactionCreation(const std::string& memo, const model::messages::gradido::TransactionCreation& protoCreation) + : TransactionBase(memo), mProtoCreation(protoCreation), mReceiverUser(nullptr) +{ + memset(mReceiverPublicHex, 0, 65); +} + +TransactionCreation::~TransactionCreation() +{ + if (mReceiverUser) { + delete mReceiverUser; + mReceiverUser = nullptr; + } +} + +int TransactionCreation::prepare() +{ + const static char functionName[] = { "TransactionCreation::prepare" }; + if (!mProtoCreation.has_receiveramount()) { + addError(new Error(functionName, "hasn't receiver amount")); + return -1; + } + auto receiverAmount = mProtoCreation.receiveramount(); + + if (receiverAmount.amount() <= 0) { + addError(new Error(functionName, "amount must be > 0")); + return -4; + } + if (receiverAmount.amount() > 10000000) { + addError(new Error(functionName, "amount must be <= 1000 GDD")); + return -5; + } + + auto receiverPublic = receiverAmount.ed25519_receiver_pubkey(); + if (receiverPublic.size() != 32) { + addError(new Error(functionName, "receiver public invalid (size not 32)")); + return -2; + } + mReceiverUser = new User((const unsigned char*)receiverPublic.data()); + getErrors(mReceiverUser); + if (mReceiverUser->getUserState() == USER_EMPTY) { + sodium_bin2hex(mReceiverPublicHex, 65, (const unsigned char*)receiverPublic.data(), receiverPublic.size()); + delete mReceiverUser; + mReceiverUser = nullptr; + } + else { + memcpy(mReceiverPublicHex, mReceiverUser->getPublicKeyHex().data(), 64); + // uncomment because not correctly working + /*if (!mReceiverUser->validateIdentHash(mProtoCreation.ident_hash())) { + addError(new Error(functionName, "ident hash isn't the same")); + addError(new ParamError(functionName, "hash calculated from email: ", mReceiverUser->getEmail())); + addError(new ParamError(functionName, "hash: ", std::to_string(mProtoCreation.ident_hash()))); + return -3; + }*/ + } + // + + + return 0; +} + +std::string TransactionCreation::getTargetDateString() +{ + // proto format is seconds, poco timestamp format is microseconds + Poco::Timestamp pocoStamp(mProtoCreation.target_date().seconds() * 1000*1000); + //Poco::DateTime(pocoStamp); + return Poco::DateTimeFormatter::format(pocoStamp, "%d. %b %y"); +} + diff --git a/login_server/src/cpp/model/TransactionTransfer.cpp b/login_server/src/cpp/model/TransactionTransfer.cpp index 136258816..4c89d7ad0 100644 --- a/login_server/src/cpp/model/TransactionTransfer.cpp +++ b/login_server/src/cpp/model/TransactionTransfer.cpp @@ -1,172 +1,172 @@ -#include "TransactionTransfer.h" - -const std::string TransactionTransfer::mInvalidIndexMessage("invalid index"); - -TransactionTransfer::KontoTableEntry::KontoTableEntry(model::table::User* user, google::protobuf::int64 amount, bool negativeAmount/* = false*/) -{ - //Normaler User <info@software-labor.de> - if (!user) return; - - composeAmountCellString(amount, negativeAmount); - - /*kontoNameCell = ""; - kontoNameCell += user->getFirstName(); - kontoNameCell += " "; - kontoNameCell += user->getLastName(); - kontoNameCell += " <"; - kontoNameCell += user->getEmail(); - kontoNameCell += ">";*/ - kontoNameCell = ""; - kontoNameCell += user->getNameWithEmailHtml(); - kontoNameCell += ""; -} - -TransactionTransfer::KontoTableEntry::KontoTableEntry(const std::string& pubkeyHex, google::protobuf::int64 amount, bool negativeAmount/* = false*/) -{ - composeAmountCellString(amount, negativeAmount); - //kontoNameCell = "0x" + pubkeyHex + ""; - kontoNameCell = "" + pubkeyHex + ""; -} - -void TransactionTransfer::KontoTableEntry::composeAmountCellString(google::protobuf::int64 amount, bool negativeAmount) -{ - //-10 GDD - //10 GDD - amountCell = "-"; - } - else { - amountCell += "success-color\">"; - } - amountCell += amountToString(amount); - //amountCell += " GDD"; - amountCell += " GDD"; -} - -// ******************************************************************************************************************************** - -TransactionTransfer::TransactionTransfer(const std::string& memo, const model::messages::gradido::Transfer& protoTransfer) - : TransactionBase(memo), mProtoTransfer(protoTransfer) -{ - -} - -TransactionTransfer::~TransactionTransfer() -{ - mKontoTable.clear(); -} - -int TransactionTransfer::prepare() -{ - lock(); - const static char functionName[] = { "TransactionTransfer::prepare" }; - if (mProtoTransfer.senderamounts_size() == 0) { - addError(new Error(functionName, "hasn't sender amount(s)")); - unlock(); - return -1; - } - if (mProtoTransfer.receiveramounts_size() == 0) { - addError(new Error(functionName, "hasn't receiver amount(s)")); - unlock(); - return -2; - } - mKontoTable.reserve(mProtoTransfer.senderamounts_size() + mProtoTransfer.receiveramounts_size()); - - //auto receiverAmount = mProtoTransfer.receiveramount(); - //auto senderAmount - int senderSum = 0; - int receiverSum = 0; - - char pubkeyHexTemp[65]; - - for (int i = 0; i < mProtoTransfer.senderamounts_size(); i++) { - auto senderAmount = mProtoTransfer.senderamounts(i); - auto pubkey = senderAmount.ed25519_sender_pubkey(); - senderSum += senderAmount.amount(); - if (pubkey.size() != 32) { - addError(new ParamError(functionName, "invalid public key for sender ", i)); - unlock(); - return -3; - } - //User user((const unsigned char*)pubkey.data()); - auto user = controller::User::create(); - if (!user->load((const unsigned char*)pubkey.data())) { - sodium_bin2hex(pubkeyHexTemp, 65, (const unsigned char*)pubkey.data(), pubkey.size()); - mKontoTable.push_back(KontoTableEntry(pubkeyHexTemp, senderAmount.amount(), true)); - } - else { - mKontoTable.push_back(KontoTableEntry(user->getModel(), senderAmount.amount(), true)); - } - } - for (int i = 0; i < mProtoTransfer.receiveramounts_size(); i++) { - auto receiverAmount = mProtoTransfer.receiveramounts(i); - auto pubkey = receiverAmount.ed25519_receiver_pubkey(); - receiverSum += receiverAmount.amount(); - if (receiverAmount.ed25519_receiver_pubkey().size() != 32) { - addError(new ParamError(functionName, "invalid public key for receiver ", i)); - unlock(); - return -4; - } - auto user = controller::User::create(); - if (!user->load((const unsigned char*)pubkey.data())) { - sodium_bin2hex(pubkeyHexTemp, 65, (const unsigned char*)pubkey.data(), pubkey.size()); - mKontoTable.push_back(KontoTableEntry(pubkeyHexTemp, receiverAmount.amount(), false)); - } - else { - mKontoTable.push_back(KontoTableEntry(user->getModel(), receiverAmount.amount(), false)); - } - } - if (senderSum != receiverSum) { - addError(new Error(functionName, "sender amounts sum != receiver amounts sum")); - unlock(); - return -5; - } - if (senderSum < 0) { - addError(new Error(functionName, "negative amount not supported")); - unlock(); - return -6; - } - - /* - mReceiverUser = new User(receiverPublic.data()); - getErrors(mReceiverUser); - if (mReceiverUser->getUserState() == USER_EMPTY) { - sodium_bin2hex(mReceiverPublicHex, 65, (const unsigned char*)receiverPublic.data(), receiverPublic.size()); - delete mReceiverUser; - mReceiverUser = nullptr; - } - else { - memcpy(mReceiverPublicHex, mReceiverUser->getPublicKeyHex().data(), 64); - } - //*/ - - unlock(); - return 0; -} - -const std::string& TransactionTransfer::getKontoNameCell(int index) -{ - - lock(); - if (index >= mKontoTable.size()) { - unlock(); - return mInvalidIndexMessage; - } - unlock(); - - return mKontoTable[index].kontoNameCell; -} - -const std::string& TransactionTransfer::getAmountCell(int index) -{ - lock(); - if (index >= mKontoTable.size()) { - unlock(); - return mInvalidIndexMessage; - } - unlock(); - - return mKontoTable[index].amountCell; -} - +#include "TransactionTransfer.h" + +const std::string TransactionTransfer::mInvalidIndexMessage("invalid index"); + +TransactionTransfer::KontoTableEntry::KontoTableEntry(model::table::User* user, google::protobuf::int64 amount, bool negativeAmount/* = false*/) +{ + //Normaler User <info@software-labor.de> + if (!user) return; + + composeAmountCellString(amount, negativeAmount); + + /*kontoNameCell = ""; + kontoNameCell += user->getFirstName(); + kontoNameCell += " "; + kontoNameCell += user->getLastName(); + kontoNameCell += " <"; + kontoNameCell += user->getEmail(); + kontoNameCell += ">";*/ + kontoNameCell = ""; + kontoNameCell += user->getNameWithEmailHtml(); + kontoNameCell += ""; +} + +TransactionTransfer::KontoTableEntry::KontoTableEntry(const std::string& pubkeyHex, google::protobuf::int64 amount, bool negativeAmount/* = false*/) +{ + composeAmountCellString(amount, negativeAmount); + //kontoNameCell = "0x" + pubkeyHex + ""; + kontoNameCell = "" + pubkeyHex + ""; +} + +void TransactionTransfer::KontoTableEntry::composeAmountCellString(google::protobuf::int64 amount, bool negativeAmount) +{ + //-10 GDD + //10 GDD + amountCell = "-"; + } + else { + amountCell += "success-color\">"; + } + amountCell += amountToString(amount); + //amountCell += " GDD"; + amountCell += " GDD"; +} + +// ******************************************************************************************************************************** + +TransactionTransfer::TransactionTransfer(const std::string& memo, const model::messages::gradido::Transfer& protoTransfer) + : TransactionBase(memo), mProtoTransfer(protoTransfer) +{ + +} + +TransactionTransfer::~TransactionTransfer() +{ + mKontoTable.clear(); +} + +int TransactionTransfer::prepare() +{ + lock(); + const static char functionName[] = { "TransactionTransfer::prepare" }; + if (mProtoTransfer.senderamounts_size() == 0) { + addError(new Error(functionName, "hasn't sender amount(s)")); + unlock(); + return -1; + } + if (mProtoTransfer.receiveramounts_size() == 0) { + addError(new Error(functionName, "hasn't receiver amount(s)")); + unlock(); + return -2; + } + mKontoTable.reserve(mProtoTransfer.senderamounts_size() + mProtoTransfer.receiveramounts_size()); + + //auto receiverAmount = mProtoTransfer.receiveramount(); + //auto senderAmount + int senderSum = 0; + int receiverSum = 0; + + char pubkeyHexTemp[65]; + + for (int i = 0; i < mProtoTransfer.senderamounts_size(); i++) { + auto senderAmount = mProtoTransfer.senderamounts(i); + auto pubkey = senderAmount.ed25519_sender_pubkey(); + senderSum += senderAmount.amount(); + if (pubkey.size() != 32) { + addError(new ParamError(functionName, "invalid public key for sender ", i)); + unlock(); + return -3; + } + //User user((const unsigned char*)pubkey.data()); + auto user = controller::User::create(); + if (!user->load((const unsigned char*)pubkey.data())) { + sodium_bin2hex(pubkeyHexTemp, 65, (const unsigned char*)pubkey.data(), pubkey.size()); + mKontoTable.push_back(KontoTableEntry(pubkeyHexTemp, senderAmount.amount(), true)); + } + else { + mKontoTable.push_back(KontoTableEntry(user->getModel(), senderAmount.amount(), true)); + } + } + for (int i = 0; i < mProtoTransfer.receiveramounts_size(); i++) { + auto receiverAmount = mProtoTransfer.receiveramounts(i); + auto pubkey = receiverAmount.ed25519_receiver_pubkey(); + receiverSum += receiverAmount.amount(); + if (receiverAmount.ed25519_receiver_pubkey().size() != 32) { + addError(new ParamError(functionName, "invalid public key for receiver ", i)); + unlock(); + return -4; + } + auto user = controller::User::create(); + if (!user->load((const unsigned char*)pubkey.data())) { + sodium_bin2hex(pubkeyHexTemp, 65, (const unsigned char*)pubkey.data(), pubkey.size()); + mKontoTable.push_back(KontoTableEntry(pubkeyHexTemp, receiverAmount.amount(), false)); + } + else { + mKontoTable.push_back(KontoTableEntry(user->getModel(), receiverAmount.amount(), false)); + } + } + if (senderSum != receiverSum) { + addError(new Error(functionName, "sender amounts sum != receiver amounts sum")); + unlock(); + return -5; + } + if (senderSum < 0) { + addError(new Error(functionName, "negative amount not supported")); + unlock(); + return -6; + } + + /* + mReceiverUser = new User(receiverPublic.data()); + getErrors(mReceiverUser); + if (mReceiverUser->getUserState() == USER_EMPTY) { + sodium_bin2hex(mReceiverPublicHex, 65, (const unsigned char*)receiverPublic.data(), receiverPublic.size()); + delete mReceiverUser; + mReceiverUser = nullptr; + } + else { + memcpy(mReceiverPublicHex, mReceiverUser->getPublicKeyHex().data(), 64); + } + //*/ + + unlock(); + return 0; +} + +const std::string& TransactionTransfer::getKontoNameCell(int index) +{ + + lock(); + if (index >= mKontoTable.size()) { + unlock(); + return mInvalidIndexMessage; + } + unlock(); + + return mKontoTable[index].kontoNameCell; +} + +const std::string& TransactionTransfer::getAmountCell(int index) +{ + lock(); + if (index >= mKontoTable.size()) { + unlock(); + return mInvalidIndexMessage; + } + unlock(); + + return mKontoTable[index].amountCell; +} + diff --git a/login_server/src/cpp/model/email/Email.cpp b/login_server/src/cpp/model/email/Email.cpp index c04ab417c..ca6dde125 100644 --- a/login_server/src/cpp/model/email/Email.cpp +++ b/login_server/src/cpp/model/email/Email.cpp @@ -1,4 +1,4 @@ -#include "Email.h" +#include "Email.h" #include "../../SingletonManager/EmailManager.h" #include "Poco/Net/MediaType.h" diff --git a/login_server/src/cpp/tasks/AuthenticatedEncryptionCreateKeyTask.cpp b/login_server/src/cpp/tasks/AuthenticatedEncryptionCreateKeyTask.cpp index fa8048e6a..ac7a6461b 100644 --- a/login_server/src/cpp/tasks/AuthenticatedEncryptionCreateKeyTask.cpp +++ b/login_server/src/cpp/tasks/AuthenticatedEncryptionCreateKeyTask.cpp @@ -1,40 +1,40 @@ -#include "AuthenticatedEncryptionCreateKeyTask.h" - -#include "../ServerConfig.h" -#include "../SingletonManager/SingletonTaskObserver.h" -#include "../SingletonManager/ErrorManager.h" - -#include "../lib/Profiler.h" - -AuthenticatedEncryptionCreateKeyTask::AuthenticatedEncryptionCreateKeyTask(Poco::AutoPtr user, const std::string& passwd) - : UniLib::controller::CPUTask(ServerConfig::g_CryptoCPUScheduler), mUser(user), mPassword(passwd) -{ - assert(!mUser.isNull()); - SingletonTaskObserver::getInstance()->addTask(mUser->getModel()->getEmail(), TASK_OBSERVER_PASSWORD_CREATION); -} - -AuthenticatedEncryptionCreateKeyTask::~AuthenticatedEncryptionCreateKeyTask() -{ - SingletonTaskObserver::getInstance()->removeTask(mUser->getModel()->getEmail(), TASK_OBSERVER_PASSWORD_CREATION); -} - -int AuthenticatedEncryptionCreateKeyTask::run() -{ - auto em = ErrorManager::getInstance(); - const static char* function_name = "AuthenticatedEncryptionCreateKeyTask::run"; - auto authenticated_encryption = new AuthenticatedEncryption; - Profiler timeUsed; - if (AuthenticatedEncryption::AUTH_ENCRYPT_OK != authenticated_encryption->createKey(mUser->getModel()->getEmail(), mPassword)) { - em->addError(new Error(function_name, "error creating key")); - em->addError(new ParamError(function_name, "for email", mUser->getModel()->getEmail())); - em->addError(new ParamError(function_name, "strerror: ", strerror(errno))); - em->sendErrorsAsEmail(); - return -1; - } - //printf("create password time: %s\n", timeUsed.string().data()); - timeUsed.reset(); - mUser->setNewPassword(authenticated_encryption); - //printf("set password time: %s\n", timeUsed.string().data()); - - return 0; +#include "AuthenticatedEncryptionCreateKeyTask.h" + +#include "../ServerConfig.h" +#include "../SingletonManager/SingletonTaskObserver.h" +#include "../SingletonManager/ErrorManager.h" + +#include "../lib/Profiler.h" + +AuthenticatedEncryptionCreateKeyTask::AuthenticatedEncryptionCreateKeyTask(Poco::AutoPtr user, const std::string& passwd) + : UniLib::controller::CPUTask(ServerConfig::g_CryptoCPUScheduler), mUser(user), mPassword(passwd) +{ + assert(!mUser.isNull()); + SingletonTaskObserver::getInstance()->addTask(mUser->getModel()->getEmail(), TASK_OBSERVER_PASSWORD_CREATION); +} + +AuthenticatedEncryptionCreateKeyTask::~AuthenticatedEncryptionCreateKeyTask() +{ + SingletonTaskObserver::getInstance()->removeTask(mUser->getModel()->getEmail(), TASK_OBSERVER_PASSWORD_CREATION); +} + +int AuthenticatedEncryptionCreateKeyTask::run() +{ + auto em = ErrorManager::getInstance(); + const static char* function_name = "AuthenticatedEncryptionCreateKeyTask::run"; + auto authenticated_encryption = new AuthenticatedEncryption; + Profiler timeUsed; + if (AuthenticatedEncryption::AUTH_ENCRYPT_OK != authenticated_encryption->createKey(mUser->getModel()->getEmail(), mPassword)) { + em->addError(new Error(function_name, "error creating key")); + em->addError(new ParamError(function_name, "for email", mUser->getModel()->getEmail())); + em->addError(new ParamError(function_name, "strerror: ", strerror(errno))); + em->sendErrorsAsEmail(); + return -1; + } + //printf("create password time: %s\n", timeUsed.string().data()); + timeUsed.reset(); + mUser->setNewPassword(authenticated_encryption); + //printf("set password time: %s\n", timeUsed.string().data()); + + return 0; } \ No newline at end of file diff --git a/login_server/src/cpp/tasks/ProcessingTransaction.cpp b/login_server/src/cpp/tasks/ProcessingTransaction.cpp index 3cf9905a8..6ed76f974 100644 --- a/login_server/src/cpp/tasks/ProcessingTransaction.cpp +++ b/login_server/src/cpp/tasks/ProcessingTransaction.cpp @@ -1,4 +1,4 @@ -#include "ProcessingTransaction.h" +#include "ProcessingTransaction.h" #include #include "../model/TransactionCreation.h" diff --git a/login_server/src/cpp/tasks/SigningTransaction.cpp b/login_server/src/cpp/tasks/SigningTransaction.cpp index f25d37bb4..ed87a03d0 100644 --- a/login_server/src/cpp/tasks/SigningTransaction.cpp +++ b/login_server/src/cpp/tasks/SigningTransaction.cpp @@ -1,284 +1,284 @@ -#include "SigningTransaction.h" - -#include - -#include "../SingletonManager/ErrorManager.h" -#include "../SingletonManager/MemoryManager.h" -#include "../SingletonManager/SingletonTaskObserver.h" - -#include "../lib/Profiler.h" - -#include "../proto/gradido/Transaction.pb.h" - -#include "sodium.h" - -#include "../ServerConfig.h" -#include "Poco/JSON/Object.h" -#include "Poco/JSON/Parser.h" -#include "Poco/StreamCopier.h" -#include "Poco/Net/HTTPSClientSession.h" -#include "Poco/Net/HTTPRequest.h" -#include "Poco/Net/HTTPResponse.h" - -SigningTransaction::SigningTransaction( - Poco::AutoPtr processingeTransaction, - Poco::AutoPtr newUser - , bool sendErrorsToAdmin/* = true*/) - : mProcessingeTransaction(processingeTransaction), mNewUser(newUser), mSendErrorsToAdminEmail(sendErrorsToAdmin) -{ - auto ob = SingletonTaskObserver::getInstance(); - auto email = getUserEmail(); - - if (email != "") { - ob->addTask(email, TASK_OBSERVER_SIGN_TRANSACTION); - } -} - -SigningTransaction::~SigningTransaction() -{ - auto ob = SingletonTaskObserver::getInstance(); - auto email = getUserEmail(); - - if (email != "") { - ob->removeTask(email, TASK_OBSERVER_SIGN_TRANSACTION); - } -} - -std::string SigningTransaction::getUserEmail() -{ - model::table::User* user_model = nullptr; - - if (!mNewUser.isNull()) { - user_model = mNewUser->getModel(); - } - if (user_model) { - return user_model->getEmail(); - } - return ""; -} - -int SigningTransaction::run() { - auto mm = MemoryManager::getInstance(); - - Error* transactionError = new Error("SigningTransaction", mProcessingeTransaction->mProtoMessageBase64.data()); - addError(transactionError, false); - - //= new Error("SigningTransaction start", mProcessingeTransaction->g) - //if (mUser.isNull() || !mUser->hasCryptoKey()) { - if(mNewUser.isNull() || !mNewUser->hasPassword()) { - addError(new Error("SigningTransaction", "user hasn't crypto key or is null")); - if(mSendErrorsToAdminEmail) sendErrorsAsEmail(); - return -1; - } - - //auto privKey = mUser->getPrivKey(); - //if (!mUser->hasPrivKey()) { - auto gradido_key_pair = mNewUser->getGradidoKeyPair(); - KeyPairEd25519* recovered_gradido_key_pair = nullptr; - if(!gradido_key_pair || !gradido_key_pair->hasPrivateKey()) { - - if (!mNewUser->tryLoadPassphraseUserBackup(&recovered_gradido_key_pair)) { - if(mNewUser->setGradidoKeyPair(recovered_gradido_key_pair)) - { - mNewUser->getModel()->updatePrivkey(); - } - } - else { - addError(new Error("SigningTransaction", "user cannot decrypt private key")); - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); - return -2; - } - } - // get body bytes - model::messages::gradido::Transaction transaction; - auto bodyBytes = transaction.mutable_bodybytes(); - *bodyBytes = mProcessingeTransaction->getBodyBytes(); - if (*bodyBytes == "") { - getErrors(mProcessingeTransaction); - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); - return -3; - } - // sign - //auto sign = mUser->sign((const unsigned char*)bodyBytes->data(), bodyBytes->size()); - MemoryBin* sign = nullptr; - if (gradido_key_pair) { - sign = gradido_key_pair->sign(*bodyBytes); - } - else if (recovered_gradido_key_pair) { - sign = recovered_gradido_key_pair->sign(*bodyBytes); - } - if (!sign) { - ErrorManager::getInstance()->sendErrorsAsEmail(); - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); - mm->releaseMemory(sign); - return -4; - } - - // pubkey for signature - /*auto pubkeyBin = mm->getFreeMemory(ed25519_pubkey_SIZE); - size_t realBin = 0; - if (sodium_hex2bin(*pubkeyBin, *pubkeyBin, pubkeyHex.data(), pubkeyHex.size(), nullptr, &realBin, nullptr)) { - addError(new Error("SigningTransaction", "error in sodium_hex2bin")); - sendErrorsAsEmail(); - mm->releaseMemory(pubkeyBin); - mm->releaseMemory(sign); - return -5; - } - */ - // add to message - auto sigMap = transaction.mutable_sigmap(); - auto sigPair = sigMap->add_sigpair(); - - auto pubkeyBytes = sigPair->mutable_pubkey(); - auto pubkeyBin = mNewUser->getModel()->getPublicKey(); - *pubkeyBytes = std::string((const char*)pubkeyBin, crypto_sign_PUBLICKEYBYTES); - - - auto sigBytes = sigPair->mutable_ed25519(); - *sigBytes = std::string((char*)*sign, sign->size()); - mm->releaseMemory(sign); - - /*std::string protoPrettyPrint; - google::protobuf::TextFormat::PrintToString(transaction, &protoPrettyPrint); - printf("transaction pretty: %s\n", protoPrettyPrint.data()); - model::messages::gradido::TransactionBody transactionBody; - transactionBody.MergeFromString(transaction.bodybytes()); - google::protobuf::TextFormat::PrintToString(transactionBody, &protoPrettyPrint); - printf("transaction body pretty: \n%s\n", protoPrettyPrint.data()); - */ - // finalize - //printf("sigpair size: %d\n", transaction.sigmap().sigpair_size()); - std::string finalTransactionBin = transaction.SerializeAsString(); - if (finalTransactionBin == "") { - addError(new Error("SigningTransaction", "error serializing final transaction")); - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); - return -6; - } - - // finale to base64 - auto finalBase64Size = sodium_base64_encoded_len(finalTransactionBin.size(), sodium_base64_VARIANT_URLSAFE_NO_PADDING); - auto finalBase64Bin = mm->getFreeMemory(finalBase64Size); - if (!sodium_bin2base64(*finalBase64Bin, finalBase64Size, (const unsigned char*)finalTransactionBin.data(), finalTransactionBin.size(), sodium_base64_VARIANT_URLSAFE_NO_PADDING)) { - addError(new Error("SigningTransaction", "error convert final transaction to base64")); - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); - mm->releaseMemory(finalBase64Bin); - return -7; - } - addError(new Error("Signing transaction final", *finalBase64Bin), false); - - // create json request - - Poco::JSON::Object requestJson; - requestJson.set("method", "putTransaction"); - requestJson.set("transaction", std::string((char*)*finalBase64Bin)); - //printf("\nbase64 transaction: \n%s\n\n", (char*)*finalBase64Bin); - mm->releaseMemory(finalBase64Bin); - - - //std::string request = requestJson.stringify(); - - // send post request via https - // 443 = HTTPS Default - // or http via port 80 if it is a test server - // TODO: adding port into ServerConfig - bool choose_ssl = false; - try { - Profiler phpRequestTime; - Poco::Net::HTTPClientSession* clientSession = nullptr; - - if (ServerConfig::g_phpServerPort) { - clientSession = new Poco::Net::HTTPSClientSession(ServerConfig::g_php_serverHost, ServerConfig::g_phpServerPort); - choose_ssl = true; - } - else if (ServerConfig::SERVER_TYPE_PRODUCTION == ServerConfig::g_ServerSetupType || - ServerConfig::SERVER_TYPE_STAGING == ServerConfig::g_ServerSetupType) { - clientSession = new Poco::Net::HTTPSClientSession(ServerConfig::g_php_serverHost, 443); - choose_ssl = true; - } - else { - clientSession = new Poco::Net::HTTPClientSession(ServerConfig::g_php_serverHost, 80); - choose_ssl = false; - } - Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, "/JsonRequestHandler"); - - request.setChunkedTransferEncoding(true); - std::ostream& requestStream = clientSession->sendRequest(request); - requestJson.stringify(requestStream); - - Poco::Net::HTTPResponse response; - std::istream& request_stream = clientSession->receiveResponse(response); - - // debugging answer - - std::stringstream responseStringStream; - for (std::string line; std::getline(request_stream, line); ) { - responseStringStream << line << std::endl; - } - Poco::Logger& speedLog= Poco::Logger::get("SpeedLog"); - speedLog.information("[putTransaction] php server time: %s", phpRequestTime.string()); - - // extract parameter from request - Poco::JSON::Parser jsonParser; - Poco::Dynamic::Var parsedJson; - try { - parsedJson = jsonParser.parse(responseStringStream.str()); - } - catch (Poco::Exception& ex) { - //printf("[JsonRequestHandler::handleRequest] Exception: %s\n", ex.displayText().data()); - addError(new ParamError("SigningTransaction", "error parsing request answer", ex.displayText().data())); - - std::string log_Path = "/var/log/grd_login/"; - //#ifdef _WIN32 -#if defined(_WIN32) || defined(_WIN64) - log_Path = "./"; -#endif - log_Path += "response.html"; - FILE* f = fopen(log_Path.data(), "wt"); - if (f) { - std::string responseString = responseStringStream.str(); - fwrite(responseString.data(), 1, responseString.size(), f); - fclose(f); - } - // */ - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(responseStringStream.str()); - return -9; - } - - //sendErrorsAsEmail("HalloRote Test "); - - Poco::JSON::Object object = *parsedJson.extract(); - - std::string stateString = ""; - if (!object.isNull("state")) { - auto state = object.get("state"); - stateString = state.convert(); - } - if (stateString != "success") { - addError(new Error("SigningTransaction", "php server don't return success")); - if (!object.isNull("msg")) { - addError(new ParamError("SigningTransaction", "msg:", object.get("msg").convert().data())); - } - if (!object.isNull("details")) { - addError(new ParamError("SigningTransaction", "details:", object.get("details").convert().data())); - } - if (!object.isNull("user_error")) { - addError(new ParamError("SigningTransaction", "user_error", object.get("user_error").convert().data())); - } - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); - return -10; - } - delete clientSession; - //printf("state: %s\n", stateString.data()); - //int zahl = 1; - } - catch (Poco::Exception& e) { - addError(new ParamError("SigningTransaction", "connect error to php server", e.displayText().data())); - addError(new ParamError("SigningTransaction", "url", ServerConfig::g_php_serverHost.data())); - addError(new ParamError("SigningTransaction", "choose_ssl", choose_ssl)); - if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); - return -8; - } - - - return 0; +#include "SigningTransaction.h" + +#include + +#include "../SingletonManager/ErrorManager.h" +#include "../SingletonManager/MemoryManager.h" +#include "../SingletonManager/SingletonTaskObserver.h" + +#include "../lib/Profiler.h" + +#include "../proto/gradido/Transaction.pb.h" + +#include "sodium.h" + +#include "../ServerConfig.h" +#include "Poco/JSON/Object.h" +#include "Poco/JSON/Parser.h" +#include "Poco/StreamCopier.h" +#include "Poco/Net/HTTPSClientSession.h" +#include "Poco/Net/HTTPRequest.h" +#include "Poco/Net/HTTPResponse.h" + +SigningTransaction::SigningTransaction( + Poco::AutoPtr processingeTransaction, + Poco::AutoPtr newUser + , bool sendErrorsToAdmin/* = true*/) + : mProcessingeTransaction(processingeTransaction), mNewUser(newUser), mSendErrorsToAdminEmail(sendErrorsToAdmin) +{ + auto ob = SingletonTaskObserver::getInstance(); + auto email = getUserEmail(); + + if (email != "") { + ob->addTask(email, TASK_OBSERVER_SIGN_TRANSACTION); + } +} + +SigningTransaction::~SigningTransaction() +{ + auto ob = SingletonTaskObserver::getInstance(); + auto email = getUserEmail(); + + if (email != "") { + ob->removeTask(email, TASK_OBSERVER_SIGN_TRANSACTION); + } +} + +std::string SigningTransaction::getUserEmail() +{ + model::table::User* user_model = nullptr; + + if (!mNewUser.isNull()) { + user_model = mNewUser->getModel(); + } + if (user_model) { + return user_model->getEmail(); + } + return ""; +} + +int SigningTransaction::run() { + auto mm = MemoryManager::getInstance(); + + Error* transactionError = new Error("SigningTransaction", mProcessingeTransaction->mProtoMessageBase64.data()); + addError(transactionError, false); + + //= new Error("SigningTransaction start", mProcessingeTransaction->g) + //if (mUser.isNull() || !mUser->hasCryptoKey()) { + if(mNewUser.isNull() || !mNewUser->hasPassword()) { + addError(new Error("SigningTransaction", "user hasn't crypto key or is null")); + if(mSendErrorsToAdminEmail) sendErrorsAsEmail(); + return -1; + } + + //auto privKey = mUser->getPrivKey(); + //if (!mUser->hasPrivKey()) { + auto gradido_key_pair = mNewUser->getGradidoKeyPair(); + KeyPairEd25519* recovered_gradido_key_pair = nullptr; + if(!gradido_key_pair || !gradido_key_pair->hasPrivateKey()) { + + if (!mNewUser->tryLoadPassphraseUserBackup(&recovered_gradido_key_pair)) { + if(mNewUser->setGradidoKeyPair(recovered_gradido_key_pair)) + { + mNewUser->getModel()->updatePrivkey(); + } + } + else { + addError(new Error("SigningTransaction", "user cannot decrypt private key")); + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); + return -2; + } + } + // get body bytes + model::messages::gradido::Transaction transaction; + auto bodyBytes = transaction.mutable_bodybytes(); + *bodyBytes = mProcessingeTransaction->getBodyBytes(); + if (*bodyBytes == "") { + getErrors(mProcessingeTransaction); + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); + return -3; + } + // sign + //auto sign = mUser->sign((const unsigned char*)bodyBytes->data(), bodyBytes->size()); + MemoryBin* sign = nullptr; + if (gradido_key_pair) { + sign = gradido_key_pair->sign(*bodyBytes); + } + else if (recovered_gradido_key_pair) { + sign = recovered_gradido_key_pair->sign(*bodyBytes); + } + if (!sign) { + ErrorManager::getInstance()->sendErrorsAsEmail(); + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); + mm->releaseMemory(sign); + return -4; + } + + // pubkey for signature + /*auto pubkeyBin = mm->getFreeMemory(ed25519_pubkey_SIZE); + size_t realBin = 0; + if (sodium_hex2bin(*pubkeyBin, *pubkeyBin, pubkeyHex.data(), pubkeyHex.size(), nullptr, &realBin, nullptr)) { + addError(new Error("SigningTransaction", "error in sodium_hex2bin")); + sendErrorsAsEmail(); + mm->releaseMemory(pubkeyBin); + mm->releaseMemory(sign); + return -5; + } + */ + // add to message + auto sigMap = transaction.mutable_sigmap(); + auto sigPair = sigMap->add_sigpair(); + + auto pubkeyBytes = sigPair->mutable_pubkey(); + auto pubkeyBin = mNewUser->getModel()->getPublicKey(); + *pubkeyBytes = std::string((const char*)pubkeyBin, crypto_sign_PUBLICKEYBYTES); + + + auto sigBytes = sigPair->mutable_ed25519(); + *sigBytes = std::string((char*)*sign, sign->size()); + mm->releaseMemory(sign); + + /*std::string protoPrettyPrint; + google::protobuf::TextFormat::PrintToString(transaction, &protoPrettyPrint); + printf("transaction pretty: %s\n", protoPrettyPrint.data()); + model::messages::gradido::TransactionBody transactionBody; + transactionBody.MergeFromString(transaction.bodybytes()); + google::protobuf::TextFormat::PrintToString(transactionBody, &protoPrettyPrint); + printf("transaction body pretty: \n%s\n", protoPrettyPrint.data()); + */ + // finalize + //printf("sigpair size: %d\n", transaction.sigmap().sigpair_size()); + std::string finalTransactionBin = transaction.SerializeAsString(); + if (finalTransactionBin == "") { + addError(new Error("SigningTransaction", "error serializing final transaction")); + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); + return -6; + } + + // finale to base64 + auto finalBase64Size = sodium_base64_encoded_len(finalTransactionBin.size(), sodium_base64_VARIANT_URLSAFE_NO_PADDING); + auto finalBase64Bin = mm->getFreeMemory(finalBase64Size); + if (!sodium_bin2base64(*finalBase64Bin, finalBase64Size, (const unsigned char*)finalTransactionBin.data(), finalTransactionBin.size(), sodium_base64_VARIANT_URLSAFE_NO_PADDING)) { + addError(new Error("SigningTransaction", "error convert final transaction to base64")); + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); + mm->releaseMemory(finalBase64Bin); + return -7; + } + addError(new Error("Signing transaction final", *finalBase64Bin), false); + + // create json request + + Poco::JSON::Object requestJson; + requestJson.set("method", "putTransaction"); + requestJson.set("transaction", std::string((char*)*finalBase64Bin)); + //printf("\nbase64 transaction: \n%s\n\n", (char*)*finalBase64Bin); + mm->releaseMemory(finalBase64Bin); + + + //std::string request = requestJson.stringify(); + + // send post request via https + // 443 = HTTPS Default + // or http via port 80 if it is a test server + // TODO: adding port into ServerConfig + bool choose_ssl = false; + try { + Profiler phpRequestTime; + Poco::Net::HTTPClientSession* clientSession = nullptr; + + if (ServerConfig::g_phpServerPort) { + clientSession = new Poco::Net::HTTPSClientSession(ServerConfig::g_php_serverHost, ServerConfig::g_phpServerPort); + choose_ssl = true; + } + else if (ServerConfig::SERVER_TYPE_PRODUCTION == ServerConfig::g_ServerSetupType || + ServerConfig::SERVER_TYPE_STAGING == ServerConfig::g_ServerSetupType) { + clientSession = new Poco::Net::HTTPSClientSession(ServerConfig::g_php_serverHost, 443); + choose_ssl = true; + } + else { + clientSession = new Poco::Net::HTTPClientSession(ServerConfig::g_php_serverHost, 80); + choose_ssl = false; + } + Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, "/JsonRequestHandler"); + + request.setChunkedTransferEncoding(true); + std::ostream& requestStream = clientSession->sendRequest(request); + requestJson.stringify(requestStream); + + Poco::Net::HTTPResponse response; + std::istream& request_stream = clientSession->receiveResponse(response); + + // debugging answer + + std::stringstream responseStringStream; + for (std::string line; std::getline(request_stream, line); ) { + responseStringStream << line << std::endl; + } + Poco::Logger& speedLog= Poco::Logger::get("SpeedLog"); + speedLog.information("[putTransaction] php server time: %s", phpRequestTime.string()); + + // extract parameter from request + Poco::JSON::Parser jsonParser; + Poco::Dynamic::Var parsedJson; + try { + parsedJson = jsonParser.parse(responseStringStream.str()); + } + catch (Poco::Exception& ex) { + //printf("[JsonRequestHandler::handleRequest] Exception: %s\n", ex.displayText().data()); + addError(new ParamError("SigningTransaction", "error parsing request answer", ex.displayText().data())); + + std::string log_Path = "/var/log/grd_login/"; + //#ifdef _WIN32 +#if defined(_WIN32) || defined(_WIN64) + log_Path = "./"; +#endif + log_Path += "response.html"; + FILE* f = fopen(log_Path.data(), "wt"); + if (f) { + std::string responseString = responseStringStream.str(); + fwrite(responseString.data(), 1, responseString.size(), f); + fclose(f); + } + // */ + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(responseStringStream.str()); + return -9; + } + + //sendErrorsAsEmail("HalloRote Test "); + + Poco::JSON::Object object = *parsedJson.extract(); + + std::string stateString = ""; + if (!object.isNull("state")) { + auto state = object.get("state"); + stateString = state.convert(); + } + if (stateString != "success") { + addError(new Error("SigningTransaction", "php server don't return success")); + if (!object.isNull("msg")) { + addError(new ParamError("SigningTransaction", "msg:", object.get("msg").convert().data())); + } + if (!object.isNull("details")) { + addError(new ParamError("SigningTransaction", "details:", object.get("details").convert().data())); + } + if (!object.isNull("user_error")) { + addError(new ParamError("SigningTransaction", "user_error", object.get("user_error").convert().data())); + } + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); + return -10; + } + delete clientSession; + //printf("state: %s\n", stateString.data()); + //int zahl = 1; + } + catch (Poco::Exception& e) { + addError(new ParamError("SigningTransaction", "connect error to php server", e.displayText().data())); + addError(new ParamError("SigningTransaction", "url", ServerConfig::g_php_serverHost.data())); + addError(new ParamError("SigningTransaction", "choose_ssl", choose_ssl)); + if (mSendErrorsToAdminEmail) sendErrorsAsEmail(); + return -8; + } + + + return 0; } \ No newline at end of file diff --git a/login_server/src/cpp/tasks/SigningTransaction.h b/login_server/src/cpp/tasks/SigningTransaction.h index 2038c0669..a35da915c 100644 --- a/login_server/src/cpp/tasks/SigningTransaction.h +++ b/login_server/src/cpp/tasks/SigningTransaction.h @@ -1,46 +1,46 @@ -#ifndef GRADIDO_LOGIN_SERVER_TASKS_SIGNING_TRANSACTION_INCLUDE -#define GRADIDO_LOGIN_SERVER_TASKS_SIGNING_TRANSACTION_INCLUDE - -#include "CPUTask.h" - -#include "../lib/ErrorList.h" -#include "../model/TransactionBase.h" -#include "../model/User.h" -#include "../controller/User.h" - -#include "../proto/gradido/Transaction.pb.h" - -#include "ProcessingTransaction.h" - -/* -* @author: Dario Rekowski -* -* @date: 28.10.19 -* @desc: Task for signing Transactions -*/ - -class SigningTransaction : public UniLib::controller::CPUTask, public ErrorList -{ -public: - SigningTransaction(Poco::AutoPtr processingeTransaction, Poco::AutoPtr newUser, bool sendErrorsToAdmin = true); - virtual ~SigningTransaction(); - - int run(); - - const char* getResourceType() const { return "SigningTransaction"; }; - - - -protected: - Poco::AutoPtr mProcessingeTransaction; - Poco::AutoPtr mNewUser; - bool mSendErrorsToAdminEmail; - -private: - - std::string getUserEmail(); - -}; - - +#ifndef GRADIDO_LOGIN_SERVER_TASKS_SIGNING_TRANSACTION_INCLUDE +#define GRADIDO_LOGIN_SERVER_TASKS_SIGNING_TRANSACTION_INCLUDE + +#include "CPUTask.h" + +#include "../lib/ErrorList.h" +#include "../model/TransactionBase.h" +#include "../model/User.h" +#include "../controller/User.h" + +#include "../proto/gradido/Transaction.pb.h" + +#include "ProcessingTransaction.h" + +/* +* @author: Dario Rekowski +* +* @date: 28.10.19 +* @desc: Task for signing Transactions +*/ + +class SigningTransaction : public UniLib::controller::CPUTask, public ErrorList +{ +public: + SigningTransaction(Poco::AutoPtr processingeTransaction, Poco::AutoPtr newUser, bool sendErrorsToAdmin = true); + virtual ~SigningTransaction(); + + int run(); + + const char* getResourceType() const { return "SigningTransaction"; }; + + + +protected: + Poco::AutoPtr mProcessingeTransaction; + Poco::AutoPtr mNewUser; + bool mSendErrorsToAdminEmail; + +private: + + std::string getUserEmail(); + +}; + + #endif //GRADIDO_LOGIN_SERVER_TASKS_SIGNING_TRANSACTION_INCLUDE \ No newline at end of file diff --git a/login_server/src/cpsp/login.cpsp b/login_server/src/cpsp/login.cpsp index 87a2aac3d..fc82adf2c 100644 --- a/login_server/src/cpsp/login.cpsp +++ b/login_server/src/cpsp/login.cpsp @@ -1,205 +1,205 @@ -<%@ page class="LoginPage" %> -<%@ page form="true" %> -<%@ page baseClass="SessionHTTPRequestHandler" %> -<%@ page ctorArg="Session*" %> -<%@ header include="SessionHTTPRequestHandler.h" %> -<%@ page compressed="true" %> -<%! -#include "../gettext.h" - -#include "Poco/Net/HTTPCookie.h" -#include "Poco/Net/HTTPServerParams.h" -#include "Poco/Logger.h" -#include "../SingletonManager/SessionManager.h" -#include "../SingletonManager/LanguageManager.h" -#include "../SingletonManager/ErrorManager.h" - -%> -<%% - const char* pageName = "Login"; - auto sm = SessionManager::getInstance(); - auto lm = LanguageManager::getInstance(); - auto em = ErrorManager::getInstance(); - - auto lang = chooseLanguage(request); - //printf("choose language return: %d\n", lang); - auto langCatalog = lm->getFreeCatalog(lang); - - std::string presetEmail(""); - if(mSession && mSession->getUser()) { - presetEmail = mSession->getUser()->getEmail(); - } - - if(!form.empty()) { - - bool langUpdatedByBtn = false; - auto langBtn = form.get("lang", ""); - if(langBtn != "") { - langUpdatedByBtn = true; - } - /* - auto langInput = form.get("lang", ""); - auto updatedLang = LANG_NULL; - if(langBtn != "") { - updatedLang = chooseLanguage(request, langBtn); - langUpdatedByBtn = true; - } else if(langInput != "") { - updatedLang = chooseLanguage(request, langInput); - } - - if(updatedLang != LANG_NULL && updatedLang != lang) { - lang = updatedLang; - langCatalog = lm->getFreeCatalog(lang); - } - */ - auto email = form.get("login-email", ""); - auto password = form.get("login-password", ""); - - if(email != "" && password != "") { - //auto session = sm->getSession(request); - //if(!mSession) mSession = sm->findByEmail(email); - if(!mSession) { - mSession = sm->getNewSession(); - mSession->setLanguageCatalog(langCatalog); - // get language - // first check url, second check language header - // for debugging client ip - auto client_host = request.clientAddress().host(); - //auto client_ip = request.clientAddress(); - // X-Real-IP forwarded ip from nginx config - auto client_host_string = request.get("X-Real-IP", client_host.toString()); - std::string clientIpString = "client ip: "; - client_host = Poco::Net::IPAddress(client_host_string); - clientIpString += client_host_string; - Poco::Logger::get("requestLog").information(clientIpString); - // debugging end - mSession->setClientIp(client_host); - response.addCookie(mSession->getLoginCookie()); - } else { - langCatalog = mSession->getLanguageCatalog(); - } - UserStates user_state; - try { - user_state = mSession->loadUser(email, password); - } catch (Poco::Exception& ex) { - addError(new ParamError("login", "exception by calling loadUser: ", ex.displayText())); - sendErrorsAsEmail(); - addError(new Error("Error", "Intern Server error, please try again later")); - } - auto user = mSession->getNewUser(); - - if(user_state >= USER_LOADED_FROM_DB && !user.isNull() && !user->getModel()->getPublicKey()) { - if(mSession->generateKeys(true, true)) { - user_state = USER_COMPLETE; - if(user->getModel()->isDisabled()) { - user_state = USER_DISABLED; - } - } - } else { - //printf("pubkey exist: %p\n",user->getModel()->getPublicKey()); - } - getErrors(mSession); - - auto uri_start = request.serverParams().getServerName(); - auto lastExternReferer = mSession->getLastReferer(); - - printf("user_state: %d\n", user_state); - - switch(user_state) { - case USER_EMPTY: - case USER_PASSWORD_INCORRECT: - addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("E-Mail or password isn't right, please try again!")), false); - if(mSession) { - getErrors(mSession); - sm->releaseSession(mSession); - } - sm->deleteLoginCookies(request, response); - break; - case USER_PASSWORD_ENCRYPTION_IN_PROCESS: - addError(new Error(langCatalog->gettext("Passwort"), langCatalog->gettext("Passwort wird noch berechnet, bitte versuche es in etwa 1 Minute erneut.")), false); - break; - case USER_KEYS_DONT_MATCH: - addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Error in saved data, the server admin will look at it."))); - break; - case USER_DISABLED: - addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Benutzer ist deaktiviert, kein Login möglich!"))); - if(mSession) { - getErrors(mSession); - sm->releaseSession(mSession); - } - sm->deleteLoginCookies(request, response); - break; - case USER_NO_PRIVATE_KEY: - case USER_COMPLETE: - case USER_EMAIL_NOT_ACTIVATED: - auto referer = request.find("Referer"); - std::string refererString; - if (referer != request.end()) { - refererString = referer->second; - } - if(lastExternReferer != "") { - //printf("redirect to: %s\n", lastExternReferer.data()); - response.redirect(lastExternReferer); - } else if(refererString != "" && - refererString.find("login") == std::string::npos && - refererString.find("logout") == std::string::npos && - refererString.find("user_delete") == std::string::npos && - refererString != ServerConfig::g_serverPath + request.getURI()) { - std::string uri = request.getURI(); - printf("request uri: %s, redirect to: %s\n", uri.data(), refererString.data()); - response.redirect(refererString); - } else { - //printf("redirect to: %s\n", ServerConfig::g_php_serverPath.data()); - response.redirect(ServerConfig::g_php_serverPath + "/"); - } - return; - } - - } else if(!langUpdatedByBtn) { - addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("Username and password are needed!")), false); - } - - } else { - - // on enter login page with empty form - //auto session = sm->getSession(request); - // remove old cookies and session if exist - if(mSession) { - getErrors(mSession); - sm->releaseSession(mSession); - } - sm->deleteLoginCookies(request, response); - } - -%><%@ include file="header.cpsp" %> -<%= getErrorsHtml() %> - -
- <%@ include file="flags.cpsp" %> -
-
- " value="<%= presetEmail %>"/> - " /> - -
-
-
- - -
-
-

 

- +<%@ page class="LoginPage" %> +<%@ page form="true" %> +<%@ page baseClass="SessionHTTPRequestHandler" %> +<%@ page ctorArg="Session*" %> +<%@ header include="SessionHTTPRequestHandler.h" %> +<%@ page compressed="true" %> +<%! +#include "../gettext.h" + +#include "Poco/Net/HTTPCookie.h" +#include "Poco/Net/HTTPServerParams.h" +#include "Poco/Logger.h" +#include "../SingletonManager/SessionManager.h" +#include "../SingletonManager/LanguageManager.h" +#include "../SingletonManager/ErrorManager.h" + +%> +<%% + const char* pageName = "Login"; + auto sm = SessionManager::getInstance(); + auto lm = LanguageManager::getInstance(); + auto em = ErrorManager::getInstance(); + + auto lang = chooseLanguage(request); + //printf("choose language return: %d\n", lang); + auto langCatalog = lm->getFreeCatalog(lang); + + std::string presetEmail(""); + if(mSession && mSession->getUser()) { + presetEmail = mSession->getUser()->getEmail(); + } + + if(!form.empty()) { + + bool langUpdatedByBtn = false; + auto langBtn = form.get("lang", ""); + if(langBtn != "") { + langUpdatedByBtn = true; + } + /* + auto langInput = form.get("lang", ""); + auto updatedLang = LANG_NULL; + if(langBtn != "") { + updatedLang = chooseLanguage(request, langBtn); + langUpdatedByBtn = true; + } else if(langInput != "") { + updatedLang = chooseLanguage(request, langInput); + } + + if(updatedLang != LANG_NULL && updatedLang != lang) { + lang = updatedLang; + langCatalog = lm->getFreeCatalog(lang); + } + */ + auto email = form.get("login-email", ""); + auto password = form.get("login-password", ""); + + if(email != "" && password != "") { + //auto session = sm->getSession(request); + //if(!mSession) mSession = sm->findByEmail(email); + if(!mSession) { + mSession = sm->getNewSession(); + mSession->setLanguageCatalog(langCatalog); + // get language + // first check url, second check language header + // for debugging client ip + auto client_host = request.clientAddress().host(); + //auto client_ip = request.clientAddress(); + // X-Real-IP forwarded ip from nginx config + auto client_host_string = request.get("X-Real-IP", client_host.toString()); + std::string clientIpString = "client ip: "; + client_host = Poco::Net::IPAddress(client_host_string); + clientIpString += client_host_string; + Poco::Logger::get("requestLog").information(clientIpString); + // debugging end + mSession->setClientIp(client_host); + response.addCookie(mSession->getLoginCookie()); + } else { + langCatalog = mSession->getLanguageCatalog(); + } + UserStates user_state; + try { + user_state = mSession->loadUser(email, password); + } catch (Poco::Exception& ex) { + addError(new ParamError("login", "exception by calling loadUser: ", ex.displayText())); + sendErrorsAsEmail(); + addError(new Error("Error", "Intern Server error, please try again later")); + } + auto user = mSession->getNewUser(); + + if(user_state >= USER_LOADED_FROM_DB && !user.isNull() && !user->getModel()->getPublicKey()) { + if(mSession->generateKeys(true, true)) { + user_state = USER_COMPLETE; + if(user->getModel()->isDisabled()) { + user_state = USER_DISABLED; + } + } + } else { + //printf("pubkey exist: %p\n",user->getModel()->getPublicKey()); + } + getErrors(mSession); + + auto uri_start = request.serverParams().getServerName(); + auto lastExternReferer = mSession->getLastReferer(); + + printf("user_state: %d\n", user_state); + + switch(user_state) { + case USER_EMPTY: + case USER_PASSWORD_INCORRECT: + addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("E-Mail or password isn't right, please try again!")), false); + if(mSession) { + getErrors(mSession); + sm->releaseSession(mSession); + } + sm->deleteLoginCookies(request, response); + break; + case USER_PASSWORD_ENCRYPTION_IN_PROCESS: + addError(new Error(langCatalog->gettext("Passwort"), langCatalog->gettext("Passwort wird noch berechnet, bitte versuche es in etwa 1 Minute erneut.")), false); + break; + case USER_KEYS_DONT_MATCH: + addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Error in saved data, the server admin will look at it."))); + break; + case USER_DISABLED: + addError(new Error(langCatalog->gettext("User"), langCatalog->gettext("Benutzer ist deaktiviert, kein Login möglich!"))); + if(mSession) { + getErrors(mSession); + sm->releaseSession(mSession); + } + sm->deleteLoginCookies(request, response); + break; + case USER_NO_PRIVATE_KEY: + case USER_COMPLETE: + case USER_EMAIL_NOT_ACTIVATED: + auto referer = request.find("Referer"); + std::string refererString; + if (referer != request.end()) { + refererString = referer->second; + } + if(lastExternReferer != "") { + //printf("redirect to: %s\n", lastExternReferer.data()); + response.redirect(lastExternReferer); + } else if(refererString != "" && + refererString.find("login") == std::string::npos && + refererString.find("logout") == std::string::npos && + refererString.find("user_delete") == std::string::npos && + refererString != ServerConfig::g_serverPath + request.getURI()) { + std::string uri = request.getURI(); + printf("request uri: %s, redirect to: %s\n", uri.data(), refererString.data()); + response.redirect(refererString); + } else { + //printf("redirect to: %s\n", ServerConfig::g_php_serverPath.data()); + response.redirect(ServerConfig::g_php_serverPath + "/"); + } + return; + } + + } else if(!langUpdatedByBtn) { + addError(new Error(langCatalog->gettext("Login"), langCatalog->gettext("Username and password are needed!")), false); + } + + } else { + + // on enter login page with empty form + //auto session = sm->getSession(request); + // remove old cookies and session if exist + if(mSession) { + getErrors(mSession); + sm->releaseSession(mSession); + } + sm->deleteLoginCookies(request, response); + } + +%><%@ include file="header.cpsp" %> +<%= getErrorsHtml() %> + +
+ <%@ include file="flags.cpsp" %> +
+
+ " value="<%= presetEmail %>"/> + " /> + +
+
+
+ + +
+
+

 

+ <%@ include file="footer.cpsp" %> \ No newline at end of file From 41b1a18072ca3f9cbb1fcf0d5aa347a99a7fffa9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 17:52:03 +0200 Subject: [PATCH 38/44] =?UTF-8?q?change=20fileformat=20back=20to=20utf-8?= =?UTF-8?q?=20with=20BOM,=20for=20visual=20studio=20source=20files=20which?= =?UTF-8?q?=20contain=20=C3=A4,=C3=84,=C3=B6,=C3=96,=C3=BC,=C3=9C=20or=20?= =?UTF-8?q?=C3=9F=20in=20strings=20must=20be=20in=20format=20utf-8-BOM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- login_server/src/cpp/ImportantTests.cpp | 2 +- login_server/src/cpp/model/email/Email.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/login_server/src/cpp/ImportantTests.cpp b/login_server/src/cpp/ImportantTests.cpp index a80af3cec..d321bab5f 100644 --- a/login_server/src/cpp/ImportantTests.cpp +++ b/login_server/src/cpp/ImportantTests.cpp @@ -1,4 +1,4 @@ -#include "ImportantTests.h" +#include "ImportantTests.h" #include #include "ServerConfig.h" diff --git a/login_server/src/cpp/model/email/Email.cpp b/login_server/src/cpp/model/email/Email.cpp index ca6dde125..c04ab417c 100644 --- a/login_server/src/cpp/model/email/Email.cpp +++ b/login_server/src/cpp/model/email/Email.cpp @@ -1,4 +1,4 @@ -#include "Email.h" +#include "Email.h" #include "../../SingletonManager/EmailManager.h" #include "Poco/Net/MediaType.h" From 3339ce9d388afea88440ce5319f51c58b6cc43b1 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 17:52:03 +0200 Subject: [PATCH 39/44] =?UTF-8?q?change=20fileformat=20back=20to=20utf-8?= =?UTF-8?q?=20with=20BOM,=20for=20visual=20studio=20source=20files=20which?= =?UTF-8?q?=20contain=20=C3=A4,=C3=84,=C3=B6,=C3=96,=C3=BC,=C3=9C=20or=20?= =?UTF-8?q?=C3=9F=20in=20strings=20must=20be=20in=20format=20utf-8-BOM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- login_server/src/cpp/ImportantTests.cpp | 2 +- login_server/src/cpp/model/Session.cpp | 2 +- login_server/src/cpp/model/email/Email.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/login_server/src/cpp/ImportantTests.cpp b/login_server/src/cpp/ImportantTests.cpp index a80af3cec..d321bab5f 100644 --- a/login_server/src/cpp/ImportantTests.cpp +++ b/login_server/src/cpp/ImportantTests.cpp @@ -1,4 +1,4 @@ -#include "ImportantTests.h" +#include "ImportantTests.h" #include #include "ServerConfig.h" diff --git a/login_server/src/cpp/model/Session.cpp b/login_server/src/cpp/model/Session.cpp index 2e135fb90..82167b9a5 100644 --- a/login_server/src/cpp/model/Session.cpp +++ b/login_server/src/cpp/model/Session.cpp @@ -1,4 +1,4 @@ -#include "Session.h" +#include "Session.h" #include "../lib/Profiler.h" #include "../ServerConfig.h" diff --git a/login_server/src/cpp/model/email/Email.cpp b/login_server/src/cpp/model/email/Email.cpp index ca6dde125..c04ab417c 100644 --- a/login_server/src/cpp/model/email/Email.cpp +++ b/login_server/src/cpp/model/email/Email.cpp @@ -1,4 +1,4 @@ -#include "Email.h" +#include "Email.h" #include "../../SingletonManager/EmailManager.h" #include "Poco/Net/MediaType.h" From 363e04fbe5bcc769439da522e5c85c58ffbcd1f4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 18:42:07 +0200 Subject: [PATCH 40/44] put back depracted email tasks because I don't exactly know if I broke something if I remove it completly (especially model/session.cpp) --- login_server/src/cpp/model/Session.cpp | 135 +++++++++--------- .../src/cpp/tasks/PrepareEmailTask.cpp | 65 +++++++++ login_server/src/cpp/tasks/PrepareEmailTask.h | 25 ++++ login_server/src/cpp/tasks/SendEmailTask.cpp | 64 +++++++++ login_server/src/cpp/tasks/SendEmailTask.h | 36 +++++ 5 files changed, 257 insertions(+), 68 deletions(-) create mode 100644 login_server/src/cpp/tasks/PrepareEmailTask.cpp create mode 100644 login_server/src/cpp/tasks/PrepareEmailTask.h create mode 100644 login_server/src/cpp/tasks/SendEmailTask.cpp create mode 100644 login_server/src/cpp/tasks/SendEmailTask.h diff --git a/login_server/src/cpp/model/Session.cpp b/login_server/src/cpp/model/Session.cpp index f231acc66..3d8a11d76 100644 --- a/login_server/src/cpp/model/Session.cpp +++ b/login_server/src/cpp/model/Session.cpp @@ -35,7 +35,7 @@ using namespace Poco::Data::Keywords; int WriteEmailVerification::run() -{ +{ auto em = ErrorManager::getInstance(); mEmailVerificationCode->getModel()->setUserId(mUser->getDBId()); @@ -98,8 +98,8 @@ Session::~Session() unlock(); reset(); } - - + + //printf("[Session::~Session] finished \n"); } @@ -116,9 +116,9 @@ void Session::reset() // watch out //updateTimeout(); mLastActivity = Poco::DateTime(); - + mState = SESSION_STATE_EMPTY; - + mPassphrase = ""; mLastExternReferer = ""; mClientLoginIP = Poco::Net::IPAddress(); @@ -133,15 +133,15 @@ void Session::reset() int Session::isActive() { - int ret = 0; + int ret = 0; try { mWorkMutex.tryLock(100); } - catch (Poco::TimeoutException &ex) { + catch (Poco::TimeoutException& ex) { return -1; } ret = (int)mActive; - unlock(); + unlock(); return ret; } @@ -154,21 +154,21 @@ bool Session::isDeadLocked() return false; } catch (Poco::Exception& ex) { - + } return true; } bool Session::setActive(bool active) -{ +{ try { mWorkMutex.tryLock(100); } - catch (Poco::TimeoutException &ex) { + catch (Poco::TimeoutException& ex) { return false; } - mActive = active; - unlock(); + mActive = active; + unlock(); return true; } @@ -233,12 +233,12 @@ bool Session::adminCreateUser(const std::string& first_name, const std::string& addError(new Error(gettext("Email Verification Code"), gettext("Fehler beim speichern!"))); return false; } - + EmailManager::getInstance()->addEmail(new model::Email(email_verification_code, newUser, model::EMAIL_ADMIN_USER_VERIFICATION_CODE)); std::unique_lock _lock(mSharedMutex); mEmailVerificationCodeObject = email_verification_code; - + return true; } @@ -311,11 +311,11 @@ bool Session::createUser(const std::string& first_name, const std::string& last_ std::unique_lock _lock(mSharedMutex); mEmailVerificationCodeObject = controller::EmailVerificationCode::create(model::table::EMAIL_OPT_IN_REGISTER); UniLib::controller::TaskPtr writeEmailVerification(new WriteEmailVerification(mSessionUser, mEmailVerificationCodeObject, ServerConfig::g_CPUScheduler, 1)); - + writeEmailVerification->setParentTaskPtrInArray(writeUserIntoDB, 0); writeEmailVerification->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, this)); writeEmailVerification->scheduleTask(writeEmailVerification); - + /*printf("LastName: %s\n", last_name.data()); for (int i = 0; i < last_name.size(); i++) { @@ -342,7 +342,7 @@ bool Session::createUser(const std::string& first_name, const std::string& last_ ss << std::endl; ss << "Mit freundlichen " << u8"Grüßen" << std::endl; ss << "Dario, Gradido Server Admin" << std::endl; - + message->addContent(new Poco::Net::StringPartSource(ss.str())); */ @@ -357,7 +357,7 @@ bool Session::createUser(const std::string& first_name, const std::string& last_ // write user into db // generate and write email verification into db // send email - + //printf("[Session::createUser] time: %s\n", usedTime.string().data()); return true; @@ -399,7 +399,7 @@ bool Session::createUserDirect(const std::string& first_name, const std::string& auto user_model = mNewUser->getModel(); user_model->insertIntoDB(true); auto user_id = user_model->getID(); - + // one retry in case of connection error if (!user_id) { @@ -444,7 +444,7 @@ bool Session::ifUserExist(const std::string& email) into(emailChecked), into(userId), useRef(email); try { - if(select.execute() == 1) return true; + if (select.execute() == 1) return true; } catch (Poco::Exception& ex) { em->addError(new ParamError(funcName, "select user from email verification code mysql error ", ex.displayText().data())); @@ -460,33 +460,33 @@ int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) // new mutex, will replace the Poco Mutex complete in the future std::unique_lock _lock_shared(mSharedMutex); Profiler usedTime; - + auto em = ErrorManager::getInstance(); if (mEmailVerificationCodeObject.isNull()) { em->addError(new Error(funcName, "email verification object is zero")); em->sendErrorsAsEmail(); - + return -2; } auto email_verification_code_model = mEmailVerificationCodeObject->getModel(); assert(email_verification_code_model); - if(email_verification_code_model->getCode() == emailVerificationCode) { + if (email_verification_code_model->getCode() == emailVerificationCode) { if (mSessionUser && mSessionUser->getDBId() == 0) { //addError(new Error("E-Mail Verification", "Benutzer wurde nicht richtig gespeichert, bitte wende dich an den Server-Admin")); em->addError(new Error(funcName, "user exist with 0 as id")); em->sendErrorsAsEmail(); - + //return false; return -2; } - + // load correct user from db if (mNewUser.isNull() || !mNewUser->getModel() || mNewUser->getModel()->getID() != email_verification_code_model->getUserId()) { mNewUser = controller::User::create(); if (1 != mNewUser->load(email_verification_code_model->getUserId())) { em->addError(new ParamError(funcName, "user load didn't return 1 with user_id ", email_verification_code_model->getUserId())); em->sendErrorsAsEmail(); - + return -2; } } @@ -495,15 +495,15 @@ int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) assert(user_model); bool first_email_activation = false; auto verification_type = email_verification_code_model->getType(); - if (model::table::EMAIL_OPT_IN_REGISTER == verification_type || - model::table::EMAIL_OPT_IN_EMPTY == verification_type || + if (model::table::EMAIL_OPT_IN_REGISTER == verification_type || + model::table::EMAIL_OPT_IN_EMPTY == verification_type || model::table::EMAIL_OPT_IN_REGISTER_DIRECT == verification_type) { first_email_activation = true; } if (first_email_activation && user_model->isEmailChecked()) { mSessionUser = new User(mNewUser); addError(new Error(gettext("E-Mail Verification"), gettext("Du hast dein Konto bereits aktiviert!")), false); - + return 1; } if (first_email_activation) { @@ -515,13 +515,13 @@ int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) } // no find all active sessions - + updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); return 0; } if (email_verification_code_model->getType() == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { - + if (mEmailVerificationCodeObject->deleteFromDB()) { mEmailVerificationCodeObject.assign(nullptr); } @@ -537,9 +537,9 @@ int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) em->addError(new Error(funcName, "invalid code path")); em->sendErrorsAsEmail(); - + return -2; - + /*if (updated_rows == 1) { Poco::Data::Statement delete_row(dbConnection); delete_row << "DELETE FROM email_opt_in where verification_code = ?", use(emailVerificationCode); @@ -560,17 +560,17 @@ int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) em->addError(new ParamError(funcName, "update user work not like expected, updated row count", updated_rows)); em->sendErrorsAsEmail(); }*/ - - + + } else { addError(new Error(gettext("E-Mail Verification"), gettext("Falscher Code für aktiven Login"))); //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - + return -1; } //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - + return 0; } @@ -683,7 +683,7 @@ bool Session::startProcessingTransaction(const std::string& proto_message_base64 Poco::AutoPtr processorTask( new ProcessingTransaction( - proto_message_base64, + proto_message_base64, DRMakeStringHash(mSessionUser->getEmail()), mSessionUser->getLanguage()) ); @@ -700,7 +700,7 @@ bool Session::startProcessingTransaction(const std::string& proto_message_base64 unlock(); return false; } - + } else { processorTask->scheduleTask(processorTask); @@ -708,7 +708,7 @@ bool Session::startProcessingTransaction(const std::string& proto_message_base64 } unlock(); return true; - + } Poco::AutoPtr Session::getNextReadyTransaction(size_t* working/* = nullptr*/) @@ -717,10 +717,10 @@ Poco::AutoPtr Session::getNextReadyTransaction(size_t* wo if (working) { *working = 0; } - else if (!mCurrentActiveProcessingTransaction.isNull()) + else if (!mCurrentActiveProcessingTransaction.isNull()) { unlock(); - return mCurrentActiveProcessingTransaction; + return mCurrentActiveProcessingTransaction; } for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { if (working && !(*it)->isTaskFinished()) { @@ -736,7 +736,7 @@ Poco::AutoPtr Session::getNextReadyTransaction(size_t* wo else { mCurrentActiveProcessingTransaction = *it; } - + } } unlock(); @@ -752,7 +752,7 @@ bool Session::finalizeTransaction(bool sign, bool reject) return false; } mProcessingTransactions.remove(mCurrentActiveProcessingTransaction); - + if (!reject) { if (sign) { Poco::AutoPtr signingTransaction(new SigningTransaction(mCurrentActiveProcessingTransaction, mNewUser)); @@ -765,10 +765,10 @@ bool Session::finalizeTransaction(bool sign, bool reject) return result == 0; } -size_t Session::getProcessingTransactionCount() -{ +size_t Session::getProcessingTransactionCount() +{ size_t count = 0; - lock("Session::getProcessingTransactionCount"); + lock("Session::getProcessingTransactionCount"); for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { @@ -785,8 +785,8 @@ size_t Session::getProcessingTransactionCount() } count = mProcessingTransactions.size(); - unlock(); - return count; + unlock(); + return count; } bool Session::isPwdValid(const std::string& pwd) @@ -850,7 +850,7 @@ UserStates Session::loadUser(const std::string& email, const std::string& passwo sendErrorsAsEmail(); } - if (exitCount >= 15) + if (exitCount >= 15) { auto running_password_creations = observer->getTasksCount(TASK_OBSERVER_PASSWORD_CREATION); @@ -859,9 +859,9 @@ UserStates Session::loadUser(const std::string& email, const std::string& passwo sendErrorsAsEmail(); return USER_PASSWORD_ENCRYPTION_IN_PROCESS; } - + //printf("new user login with result: %d\n", loginResult); - + if (-1 == loginResult) { addError(new Error(functionName, "error in user data set, saved pubkey didn't match extracted pubkey from private key")); addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); @@ -881,9 +881,9 @@ UserStates Session::loadUser(const std::string& email, const std::string& passwo auto user_backups = controller::UserBackups::load(user_model->getID()); for (auto it = user_backups.begin(); it != user_backups.end(); it++) { auto key = std::unique_ptr((*it)->createGradidoKeyPair()); - if (key->isTheSame(user_model->getPublicKey())) + if (key->isTheSame(user_model->getPublicKey())) { - + // set valid key pair if (1 == mNewUser->setGradidoKeyPair(key.release())) { // save new encrypted private key @@ -934,7 +934,7 @@ bool Session::deleteUser() { lock("Session::deleteUser"); bool bResult = false; - if(mSessionUser) { + if (mSessionUser) { JsonRequest phpServerRequest(ServerConfig::g_php_serverHost, 443); Poco::Net::NameValueCollection payload; payload.add("user", std::string(mSessionUser->getPublicKeyHex())); @@ -954,7 +954,7 @@ bool Session::deleteUser() return false; } } - if(!bResult) { + if (!bResult) { addError(new Error(gettext("Benutzer"), gettext("Fehler beim Löschen des Accounts. Bitte logge dich erneut ein und versuche es nochmal."))); } unlock(); @@ -1024,7 +1024,7 @@ void Session::detectSessionState() else if (checkEmail != -1) { mEmailVerificationCodeObject = emailVerificationCodeObjects[checkEmail]; } - + } catch (Poco::Exception& ex) { printf("[Session::detectSessionState] exception: %s\n", ex.displayText().data()); @@ -1037,13 +1037,13 @@ void Session::detectSessionState() updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); return; } - + updateState(SESSION_STATE_USER_WRITTEN); return; } if (USER_NO_KEYS == userState) { - + auto user_id = mSessionUser->getDBId(); auto userBackups = controller::UserBackups::load(user_id); @@ -1071,8 +1071,8 @@ void Session::detectSessionState() auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); Poco::Data::Statement select(dbConnection); Poco::Nullable passphrase; - - select << "SELECT passphrase from user_backups where user_id = ?;", + + select << "SELECT passphrase from user_backups where user_id = ?;", into(passphrase), use(user_id); try { if (select.execute() == 1 && !passphrase.isNull()) { @@ -1095,7 +1095,7 @@ void Session::detectSessionState() updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); return; } - + updateState(SESSION_STATE_KEY_PAIR_WRITTEN); if (resetPasswd != -1) { @@ -1120,7 +1120,7 @@ Poco::Net::HTTPCookie Session::getLoginCookie() keks.setSecure(true); } #endif - + return keks; } @@ -1193,7 +1193,7 @@ const char* Session::translateSessionStateToString(SessionStates state) case SESSION_STATE_EMAIL_VERIFICATION_SEND: return "Verification E-Mail sended"; case SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED: return "Verification Code checked"; case SESSION_STATE_PASSPHRASE_GENERATED: return "Passphrase generated"; - case SESSION_STATE_PASSPHRASE_SHOWN: return "Passphrase shown"; + case SESSION_STATE_PASSPHRASE_SHOWN: return "Passphrase shown"; case SESSION_STATE_PASSPHRASE_WRITTEN: return "Passphrase written"; case SESSION_STATE_KEY_PAIR_GENERATED: return "Gradido Address created"; case SESSION_STATE_KEY_PAIR_WRITTEN: return "Gradido Address saved"; @@ -1210,7 +1210,7 @@ const char* Session::translateSessionStateToString(SessionStates state) bool Session::useOrGeneratePassphrase(const std::string& passphase) { if (passphase != "" && User::validatePassphrase(passphase)) { - // passphrase is valid + // passphrase is valid setPassphrase(passphase); updateState(SESSION_STATE_PASSPHRASE_SHOWN); return true; @@ -1225,7 +1225,7 @@ bool Session::useOrGeneratePassphrase(const std::string& passphase) bool Session::generatePassphrase() { if (mNewUser.isNull()) return false; - + auto lang = getLanguage(); if (lang == LANG_EN) { mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); @@ -1321,4 +1321,3 @@ bool Session::generateKeys(bool savePrivkey, bool savePassphrase) return true; */ } - diff --git a/login_server/src/cpp/tasks/PrepareEmailTask.cpp b/login_server/src/cpp/tasks/PrepareEmailTask.cpp new file mode 100644 index 000000000..cc20d9472 --- /dev/null +++ b/login_server/src/cpp/tasks/PrepareEmailTask.cpp @@ -0,0 +1,65 @@ +#include "PrepareEmailTask.h" +#include "../lib/Profiler.h" +#include "../ServerConfig.h" +#include "../SingletonManager/ErrorManager.h" + +#include "Poco/Net/SSLException.h" + +PrepareEmailTask::PrepareEmailTask(UniLib::controller::CPUSheduler* cpuScheduler) + : UniLib::controller::CPUTask(cpuScheduler), mMailClientSession(nullptr) +{ + +} + +PrepareEmailTask::~PrepareEmailTask() +{ + if (mMailClientSession) { + delete mMailClientSession; + } +} + +int PrepareEmailTask::run() +{ + if (ServerConfig::g_disableEmail) return 0; + Profiler timeUsed; + mMailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port); + mMailClientSession->login(); + try { + mMailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context); + mMailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password); + } catch(Poco::Net::SSLException& ex) { + printf("[PrepareEmailTask] ssl certificate error: %s\nPlease make sure you have cacert.pem (CA/root certificates) next to binary from https://curl.haxx.se/docs/caextract.html\n", ex.displayText().data()); + return -1; + } + + //printf("[PrepareEmailTask] time: %s\n", timeUsed.string().data()); + /* + session.login(); + session.startTLS(pContext); + if (!username.empty()) + { + session.login(SMTPClientSession::AUTH_LOGIN, username, password); + } + session.sendMessage(message); + session.close(); + */ + + return 0; +} + +int PrepareEmailTask::send(Poco::Net::MailMessage* message) +{ + if (ServerConfig::g_disableEmail) return 0; + + auto er = ErrorManager::getInstance(); + try { + mMailClientSession->sendMessage(*message); + mMailClientSession->close(); + } + catch (Poco::Exception& exc) { + er->addError(new ParamError("PrepareEmailTask::send", "error sending email", exc.displayText().data())); + printf("[PrepareEmailTask::%s] error sending email: %s\n", __FUNCTION__, exc.displayText().data()); + return -1; + } + return 0; +} \ No newline at end of file diff --git a/login_server/src/cpp/tasks/PrepareEmailTask.h b/login_server/src/cpp/tasks/PrepareEmailTask.h new file mode 100644 index 000000000..4036eefc2 --- /dev/null +++ b/login_server/src/cpp/tasks/PrepareEmailTask.h @@ -0,0 +1,25 @@ +#ifndef GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE +#define GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE + +#include "CPUTask.h" +#include "Poco/Net/SecureSMTPClientSession.h" + + + +class PrepareEmailTask : public UniLib::controller::CPUTask +{ +public: + PrepareEmailTask(UniLib::controller::CPUSheduler* cpuScheduler); + virtual ~PrepareEmailTask(); + + virtual int run(); + int send(Poco::Net::MailMessage* message); + virtual const char* getResourceType() const { return "PrepareEmailTask"; }; +protected: + +private: + Poco::Net::SecureSMTPClientSession* mMailClientSession; +}; + + +#endif //GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE \ No newline at end of file diff --git a/login_server/src/cpp/tasks/SendEmailTask.cpp b/login_server/src/cpp/tasks/SendEmailTask.cpp new file mode 100644 index 000000000..e3a5e0069 --- /dev/null +++ b/login_server/src/cpp/tasks/SendEmailTask.cpp @@ -0,0 +1,64 @@ +#include "SendEmailTask.h" +#include "PrepareEmailTask.h" +#include "../lib/Profiler.h" +#include "../SingletonManager/ErrorManager.h" +#include "../SingletonManager/EmailManager.h" +#include "../ServerConfig.h" + +#include "Poco/Net/MediaType.h" + +SendEmailTask::SendEmailTask(Poco::Net::MailMessage* mailMessage, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount/* = 0*/) + : UniLib::controller::CPUTask(cpuScheduler, additionalTaskDependenceCount+1), mMailMessage(mailMessage), mEmail(nullptr) +{ +} + +SendEmailTask::SendEmailTask(model::Email*email, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount/* = 0*/) + : UniLib::controller::CPUTask(cpuScheduler, additionalTaskDependenceCount), mMailMessage(nullptr), mEmail(email) +{ + +} + +SendEmailTask::~SendEmailTask() +{ + if (mMailMessage) { + delete mMailMessage; + mMailMessage = nullptr; + } + if (mEmail) { + delete mEmail; + mEmail = nullptr; + } + +} + +int SendEmailTask::run() +{ + if(ServerConfig::g_disableEmail) return 0; + + Profiler timeUsed; + auto er = ErrorManager::getInstance(); + auto parent = getParent(0); + + if (mMailMessage) { + + if (strcmp(parent->getResourceType(), "PrepareEmailTask") != 0) { + er->addError(new Error("SendEmailTask", "first parent isn't PrepareEmailTask")); + er->sendErrorsAsEmail(); + return -1; + } + PrepareEmailTask* prepare = (PrepareEmailTask*)&(*parent); + mMailMessage->setSender(ServerConfig::g_EmailAccount.sender); + + if (prepare->send(mMailMessage)) { + er->sendErrorsAsEmail(); + return -1; + } + } + else if (mEmail) { + auto em = EmailManager::getInstance(); + em->addEmail(mEmail); + mEmail = nullptr; + } + //printf("[SendEmailTask] time: %s\n", timeUsed.string().data()); + return 0; +} \ No newline at end of file diff --git a/login_server/src/cpp/tasks/SendEmailTask.h b/login_server/src/cpp/tasks/SendEmailTask.h new file mode 100644 index 000000000..86e4b76dc --- /dev/null +++ b/login_server/src/cpp/tasks/SendEmailTask.h @@ -0,0 +1,36 @@ +#ifndef GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE +#define GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE + +#include "CPUTask.h" +#include "Poco/Net/MailMessage.h" + +#include "../model/email/Email.h" + +/* + * @author: Dario Rekowski + * + * @date: 29.09.19 + * @desc: Task for send an email, the first parent dependence pointer must be a prepare email task +*/ + + +class SendEmailTask : public UniLib::controller::CPUTask +{ +public: + + SendEmailTask(Poco::Net::MailMessage* mailMessage, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount = 0); + SendEmailTask(model::Email* email, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount = 0); + virtual ~SendEmailTask(); + + virtual int run(); + + virtual const char* getResourceType() const { return "SendEmailTask"; }; +protected: + +private: + Poco::Net::MailMessage* mMailMessage; + model::Email* mEmail; +}; + + +#endif //GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE \ No newline at end of file From d4f850d200d2811c58ffd05c74337b3b3b2c21b1 Mon Sep 17 00:00:00 2001 From: ogerly Date: Wed, 31 Mar 2021 08:08:02 +0200 Subject: [PATCH 41/44] elender murks --- frontend/DEV-README.md | 21 +++++++++++++++++++++ frontend/src/store/store.js | 23 +---------------------- 2 files changed, 22 insertions(+), 22 deletions(-) create mode 100644 frontend/DEV-README.md diff --git a/frontend/DEV-README.md b/frontend/DEV-README.md new file mode 100644 index 000000000..ab2b3e225 --- /dev/null +++ b/frontend/DEV-README.md @@ -0,0 +1,21 @@ +DEV README von Alex + +default Page: +´´´ + + + + +´´´ + diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index cc65ad353..f24eecb5b 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -18,7 +18,6 @@ export const store = new Vuex.Store({ balance: 0, balance_gdt: 0, }, - transactions: [], modals: false, optionAxios: { headers: { @@ -29,9 +28,7 @@ export const store = new Vuex.Store({ }, }, getters: { - //isLoggedIn: (state /*, getters */) => { - // return state.session_id !== null; - //} + }, // Syncronous mutation of the state mutations: { @@ -67,10 +64,6 @@ export const store = new Vuex.Store({ //console.log('mutation: user_balance_gdt') state.user.balance_gdt = balance / 10000 }, - transactions: (state, transactions) => { - //console.log('mutation: transactions') - state.transactions = transactions - }, }, // Asyncronous actions - used for api calls actions: { @@ -118,7 +111,6 @@ export const store = new Vuex.Store({ }, logout: async ({ commit, state }) => { //console.log('action: logout') - // Are we actually logged in? if (state.session_id) { const result = await loginAPI.logout(state.session_id) // The result can be error, but thats ok with us @@ -131,24 +123,11 @@ export const store = new Vuex.Store({ $cookies.remove('gdd_lang') router.push('/Login') }, - ajaxListTransactions: async ({ commit, dispatch, state }) => { - // console.log('action: ajaxListTransactions', state.session_id) - // const result = await communityAPI.transactions(state.session_id) - }, accountBalance: async ({ commit, dispatch, state }) => { - //console.log('action: accountBalance') - // console.log('action: dispatch', dispatch) - // console.log('action: state.session_id', state.session_id) - // console.log(" action: $cookies.get('gdd_session_id') ", $cookies.get("gdd_session_id") ) - // commit('session_id', $cookies.get("gdd_session_id")) - // commit('email', $cookies.get("gdd_u")) const result = await communityAPI.balance($cookies.get('gdd_session_id')) - //console.log("accountBalance result", result) - //console.log("aresult.result.data.balance", result.result.data.balance) if (result.success) { commit('user_balance', result.result.data.balance) } else { - //console.log('action accountBalance to logout start') dispatch('logout') } }, From 2630809d6743a0b580ee0c0ece7c1347f779aeee Mon Sep 17 00:00:00 2001 From: ogerly Date: Wed, 31 Mar 2021 08:13:54 +0200 Subject: [PATCH 42/44] yarn lint --- frontend/src/store/store.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index f24eecb5b..ecddfba22 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -27,9 +27,7 @@ export const store = new Vuex.Store({ }, }, }, - getters: { - - }, + getters: {}, // Syncronous mutation of the state mutations: { language: (state, language) => { From 363de9b3c20c8fbc8b7f8c9a155a23671c58aa56 Mon Sep 17 00:00:00 2001 From: ogerly Date: Wed, 31 Mar 2021 08:35:18 +0200 Subject: [PATCH 43/44] =?UTF-8?q?nicht=20verwendete=20files=20gel=C3=B6sch?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/routes/routes.js | 23 +- frontend/src/views/AdminOverview.vue | 399 ---- .../views/AdminOverview/AdminUserCreation.vue | 347 ---- .../views/AdminOverview/AdminUserSearch.vue | 261 --- frontend/src/views/AdminOverview/default.vue | 14 - frontend/src/views/Dashboard.vue | 215 --- frontend/src/views/Dashboard/GoodTable.vue | 150 -- .../src/views/Dashboard/PageVisitsTable.vue | 80 - .../views/Dashboard/SocialTrafficTable.vue | 85 - frontend/src/views/Icons.vue | 1615 ----------------- frontend/src/views/KontoOverview/default.vue | 14 - frontend/src/views/Layout/AdminLayout.vue | 114 -- frontend/src/views/Layout/AuthLayout.vue | 173 -- ...oardLayout.vue => DashboardLayout_gdd.vue} | 0 frontend/src/views/Maps/API_KEY.js | 1 - .../views/Tables/RegularTables/DarkTable.vue | 105 -- .../views/Tables/RegularTables/LightTable.vue | 105 -- frontend/src/views/Tables/projects.js | 74 - frontend/src/views/Tables/users.js | 42 - frontend/src/views/default.vue | 14 - 20 files changed, 1 insertion(+), 3830 deletions(-) delete mode 100644 frontend/src/views/AdminOverview.vue delete mode 100644 frontend/src/views/AdminOverview/AdminUserCreation.vue delete mode 100644 frontend/src/views/AdminOverview/AdminUserSearch.vue delete mode 100644 frontend/src/views/AdminOverview/default.vue delete mode 100644 frontend/src/views/Dashboard.vue delete mode 100644 frontend/src/views/Dashboard/GoodTable.vue delete mode 100644 frontend/src/views/Dashboard/PageVisitsTable.vue delete mode 100644 frontend/src/views/Dashboard/SocialTrafficTable.vue delete mode 100644 frontend/src/views/Icons.vue delete mode 100644 frontend/src/views/KontoOverview/default.vue delete mode 100644 frontend/src/views/Layout/AdminLayout.vue delete mode 100755 frontend/src/views/Layout/AuthLayout.vue rename frontend/src/views/Layout/{DashboardLayout.vue => DashboardLayout_gdd.vue} (100%) delete mode 100755 frontend/src/views/Maps/API_KEY.js delete mode 100644 frontend/src/views/Tables/RegularTables/DarkTable.vue delete mode 100644 frontend/src/views/Tables/RegularTables/LightTable.vue delete mode 100644 frontend/src/views/Tables/projects.js delete mode 100755 frontend/src/views/Tables/users.js delete mode 100644 frontend/src/views/default.vue diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js index ecb3e7bba..3128ad0e8 100755 --- a/frontend/src/routes/routes.js +++ b/frontend/src/routes/routes.js @@ -1,6 +1,5 @@ -import DashboardLayout from '@/views/Layout/DashboardLayout.vue' +import DashboardLayout from '@/views/Layout/DashboardLayout_gdd.vue' import AuthLayoutGDD from '@/views/Layout/AuthLayout_gdd.vue' -import AuthLayout from '@/views/Layout/AuthLayout.vue' import NotFound from '@/views/NotFoundPage.vue' @@ -31,11 +30,6 @@ const routes = [ name: 'Explorer', component: () => import('../views/Pages/Explorer.vue'), }, - { - path: '/icons', - name: 'Icons', - component: () => import('../views/Icons.vue'), - }, ], }, { @@ -68,21 +62,6 @@ const routes = [ }, ], }, - { - path: '/admin', - redirect: 'admin', - component: AuthLayout, - children: [ - { - path: '/AdminOverview', - name: 'Adminübersicht', - component: () => import('../views/AdminOverview.vue'), - meta: { - requiresAuth: true, - }, - }, - ], - }, { path: '*', component: NotFound }, ] diff --git a/frontend/src/views/AdminOverview.vue b/frontend/src/views/AdminOverview.vue deleted file mode 100644 index c7c3ef6aa..000000000 --- a/frontend/src/views/AdminOverview.vue +++ /dev/null @@ -1,399 +0,0 @@ - - - diff --git a/frontend/src/views/AdminOverview/AdminUserCreation.vue b/frontend/src/views/AdminOverview/AdminUserCreation.vue deleted file mode 100644 index c99ced14d..000000000 --- a/frontend/src/views/AdminOverview/AdminUserCreation.vue +++ /dev/null @@ -1,347 +0,0 @@ - - - diff --git a/frontend/src/views/AdminOverview/AdminUserSearch.vue b/frontend/src/views/AdminOverview/AdminUserSearch.vue deleted file mode 100644 index a06d85766..000000000 --- a/frontend/src/views/AdminOverview/AdminUserSearch.vue +++ /dev/null @@ -1,261 +0,0 @@ - - - diff --git a/frontend/src/views/AdminOverview/default.vue b/frontend/src/views/AdminOverview/default.vue deleted file mode 100644 index dd4639ab9..000000000 --- a/frontend/src/views/AdminOverview/default.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue deleted file mode 100644 index 8e2f0cb47..000000000 --- a/frontend/src/views/Dashboard.vue +++ /dev/null @@ -1,215 +0,0 @@ - - - diff --git a/frontend/src/views/Dashboard/GoodTable.vue b/frontend/src/views/Dashboard/GoodTable.vue deleted file mode 100644 index 95f225d2f..000000000 --- a/frontend/src/views/Dashboard/GoodTable.vue +++ /dev/null @@ -1,150 +0,0 @@ - - - diff --git a/frontend/src/views/Dashboard/PageVisitsTable.vue b/frontend/src/views/Dashboard/PageVisitsTable.vue deleted file mode 100644 index 5a2694553..000000000 --- a/frontend/src/views/Dashboard/PageVisitsTable.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - diff --git a/frontend/src/views/Dashboard/SocialTrafficTable.vue b/frontend/src/views/Dashboard/SocialTrafficTable.vue deleted file mode 100644 index 735f6a03f..000000000 --- a/frontend/src/views/Dashboard/SocialTrafficTable.vue +++ /dev/null @@ -1,85 +0,0 @@ - - - diff --git a/frontend/src/views/Icons.vue b/frontend/src/views/Icons.vue deleted file mode 100644 index 598c7c8a8..000000000 --- a/frontend/src/views/Icons.vue +++ /dev/null @@ -1,1615 +0,0 @@ - - - diff --git a/frontend/src/views/KontoOverview/default.vue b/frontend/src/views/KontoOverview/default.vue deleted file mode 100644 index dd4639ab9..000000000 --- a/frontend/src/views/KontoOverview/default.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/frontend/src/views/Layout/AdminLayout.vue b/frontend/src/views/Layout/AdminLayout.vue deleted file mode 100644 index 83f5ab762..000000000 --- a/frontend/src/views/Layout/AdminLayout.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - diff --git a/frontend/src/views/Layout/AuthLayout.vue b/frontend/src/views/Layout/AuthLayout.vue deleted file mode 100755 index a2f9e5c3c..000000000 --- a/frontend/src/views/Layout/AuthLayout.vue +++ /dev/null @@ -1,173 +0,0 @@ - - - diff --git a/frontend/src/views/Layout/DashboardLayout.vue b/frontend/src/views/Layout/DashboardLayout_gdd.vue similarity index 100% rename from frontend/src/views/Layout/DashboardLayout.vue rename to frontend/src/views/Layout/DashboardLayout_gdd.vue diff --git a/frontend/src/views/Maps/API_KEY.js b/frontend/src/views/Maps/API_KEY.js deleted file mode 100755 index b61195215..000000000 --- a/frontend/src/views/Maps/API_KEY.js +++ /dev/null @@ -1 +0,0 @@ -export const API_KEY = 'YOUR_API_KEY' diff --git a/frontend/src/views/Tables/RegularTables/DarkTable.vue b/frontend/src/views/Tables/RegularTables/DarkTable.vue deleted file mode 100644 index 6edef88a0..000000000 --- a/frontend/src/views/Tables/RegularTables/DarkTable.vue +++ /dev/null @@ -1,105 +0,0 @@ - - diff --git a/frontend/src/views/Tables/RegularTables/LightTable.vue b/frontend/src/views/Tables/RegularTables/LightTable.vue deleted file mode 100644 index 1c150a86e..000000000 --- a/frontend/src/views/Tables/RegularTables/LightTable.vue +++ /dev/null @@ -1,105 +0,0 @@ - - diff --git a/frontend/src/views/Tables/projects.js b/frontend/src/views/Tables/projects.js deleted file mode 100644 index be491a407..000000000 --- a/frontend/src/views/Tables/projects.js +++ /dev/null @@ -1,74 +0,0 @@ -export default [ - { - img: 'img/theme/bootstrap.jpg', - title: 'Argon Design System', - budget: '$2500 USD', - status: 'pending', - statusType: 'warning', - completion: 60, - }, - { - img: 'img/theme/angular.jpg', - title: 'Angular Now UI Kit PRO', - budget: '$1800 USD', - status: 'completed', - statusType: 'success', - completion: 100, - }, - { - img: 'img/theme/sketch.jpg', - title: 'Black Dashboard', - budget: '$3150 USD', - status: 'delayed', - statusType: 'danger', - completion: 72, - }, - { - img: 'img/theme/react.jpg', - title: 'React Material Dashboard', - budget: '$4400 USD', - status: 'on schedule', - statusType: 'info', - completion: 90, - }, - { - img: 'img/theme/vue.jpg', - title: 'Vue Paper UI Kit PRO', - budget: '$2200 USD', - status: 'completed', - statusType: 'success', - completion: 100, - }, - { - img: 'img/theme/bootstrap.jpg', - title: 'Argon Design System', - budget: '$2500 USD', - status: 'pending', - statusType: 'warning', - completion: 60, - }, - { - img: 'img/theme/angular.jpg', - title: 'Angular Now UI Kit PRO', - budget: '$1800 USD', - status: 'completed', - statusType: 'success', - completion: 100, - }, - { - img: 'img/theme/sketch.jpg', - title: 'Black Dashboard', - budget: '$3150 USD', - status: 'delayed', - statusType: 'danger', - completion: 72, - }, - { - img: 'img/theme/vue.jpg', - title: 'Vue Paper UI Kit PRO', - budget: '$2200 USD', - status: 'completed', - statusType: 'success', - completion: 100, - }, -] diff --git a/frontend/src/views/Tables/users.js b/frontend/src/views/Tables/users.js deleted file mode 100755 index d47260c42..000000000 --- a/frontend/src/views/Tables/users.js +++ /dev/null @@ -1,42 +0,0 @@ -export default [ - { - id: 1, - name: 'John Michael', - image: 'img/theme/team-1.jpg', - createdAt: '10/09/2018', - product: 'Argon Dashboard PRO', - active: true, - }, - { - id: 2, - name: 'Alexandra Smith', - image: 'img/theme/team-2.jpg', - createdAt: '08/09/2018', - product: 'Argon Design System', - active: false, - }, - { - id: 3, - name: 'Samantha Ivy', - image: 'img/theme/team-3.jpg', - createdAt: '30/08/2018', - product: 'Black Dashboard', - active: false, - }, - { - id: 4, - name: 'John Michael', - image: 'img/theme/team-1.jpg', - createdAt: '10/09/2018', - product: 'Argon Dashboard PRO', - active: true, - }, - { - id: 5, - name: 'Alexandra Smith', - image: 'img/theme/team-2.jpg', - createdAt: '30/09/2018', - product: 'Vue Argon Dashboard', - active: true, - }, -] diff --git a/frontend/src/views/default.vue b/frontend/src/views/default.vue deleted file mode 100644 index dd4639ab9..000000000 --- a/frontend/src/views/default.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - From e6067b0b381bebfad9f52cd72d3dbdd3dbb4209d Mon Sep 17 00:00:00 2001 From: ogerly Date: Wed, 31 Mar 2021 08:54:40 +0200 Subject: [PATCH 44/44] background color entfernt --- frontend/src/locales/de.json | 56 ++++++++++++----------------- frontend/src/locales/en.json | 10 ------ frontend/src/views/NotFoundPage.vue | 8 ++--- 3 files changed, 25 insertions(+), 49 deletions(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index d6337efae..44629a6e1 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -60,43 +60,33 @@ "title": "Danke!", "subtitle": "Wir haben dir eine eMail gesendet." }, - "overview":{ + "overview":{ "account_overview":"Kontoübersicht", "since_last_month": "seid letzten Monat", "send_gradido":"Gradido versenden", "add_work":"neuer Gemeinschaftsbeitrag" - }, - "navbar" : { - "my-profil":"Mein Profil", - "settings":"Einstellungen", - "activity":"Aktivität", - "support":"Support" - }, - "sidebar" : { - "community":"Gemeinschaft", - "members_area":"Mitgliederbereich", - "membership":"Mitgliedschaft", - "language":"Sprachen" - }, - "landing1" : { - "explore": "Erkunden Sie Gradido", - "text": "Gesundes Geld für eine gesunde Welt — Das Gradido-Modell kann weltweiten Wohlstand und Frieden schaffen", - "link": "Seiten erkunden" - }, - "404" : { - "ooops" : "Ooops!", - "text" : "Seite nicht gefunden. Aber keine Sorge, wir haben noch viele andere Seiten zum Erkunden", - "back" : "Zurück zur Übersicht!" - } - }, - "admin": { - "site": { - "overview": { - "created": "Schöpfen", - "transience" : "Transience", - "exchanged": "Exchanged", - "members" : "Members" - } + }, + "navbar" : { + "my-profil":"Mein Profil", + "settings":"Einstellungen", + "activity":"Aktivität", + "support":"Support" + }, + "sidebar" : { + "community":"Gemeinschaft", + "members_area":"Mitgliederbereich", + "membership":"Mitgliedschaft", + "language":"Sprachen" + }, + "landing1" : { + "explore": "Erkunden Sie Gradido", + "text": "Gesundes Geld für eine gesunde Welt — Das Gradido-Modell kann weltweiten Wohlstand und Frieden schaffen", + "link": "Seiten erkunden" + }, + "404" : { + "ooops" : "Ooops!", + "text" : "Seite nicht gefunden. Aber keine Sorge, wir haben noch viele andere Seiten zum Erkunden", + "back" : "Zurück zur Übersicht!" } }, "nav": { diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 0961a1fc1..104d505c1 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -90,16 +90,6 @@ "back" : "Back to dashboard!" } }, - "admin": { - "site": { - "overview": { - "creation": "Creation", - "transience" : "Transience", - "exchanged": "Exchanged", - "members" : "Members" - } - } - }, "nav": { "features": "Features" } diff --git a/frontend/src/views/NotFoundPage.vue b/frontend/src/views/NotFoundPage.vue index 89587cb30..ef387ab9d 100755 --- a/frontend/src/views/NotFoundPage.vue +++ b/frontend/src/views/NotFoundPage.vue @@ -1,7 +1,8 @@