diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 506da5b7..220fbd79 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,6 +15,7 @@ jobs: env: QT_VERSION: 6.6.2 QIF_VERSION: 4.7 + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} steps: - name: 'Install Qt' @@ -80,6 +81,7 @@ jobs: QT_VERSION: 6.6.2 QIF_VERSION: 4.7 BUILD_ARCH: 64 + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} steps: - name: 'Get sources' @@ -141,6 +143,7 @@ jobs: QT_VERSION: 6.6.2 CC: cc CXX: c++ + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} steps: - name: 'Setup xcode' @@ -231,6 +234,7 @@ jobs: # Keep compat with MacOS 10.15 aka Catalina by Qt 6.4 QT_VERSION: 6.4.3 QIF_VERSION: 4.6 + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} steps: - name: 'Setup xcode' @@ -295,6 +299,7 @@ jobs: ANDROID_BUILD_PLATFORM: android-34 QT_VERSION: 6.6.2 QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} steps: - name: 'Install desktop Qt' diff --git a/.github/workflows/tag-deploy.yml b/.github/workflows/tag-deploy.yml index b88390f4..e117a6c6 100644 --- a/.github/workflows/tag-deploy.yml +++ b/.github/workflows/tag-deploy.yml @@ -15,6 +15,7 @@ jobs: env: QT_VERSION: 6.4.1 QIF_VERSION: 4.5 + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} steps: - name: 'Install desktop Qt' diff --git a/.gitmodules b/.gitmodules index 78d45e25..3ceaa56e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "client/3rd/amneziawg-apple"] path = client/3rd/amneziawg-apple url = https://github.com/amnezia-vpn/amneziawg-apple +[submodule "client/3rd/QSimpleCrypto"] + path = client/3rd/QSimpleCrypto + url = https://github.com/amnezia-vpn/QSimpleCrypto.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 51ca8edc..41e05838 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.6.1.0 +project(${PROJECT} VERSION 4.7.0.0 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) @@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 56) +set(APP_ANDROID_VERSION_CODE 57) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/3rd/QSimpleCrypto b/client/3rd/QSimpleCrypto new file mode 160000 index 00000000..c99b33f0 --- /dev/null +++ b/client/3rd/QSimpleCrypto @@ -0,0 +1 @@ +Subproject commit c99b33f0e08b7206116ddff85c22d3b97ce1e79d diff --git a/client/3rd/QSimpleCrypto/QSimpleCrypto.cmake b/client/3rd/QSimpleCrypto/QSimpleCrypto.cmake deleted file mode 100644 index 7ec5498a..00000000 --- a/client/3rd/QSimpleCrypto/QSimpleCrypto.cmake +++ /dev/null @@ -1,20 +0,0 @@ -include_directories(${CMAKE_CURRENT_LIST_DIR}) - -set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/include/QAead.h - ${CMAKE_CURRENT_LIST_DIR}/include/QBlockCipher.h - ${CMAKE_CURRENT_LIST_DIR}/include/QCryptoError.h - ${CMAKE_CURRENT_LIST_DIR}/include/QRsa.h - ${CMAKE_CURRENT_LIST_DIR}/include/QSimpleCrypto_global.h - ${CMAKE_CURRENT_LIST_DIR}/include/QX509.h - ${CMAKE_CURRENT_LIST_DIR}/include/QX509Store.h -) - -set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/sources/QAead.cpp - ${CMAKE_CURRENT_LIST_DIR}/sources/QBlockCipher.cpp - ${CMAKE_CURRENT_LIST_DIR}/sources/QCryptoError.cpp - ${CMAKE_CURRENT_LIST_DIR}/sources/QRsa.cpp - ${CMAKE_CURRENT_LIST_DIR}/sources/QX509.cpp - ${CMAKE_CURRENT_LIST_DIR}/sources/QX509Store.cpp -) diff --git a/client/3rd/QSimpleCrypto/QSimpleCrypto.pri b/client/3rd/QSimpleCrypto/QSimpleCrypto.pri deleted file mode 100644 index 99a1c129..00000000 --- a/client/3rd/QSimpleCrypto/QSimpleCrypto.pri +++ /dev/null @@ -1,18 +0,0 @@ -INCLUDEPATH += $$PWD - -HEADERS += \ - $$PWD/include/QAead.h \ - $$PWD/include/QBlockCipher.h \ - $$PWD/include/QCryptoError.h \ - $$PWD/include/QRsa.h \ - $$PWD/include/QSimpleCrypto_global.h \ - $$PWD/include/QX509.h \ - $$PWD/include/QX509Store.h - -SOURCES += \ - $$PWD/sources/QAead.cpp \ - $$PWD/sources/QBlockCipher.cpp \ - $$PWD/sources/QCryptoError.cpp \ - $$PWD/sources/QRsa.cpp \ - $$PWD/sources/QX509.cpp \ - $$PWD/sources/QX509Store.cpp diff --git a/client/3rd/QSimpleCrypto/include/QAead.h b/client/3rd/QSimpleCrypto/include/QAead.h deleted file mode 100644 index 11f60b31..00000000 --- a/client/3rd/QSimpleCrypto/include/QAead.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#ifndef QAEAD_H -#define QAEAD_H - -#include "QSimpleCrypto_global.h" - -#include - -#include - -#include -#include -#include -#include - -#include "QCryptoError.h" - -// clang-format off -namespace QSimpleCrypto -{ - class QSIMPLECRYPTO_EXPORT QAead { - public: - QAead(); - - /// - /// \brief encryptAesGcm - Function encrypts data with Gcm algorithm. - /// \param data - Data that will be encrypted. - /// \param key - AES key. - /// \param iv - Initialization vector. - /// \param tag - Authorization tag. - /// \param aad - Additional authenticated data. Must be nullptr, if not used. - /// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm(). - /// \return Returns encrypted data or "", if error happened. - /// - QByteArray encryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_gcm()); - - /// - /// \brief decryptAesGcm - Function decrypts data with Gcm algorithm. - /// \param data - Data that will be decrypted - /// \param key - AES key - /// \param iv - Initialization vector - /// \param tag - Authorization tag - /// \param aad - Additional authenticated data. Must be nullptr, if not used - /// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm() - /// \return Returns decrypted data or "", if error happened. - /// - QByteArray decryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_gcm()); - - /// - /// \brief encryptAesCcm - Function encrypts data with Ccm algorithm. - /// \param data - Data that will be encrypted. - /// \param key - AES key. - /// \param iv - Initialization vector. - /// \param tag - Authorization tag. - /// \param aad - Additional authenticated data. Must be nullptr, if not used. - /// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm(). - /// \return Returns encrypted data or "", if error happened. - /// - QByteArray encryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_ccm()); - - /// - /// \brief decryptAesCcm - Function decrypts data with Ccm algorithm. - /// \param data - Data that will be decrypted. - /// \param key - AES key. - /// \param iv - Initialization vector. - /// \param tag - Authorization tag. - /// \param aad - Additional authenticated data. Must be nullptr, if not used. - /// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm(). - /// \return Returns decrypted data or "", if error happened. - /// - QByteArray decryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_ccm()); - - /// - /// \brief error - Error handler class. - /// - QCryptoError error; - }; -} // namespace QSimpleCrypto - -#endif // QAEAD_H diff --git a/client/3rd/QSimpleCrypto/include/QBlockCipher.h b/client/3rd/QSimpleCrypto/include/QBlockCipher.h deleted file mode 100644 index e7b83a88..00000000 --- a/client/3rd/QSimpleCrypto/include/QBlockCipher.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#ifndef QBLOCKCIPHER_H -#define QBLOCKCIPHER_H - -#include "QSimpleCrypto_global.h" - -#include - -#include - -#include -#include -#include -#include - -#include "QCryptoError.h" - -// clang-format off -namespace QSimpleCrypto -{ - class QSIMPLECRYPTO_EXPORT QBlockCipher { - - #define Aes128Rounds 10 - #define Aes192Rounds 12 - #define Aes256Rounds 14 - - public: - QBlockCipher(); - - /// - /// \brief generateRandomBytes - Function generates random bytes by size. - /// \param size - Size of generated bytes. - /// \return Returns random bytes. - /// - QByteArray generateRandomBytes(const int& size); - QByteArray generateSecureRandomBytes(const int& size); - - /// - /// \brief encryptAesBlockCipher - Function encrypts data with Aes Block Cipher algorithm. - /// \param data - Data that will be encrypted. - /// \param key - AES key. - /// \param iv - Initialization vector. - /// \param password - Encryption password. - /// \param salt - Random delta. - /// \param rounds - Transformation rounds. - /// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). - /// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512(). - /// \return Returns decrypted data or "", if error happened. - /// - QByteArray encryptAesBlockCipher(QByteArray data, QByteArray key, - QByteArray iv = "", const int& rounds = Aes256Rounds, - const EVP_CIPHER* cipher = EVP_aes_256_cbc(), const EVP_MD* md = EVP_sha512()); - - /// - /// \brief decryptAesBlockCipher - Function decrypts data with Aes Block Cipher algorithm. - /// \param data - Data that will be decrypted. - /// \param key - AES key. - /// \param iv - Initialization vector. - /// \param password - Decryption password. - /// \param salt - Random delta. - /// \param rounds - Transformation rounds. - /// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). - /// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512(). - /// \return Returns decrypted data or "", if error happened. - /// - QByteArray decryptAesBlockCipher(QByteArray data, QByteArray key, - QByteArray iv = "", const int& rounds = Aes256Rounds, - const EVP_CIPHER* cipher = EVP_aes_256_cbc(), const EVP_MD* md = EVP_sha512()); - - /// - /// \brief error - Error handler class. - /// - QCryptoError error; - }; -} // namespace QSimpleCrypto - -#endif // QBLOCKCIPHER_H diff --git a/client/3rd/QSimpleCrypto/include/QCryptoError.h b/client/3rd/QSimpleCrypto/include/QCryptoError.h deleted file mode 100644 index fc059654..00000000 --- a/client/3rd/QSimpleCrypto/include/QCryptoError.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef QCRYPTOERROR_H -#define QCRYPTOERROR_H - -#include - -#include "QSimpleCrypto_global.h" - -/// TODO: Add Special error code for each error. - -// clang-format off -namespace QSimpleCrypto -{ - class QSIMPLECRYPTO_EXPORT QCryptoError : public QObject { - Q_OBJECT - - public: - explicit QCryptoError(QObject* parent = nullptr); - - /// - /// \brief setError - Sets error information - /// \param errorCode - Error code. - /// \param errorSummary - Error summary. - /// - inline void setError(const quint8 errorCode, const QString& errorSummary) - { - m_currentErrorCode = errorCode; - m_errorSummary = errorSummary; - } - - /// - /// \brief lastError - Returns last error. - /// \return Returns eror ID and error Text. - /// - inline QPair lastError() const - { - return QPair(m_currentErrorCode, m_errorSummary); - } - - private: - quint8 m_currentErrorCode; - QString m_errorSummary; - }; -} - -#endif // QCRYPTOERROR_H diff --git a/client/3rd/QSimpleCrypto/include/QRsa.h b/client/3rd/QSimpleCrypto/include/QRsa.h deleted file mode 100644 index 45eb3169..00000000 --- a/client/3rd/QSimpleCrypto/include/QRsa.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#ifndef QRSA_H -#define QRSA_H - -#include "QSimpleCrypto_global.h" - -#include -#include - -#include - -#include -#include -#include - -#include "QCryptoError.h" - -// clang-format off -namespace QSimpleCrypto -{ - class QSIMPLECRYPTO_EXPORT QRsa { - - #define PublicEncrypt 0 - #define PrivateEncrypt 1 - #define PublicDecrypt 2 - #define PrivateDecrypt 3 - - public: - QRsa(); - - /// - /// \brief generateRsaKeys - Function generate Rsa Keys and returns them in OpenSSL structure. - /// \param bits - RSA key size. - /// \param rsaBigNumber - The exponent is an odd number, typically 3, 17 or 65537. - /// \return Returns 'OpenSSL RSA structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'RSA_free()' to avoid memory leak. - /// - RSA* generateRsaKeys(const int& bits, const int& rsaBigNumber); - - /// - /// \brief savePublicKey - Saves to file RSA public key. - /// \param rsa - OpenSSL RSA structure. - /// \param publicKeyFileName - Public key file name. - /// - void savePublicKey(RSA *rsa, const QByteArray& publicKeyFileName); - - /// - /// \brief savePrivateKey - Saves to file RSA private key. - /// \param rsa - OpenSSL RSA structure. - /// \param privateKeyFileName - Private key file name. - /// \param password - Private key password. - /// \param cipher - Can be used with 'OpenSSL EVP_CIPHER' (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). - /// - void savePrivateKey(RSA* rsa, const QByteArray& privateKeyFileName, QByteArray password = "", const EVP_CIPHER* cipher = nullptr); - - /// - /// \brief getPublicKeyFromFile - Gets RSA public key from a file. - /// \param filePath - File path to public key file. - /// \return Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak. - /// - EVP_PKEY* getPublicKeyFromFile(const QByteArray& filePath); - - /// - /// \brief getPrivateKeyFromFile - Gets RSA private key from a file. - /// \param filePath - File path to private key file. - /// \param password - Private key password. - /// \return - Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak. - /// - EVP_PKEY* getPrivateKeyFromFile(const QByteArray& filePath, const QByteArray& password = ""); - - /// - /// \brief encrypt - Encrypt data with RSA algorithm. - /// \param plaintext - Text that must be encrypted. - /// \param rsa - OpenSSL RSA structure. - /// \param encryptType - Public or private encrypt type. (PUBLIC_ENCRYPT, PRIVATE_ENCRYPT). - /// \param padding - OpenSSL RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc. - /// \return Returns encrypted data or "", if error happened. - /// - QByteArray encrypt(QByteArray plainText, RSA* rsa, const int& encryptType = PublicEncrypt, const int& padding = RSA_PKCS1_PADDING); - - /// - /// \brief decrypt - Decrypt data with RSA algorithm. - /// \param cipherText - Text that must be decrypted. - /// \param rsa - OpenSSL RSA structure. - /// \param decryptType - Public or private type. (PUBLIC_DECRYPT, PRIVATE_DECRYPT). - /// \param padding - RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc. - /// \return - Returns decrypted data or "", if error happened. - /// - QByteArray decrypt(QByteArray cipherText, RSA* rsa, const int& decryptType = PrivateDecrypt, const int& padding = RSA_PKCS1_PADDING); - - /// - /// \brief error - Error handler class. - /// - QCryptoError error; - }; -} // namespace QSimpleCrypto - -#endif // QRSA_H diff --git a/client/3rd/QSimpleCrypto/include/QSimpleCrypto_global.h b/client/3rd/QSimpleCrypto/include/QSimpleCrypto_global.h deleted file mode 100644 index fdd6c020..00000000 --- a/client/3rd/QSimpleCrypto/include/QSimpleCrypto_global.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef QSIMPLECRYPTO_GLOBAL_H -#define QSIMPLECRYPTO_GLOBAL_H - -#include -#include - -#define QSIMPLECRYPTO_EXPORT - -#endif // QSIMPLECRYPTO_GLOBAL_H diff --git a/client/3rd/QSimpleCrypto/include/QX509.h b/client/3rd/QSimpleCrypto/include/QX509.h deleted file mode 100644 index c31cb9e4..00000000 --- a/client/3rd/QSimpleCrypto/include/QX509.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#ifndef QX509_H -#define QX509_H - -#include "QSimpleCrypto_global.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "QCryptoError.h" - -// clang-format off -namespace QSimpleCrypto -{ - class QSIMPLECRYPTO_EXPORT QX509 { - - #define oneYear 31536000L - #define x509LastVersion 2 - - public: - QX509(); - - /// - /// \brief loadCertificateFromFile - Function load X509 from file and returns OpenSSL structure. - /// \param fileName - File path to certificate. - /// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak. - /// - X509* loadCertificateFromFile(const QByteArray& fileName); - - /// - /// \brief signCertificate - Function signs X509 certificate and returns signed X509 OpenSSL structure. - /// \param endCertificate - Certificate that will be signed - /// \param caCertificate - CA certificate that will sign end certificate - /// \param caPrivateKey - CA certificate private key - /// \param fileName - With that name certificate will be saved. Leave "", if don't need to save it - /// \return Returns OpenSSL X509 structure or nullptr, if error happened. - /// - X509* signCertificate(X509* endCertificate, X509* caCertificate, EVP_PKEY* caPrivateKey, const QByteArray& fileName = ""); - - /// - /// \brief verifyCertificate - Function verifies X509 certificate and returns verified X509 OpenSSL structure. - /// \param x509 - OpenSSL X509. That certificate will be verified. - /// \param store - Trusted certificate must be added to X509_Store with 'addCertificateToStore(X509_STORE* ctx, X509* x509)'. - /// \return Returns OpenSSL X509 structure or nullptr, if error happened - /// - X509* verifyCertificate(X509* x509, X509_STORE* store); - - /// - /// \brief generateSelfSignedCertificate - Function generatesand returns self signed X509. - /// \param rsa - OpenSSL RSA. - /// \param additionalData - Certificate information. - /// \param certificateFileName - With that name certificate will be saved. Leave "", if don't need to save it. - /// \param md - OpenSSL EVP_MD structure. Example: EVP_sha512(). - /// \param serialNumber - X509 certificate serial number. - /// \param version - X509 certificate version. - /// \param notBefore - X509 start date. - /// \param notAfter - X509 end date. - /// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak. - /// - X509* generateSelfSignedCertificate(RSA* rsa, const QMap& additionalData, - const QByteArray& certificateFileName = "", const EVP_MD* md = EVP_sha512(), - const long& serialNumber = 1, const long& version = x509LastVersion, - const long& notBefore = 0, const long& notAfter = oneYear); - - /// - /// \brief error - Error handler class. - /// - QCryptoError error; - }; -} // namespace QSimpleCrypto - -#endif // QX509_H diff --git a/client/3rd/QSimpleCrypto/include/QX509Store.h b/client/3rd/QSimpleCrypto/include/QX509Store.h deleted file mode 100644 index 8cd8ca82..00000000 --- a/client/3rd/QSimpleCrypto/include/QX509Store.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#ifndef QX509STORE_H -#define QX509STORE_H - -#include "QSimpleCrypto_global.h" - -#include -#include -#include - -#include - -#include -#include -#include - -#include "QCryptoError.h" - -// clang-format off -namespace QSimpleCrypto -{ - class QSIMPLECRYPTO_EXPORT QX509Store { - public: - QX509Store(); - - /// - /// \brief addCertificateToStore - /// \param store - OpenSSL X509_STORE. - /// \param x509 - OpenSSL X509. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool addCertificateToStore(X509_STORE* store, X509* x509); - - /// - /// \brief addLookup - /// \param store - OpenSSL X509_STORE. - /// \param method - OpenSSL X509_LOOKUP_METHOD. Example: X509_LOOKUP_file. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool addLookup(X509_STORE* store, X509_LOOKUP_METHOD* method); - - /// - /// \brief setCertificateDepth - /// \param store - OpenSSL X509_STORE. - /// \param depth - That is the maximum number of untrusted CA certificates that can appear in a chain. Example: 0. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool setDepth(X509_STORE* store, const int& depth); - - /// - /// \brief setFlag - /// \param store - OpenSSL X509_STORE. - /// \param flag - The verification flags consists of zero or more of the following flags ored together. Example: X509_V_FLAG_CRL_CHECK. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool setFlag(X509_STORE* store, const unsigned long& flag); - - /// - /// \brief setFlag - /// \param store - OpenSSL X509_STORE. - /// \param purpose - Verification purpose in param to purpose. Example: X509_PURPOSE_ANY. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool setPurpose(X509_STORE* store, const int& purpose); - - /// - /// \brief setTrust - /// \param store - OpenSSL X509_STORE. - /// \param trust - Trust Level. Example: X509_TRUST_SSL_SERVER. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool setTrust(X509_STORE* store, const int& trust); - - /// - /// \brief setDefaultPaths - /// \param store - OpenSSL X509_STORE. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool setDefaultPaths(X509_STORE* store); - - /// - /// \brief loadLocations - /// \param store - OpenSSL X509_STORE. - /// \param fileName - File name. Example: "caCertificate.pem". - /// \param dirPath - Path to file. Example: "path/To/File". - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool loadLocations(X509_STORE* store, const QByteArray& fileName, const QByteArray& dirPath); - - /// - /// \brief loadLocations - /// \param store - OpenSSL X509_STORE. - /// \param file - Qt QFile that will be loaded. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool loadLocations(X509_STORE* store, const QFile& file); - - /// - /// \brief loadLocations - /// \param store - OpenSSL X509_STORE. - /// \param fileInfo - Qt QFileInfo. - /// \return Returns 'true' on success and 'false', if error happened. - /// - bool loadLocations(X509_STORE* store, const QFileInfo& fileInfo); - - /// - /// \brief error - Error handler class. - /// - QCryptoError error; - }; -} - -#endif // QX509STORE_H diff --git a/client/3rd/QSimpleCrypto/sources/QAead.cpp b/client/3rd/QSimpleCrypto/sources/QAead.cpp deleted file mode 100644 index 968c8841..00000000 --- a/client/3rd/QSimpleCrypto/sources/QAead.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#include "include/QAead.h" - -QSimpleCrypto::QAead::QAead() -{ -} - -/// -/// \brief QSimpleCrypto::QAEAD::encryptAesGcm - Function encrypts data with Gcm algorithm. -/// \param data - Data that will be encrypted. -/// \param key - AES key. -/// \param iv - Initialization vector. -/// \param tag - Authorization tag. -/// \param aad - Additional authenticated data. Must be nullptr, if not used. -/// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm(). -/// \return Returns encrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QAead::encryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher) -{ - try { - /* Initialize EVP_CIPHER_CTX */ - std::unique_ptr encryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; - if (encryptionCipher == nullptr) { - throw std::runtime_error("Couldn't initialize \'encryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set data length */ - int plainTextLength = data.size(); - int cipherTextLength = 0; - - /* Initialize cipherText. Here encrypted data will be stored */ - std::unique_ptr cipherText { new unsigned char[plainTextLength]() }; - if (cipherText == nullptr) { - throw std::runtime_error("Couldn't allocate memory for 'ciphertext'."); - } - - /* Initialize encryption operation. */ - if (!EVP_EncryptInit_ex(encryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) { - throw std::runtime_error("Couldn't initialize encryption operation. EVP_EncryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set IV length if default 12 bytes (96 bits) is not appropriate */ - if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_GCM_SET_IVLEN, iv.length(), nullptr)) { - throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - -// /* Check if aad need to be used */ -// if (aad.length() > 0) { -// /* Provide any AAD data. This can be called zero or more times as required */ -// if (!EVP_EncryptUpdate(encryptionCipher.get(), nullptr, &cipherTextLength, reinterpret_cast(aad.data()), aad.length())) { -// throw std::runtime_error("Couldn't provide aad data. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); -// } -// } - - /* - * Provide the message to be encrypted, and obtain the encrypted output. - * EVP_EncryptUpdate can be called multiple times if necessary - */ - if (!EVP_EncryptUpdate(encryptionCipher.get(), cipherText.get(), &cipherTextLength, reinterpret_cast(data.data()), plainTextLength)) { - throw std::runtime_error("Couldn't provide message to be encrypted. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* - * Finalize the encryption. Normally cipher text bytes may be written at - * this stage, but this does not occur in GCM mode - */ - if (!EVP_EncryptFinal_ex(encryptionCipher.get(), cipherText.get(), &plainTextLength)) { - throw std::runtime_error("Couldn't finalize encryption. EVP_EncryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - -// /* Get tag */ -// if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_GCM_GET_TAG, tag->length(), reinterpret_cast(tag->data()))) { -// throw std::runtime_error("Couldn't get tag. EVP_CIPHER_CTX_ctrl(. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); -// } - - /* Finilize data to be readable with qt */ - QByteArray encryptedData = QByteArray(reinterpret_cast(cipherText.get()), cipherTextLength); - - return encryptedData; - - } catch (std::exception& exception) { - QSimpleCrypto::QAead::error.setError(1, exception.what()); - return QByteArray(); - } catch (...) { - QSimpleCrypto::QAead::error.setError(2, "Unknown error!"); - return QByteArray(); - } - - return QByteArray(); -} - -/// -/// \brief QSimpleCrypto::QAEAD::decryptAesGcm - Function decrypts data with Gcm algorithm. -/// \param data - Data that will be decrypted -/// \param key - AES key -/// \param iv - Initialization vector -/// \param tag - Authorization tag -/// \param aad - Additional authenticated data. Must be nullptr, if not used -/// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm() -/// \return Returns decrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QAead::decryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher) -{ - try { - /* Initialize EVP_CIPHER_CTX */ - std::unique_ptr decryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; - if (decryptionCipher.get() == nullptr) { - throw std::runtime_error("Couldn't initialize \'decryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set data length */ - int cipherTextLength = data.size(); - int plainTextLength = 0; - - /* Initialize plainText. Here decrypted data will be stored */ - std::unique_ptr plainText { new unsigned char[cipherTextLength]() }; - if (plainText == nullptr) { - throw std::runtime_error("Couldn't allocate memory for 'plaintext'."); - } - - /* Initialize decryption operation. */ - if (!EVP_DecryptInit_ex(decryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) { - throw std::runtime_error("Couldn't initialize decryption operation. EVP_DecryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set IV length. Not necessary if this is 12 bytes (96 bits) */ - if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_GCM_SET_IVLEN, iv.length(), nullptr)) { - throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - -// /* Check if aad need to be used */ -// if (aad.length() > 0) { -// /* Provide any AAD data. This can be called zero or more times as required */ -// if (!EVP_DecryptUpdate(decryptionCipher.get(), nullptr, &plainTextLength, reinterpret_cast(aad.data()), aad.length())) { -// throw std::runtime_error("Couldn't provide aad data. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); -// } -// } - - /* - * Provide the message to be decrypted, and obtain the plain text output. - * EVP_DecryptUpdate can be called multiple times if necessary - */ - if (!EVP_DecryptUpdate(decryptionCipher.get(), plainText.get(), &plainTextLength, reinterpret_cast(data.data()), cipherTextLength)) { - throw std::runtime_error("Couldn't provide message to be decrypted. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - -// /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ -// if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_GCM_SET_TAG, tag->length(), reinterpret_cast(tag->data()))) { -// throw std::runtime_error("Coldn't set tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); -// } - - /* - * Finalize the decryption. A positive return value indicates success, - * anything else is a failure - the plain text is not trustworthy. - */ - if (!EVP_DecryptFinal_ex(decryptionCipher.get(), plainText.get(), &cipherTextLength)) { - throw std::runtime_error("Couldn't finalize decryption. EVP_DecryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Finilize data to be readable with qt */ - QByteArray decryptedData = QByteArray(reinterpret_cast(plainText.get()), plainTextLength); - - return decryptedData; - - } catch (std::exception& exception) { - QSimpleCrypto::QAead::error.setError(1, exception.what()); - return QByteArray(); - } catch (...) { - QSimpleCrypto::QAead::error.setError(2, "Unknown error!"); - return QByteArray(); - } - - return QByteArray(); -} - -/// -/// \brief QSimpleCrypto::QAEAD::encryptAesCcm - Function encrypts data with Ccm algorithm. -/// \param data - Data that will be encrypted. -/// \param key - AES key. -/// \param iv - Initialization vector. -/// \param tag - Authorization tag. -/// \param aad - Additional authenticated data. Must be nullptr, if not used. -/// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm(). -/// \return Returns encrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QAead::encryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher) -{ - try { - /* Initialize EVP_CIPHER_CTX */ - std::unique_ptr encryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; - if (encryptionCipher == nullptr) { - throw std::runtime_error("Couldn't initialize \'encryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set data length */ - int plainTextLength = data.size(); - int cipherTextLength = 0; - - /* Initialize cipherText. Here encrypted data will be stored */ - std::unique_ptr cipherText { new unsigned char[plainTextLength]() }; - if (cipherText.get() == nullptr) { - throw std::runtime_error("Couldn't allocate memory for 'ciphertext'."); - } - - /* Initialize encryption operation. */ - if (!EVP_EncryptInit_ex(encryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) { - throw std::runtime_error("Couldn't initialize encryption operation. EVP_EncryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set IV length if default 12 bytes (96 bits) is not appropriate */ - if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_CCM_SET_IVLEN, iv.length(), nullptr)) { - throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set tag length */ - if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_CCM_SET_TAG, tag->length(), nullptr)) { - throw std::runtime_error("Coldn't set tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Check if aad need to be used */ - if (aad.length() > 0) { - /* Provide the total plain text length */ - if (!EVP_EncryptUpdate(encryptionCipher.get(), nullptr, &cipherTextLength, nullptr, plainTextLength)) { - throw std::runtime_error("Couldn't provide total plaintext length. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Provide any AAD data. This can be called zero or more times as required */ - if (!EVP_EncryptUpdate(encryptionCipher.get(), nullptr, &cipherTextLength, reinterpret_cast(aad.data()), aad.length())) { - throw std::runtime_error("Couldn't provide aad data. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - } - - /* - * Provide the message to be encrypted, and obtain the encrypted output. - * EVP_EncryptUpdate can be called multiple times if necessary - */ - if (!EVP_EncryptUpdate(encryptionCipher.get(), cipherText.get(), &cipherTextLength, reinterpret_cast(data.data()), plainTextLength)) { - throw std::runtime_error("Couldn't provide message to be encrypted. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* - * Finalize the encryption. Normally ciphertext bytes may be written at - * this stage, but this does not occur in GCM mode - */ - if (!EVP_EncryptFinal_ex(encryptionCipher.get(), cipherText.get(), &plainTextLength)) { - throw std::runtime_error("Couldn't finalize encryption. EVP_EncryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Get tag */ - if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_CCM_GET_TAG, tag->length(), reinterpret_cast(tag->data()))) { - throw std::runtime_error("Couldn't get tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Finilize data to be readable with qt */ - QByteArray encryptedData = QByteArray(reinterpret_cast(cipherText.get()), cipherTextLength); - - return encryptedData; - - } catch (std::exception& exception) { - QSimpleCrypto::QAead::error.setError(1, exception.what()); - return QByteArray(); - } catch (...) { - QSimpleCrypto::QAead::error.setError(2, "Unknown error!"); - return QByteArray(); - } - - return QByteArray(); -} - -/// -/// \brief QSimpleCrypto::QAEAD::decryptAesCcm - Function decrypts data with Ccm algorithm. -/// \param data - Data that will be decrypted. -/// \param key - AES key. -/// \param iv - Initialization vector. -/// \param tag - Authorization tag. -/// \param aad - Additional authenticated data. Must be nullptr, if not used. -/// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm(). -/// \return Returns decrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QAead::decryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher) -{ - try { - /* Initialize EVP_CIPHER_CTX */ - std::unique_ptr decryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; - if (decryptionCipher.get() == nullptr) { - throw std::runtime_error("Couldn't initialize \'decryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set data length */ - int cipherTextLength = data.size(); - int plainTextLength = 0; - - /* Initialize plainText. Here decrypted data will be stored */ - std::unique_ptr plainText { new unsigned char[cipherTextLength]() }; - if (plainText == nullptr) { - throw std::runtime_error("Couldn't allocate memory for 'plaintext'."); - } - - /* Initialize decryption operation. */ - if (!EVP_DecryptInit_ex(decryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) { - throw std::runtime_error("Couldn't initialize decryption operation. EVP_DecryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set IV length. Not necessary if this is 12 bytes (96 bits) */ - if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_CCM_SET_IVLEN, iv.length(), nullptr)) { - throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ - if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_CCM_SET_TAG, tag->length(), reinterpret_cast(tag->data()))) { - throw std::runtime_error("Coldn't set tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Check if aad need to be used */ - if (aad.length() > 0) { - /* Provide the total ciphertext length */ - if (!EVP_DecryptUpdate(decryptionCipher.get(), nullptr, &plainTextLength, nullptr, cipherTextLength)) { - throw std::runtime_error("Couldn't provide total plaintext length. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Provide any AAD data. This can be called zero or more times as required */ - if (!EVP_DecryptUpdate(decryptionCipher.get(), nullptr, &plainTextLength, reinterpret_cast(aad.data()), aad.length())) { - throw std::runtime_error("Couldn't provide aad data. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - } - - /* - * Provide the message to be decrypted, and obtain the plaintext output. - * EVP_DecryptUpdate can be called multiple times if necessary - */ - if (!EVP_DecryptUpdate(decryptionCipher.get(), plainText.get(), &plainTextLength, reinterpret_cast(data.data()), cipherTextLength)) { - throw std::runtime_error("Couldn't provide message to be decrypted. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* - * Finalize the decryption. A positive return value indicates success, - * anything else is a failure - the plaintext is not trustworthy. - */ - if (!EVP_DecryptFinal_ex(decryptionCipher.get(), plainText.get(), &cipherTextLength)) { - throw std::runtime_error("Couldn't finalize decryption. EVP_DecryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Finilize data to be readable with qt */ - QByteArray decryptedData = QByteArray(reinterpret_cast(plainText.get()), plainTextLength); - - return decryptedData; - - } catch (std::exception& exception) { - QSimpleCrypto::QAead::error.setError(1, exception.what()); - return QByteArray(); - } catch (...) { - QSimpleCrypto::QAead::error.setError(2, "Unknown error!"); - return QByteArray(); - } - - return QByteArray(); -} diff --git a/client/3rd/QSimpleCrypto/sources/QBlockCipher.cpp b/client/3rd/QSimpleCrypto/sources/QBlockCipher.cpp deleted file mode 100644 index 8b86ab98..00000000 --- a/client/3rd/QSimpleCrypto/sources/QBlockCipher.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#include "include/QBlockCipher.h" - -QSimpleCrypto::QBlockCipher::QBlockCipher() -{ -} - -/// -/// \brief QSimpleCrypto::QBlockCipher::generateRandomBytes - Function generates random bytes by size. -/// \param size - Size of generated bytes. -/// \return Returns random bytes. -/// -QByteArray QSimpleCrypto::QBlockCipher::generateRandomBytes(const int& size) -{ - unsigned char arr[sizeof(size)]; - RAND_bytes(arr, sizeof(size)); - - QByteArray buffer = QByteArray(reinterpret_cast(arr), size); - return buffer; -} - -QByteArray QSimpleCrypto::QBlockCipher::generateSecureRandomBytes(const int &size) -{ - unsigned char arr[sizeof(size)]; - RAND_priv_bytes(arr, sizeof(size)); - - QByteArray buffer = QByteArray(reinterpret_cast(arr), size); - return buffer; -} - -/// -/// \brief QSimpleCrypto::QBlockCipher::encryptAesBlockCipher - Function encrypts data with Aes Block Cipher algorithm. -/// \param data - Data that will be encrypted. -/// \param key - AES key. -/// \param iv - Initialization vector. -/// \param password - Encryption password. -/// \param salt - Random delta. -/// \param rounds - Transformation rounds. -/// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). -/// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512(). -/// \return Returns decrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QBlockCipher::encryptAesBlockCipher(QByteArray data, QByteArray key, - QByteArray iv, - const int& rounds, const EVP_CIPHER* cipher, const EVP_MD* md) -{ - try { - /* Initialize EVP_CIPHER_CTX */ - std::unique_ptr encryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; - if (encryptionCipher == nullptr) { - throw std::runtime_error("Couldn't initialize \'encryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Reinterpret values for multi use */ - unsigned char* m_key = reinterpret_cast(key.data()); - unsigned char* m_iv = reinterpret_cast(iv.data()); - - /* Set data length */ - int cipherTextLength(data.size() + AES_BLOCK_SIZE); - int finalLength = 0; - - /* Initialize cipcherText. Here encrypted data will be stored */ - std::unique_ptr cipherText { new unsigned char[cipherTextLength]() }; - if (cipherText == nullptr) { - throw std::runtime_error("Couldn't allocate memory for 'cipherText'."); - } - - // Bug here -// /* Start encryption with password based encryption routine */ -// if (!EVP_BytesToKey(cipher, md, reinterpret_cast(salt.data()), reinterpret_cast(password.data()), password.length(), rounds, m_key, m_iv)) { -// throw std::runtime_error("Couldn't start encryption routine. EVP_BytesToKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); -// } - - /* Initialize encryption operation. */ - if (!EVP_EncryptInit_ex(encryptionCipher.get(), cipher, nullptr, m_key, m_iv)) { - throw std::runtime_error("Couldn't initialize encryption operation. EVP_EncryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* - * Provide the message to be encrypted, and obtain the encrypted output. - * EVP_EncryptUpdate can be called multiple times if necessary - */ - if (!EVP_EncryptUpdate(encryptionCipher.get(), cipherText.get(), &cipherTextLength, reinterpret_cast(data.data()), data.size())) { - throw std::runtime_error("Couldn't provide message to be encrypted. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Finalize the encryption. Normally ciphertext bytes may be written at this stage */ - if (!EVP_EncryptFinal(encryptionCipher.get(), cipherText.get() + cipherTextLength, &finalLength)) { - throw std::runtime_error("Couldn't finalize encryption. EVP_EncryptFinal(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Finilize data to be readable with qt */ - QByteArray encryptedData = QByteArray(reinterpret_cast(cipherText.get()), cipherTextLength + finalLength); - - return encryptedData; - - } catch (std::exception& exception) { - QSimpleCrypto::QBlockCipher::error.setError(1, exception.what()); - return QByteArray(); - } catch (...) { - QSimpleCrypto::QBlockCipher::error.setError(2, "Unknown error!"); - return QByteArray(); - } - - return QByteArray(); -} - -/// -/// \brief QSimpleCrypto::QBlockCipher::encryptAesBlockCipher - Function decrypts data with Aes Block Cipher algorithm. -/// \param data - Data that will be decrypted. -/// \param key - AES key. -/// \param iv - Initialization vector. -/// \param password - Decryption password. -/// \param salt - Random delta. -/// \param rounds - Transformation rounds. -/// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). -/// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512(). -/// \return Returns decrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QBlockCipher::decryptAesBlockCipher(QByteArray data, QByteArray key, - QByteArray iv, - const int& rounds, const EVP_CIPHER* cipher, const EVP_MD* md) -{ - try { - /* Initialize EVP_CIPHER_CTX */ - std::unique_ptr decryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; - if (decryptionCipher == nullptr) { - throw std::runtime_error("Couldn't initialize \'decryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Reinterpret values for multi use */ - unsigned char* m_key = reinterpret_cast(key.data()); - unsigned char* m_iv = reinterpret_cast(iv.data()); - - /* Set data length */ - int plainTextLength(data.size()); - int finalLength = 0; - - /* Initialize plainText. Here decrypted data will be stored */ - std::unique_ptr plainText { new unsigned char[plainTextLength + AES_BLOCK_SIZE]() }; - if (plainText == nullptr) { - throw std::runtime_error("Couldn't allocate memory for \'plainText\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - // Bug here -// /* Start encryption with password based encryption routine */ -// if (!EVP_BytesToKey(cipher, md, reinterpret_cast(salt.data()), reinterpret_cast(password.data()), password.length(), rounds, m_key, m_iv)) { -// throw std::runtime_error("Couldn't start decryption routine. EVP_BytesToKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); -// } - - /* Initialize decryption operation. */ - if (!EVP_DecryptInit_ex(decryptionCipher.get(), cipher, nullptr, m_key, m_iv)) { - throw std::runtime_error("Couldn't initialize decryption operation. EVP_DecryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* - * Provide the message to be decrypted, and obtain the plaintext output. - * EVP_DecryptUpdate can be called multiple times if necessary - */ - if (!EVP_DecryptUpdate(decryptionCipher.get(), plainText.get(), &plainTextLength, reinterpret_cast(data.data()), data.size())) { - throw std::runtime_error("Couldn't provide message to be decrypted. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* - * Finalize the decryption. A positive return value indicates success, - * anything else is a failure - the plaintext is not trustworthy. - */ - if (!EVP_DecryptFinal(decryptionCipher.get(), plainText.get() + plainTextLength, &finalLength)) { - throw std::runtime_error("Couldn't finalize decryption. EVP_DecryptFinal. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Finilize data to be readable with qt */ - QByteArray decryptedData = QByteArray(reinterpret_cast(plainText.get()), plainTextLength + finalLength); - - return decryptedData; - - } catch (std::exception& exception) { - QSimpleCrypto::QBlockCipher::error.setError(1, exception.what()); - return QByteArray(exception.what()); - } catch (...) { - QSimpleCrypto::QBlockCipher::error.setError(2, "Unknown error!"); - return QByteArray(); - } - - return QByteArray(); -} diff --git a/client/3rd/QSimpleCrypto/sources/QCryptoError.cpp b/client/3rd/QSimpleCrypto/sources/QCryptoError.cpp deleted file mode 100644 index 234f55d7..00000000 --- a/client/3rd/QSimpleCrypto/sources/QCryptoError.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "include/QCryptoError.h" - -QSimpleCrypto::QCryptoError::QCryptoError(QObject* parent) - : QObject(parent) -{ -} diff --git a/client/3rd/QSimpleCrypto/sources/QRsa.cpp b/client/3rd/QSimpleCrypto/sources/QRsa.cpp deleted file mode 100644 index 544d6746..00000000 --- a/client/3rd/QSimpleCrypto/sources/QRsa.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#include "include/QRsa.h" - -QSimpleCrypto::QRsa::QRsa() -{ -} - -/// -/// \brief QSimpleCrypto::QRSA::generateRsaKeys - Function generate Rsa Keys and returns them in OpenSSL structure. -/// \param bits - RSA key size. -/// \param rsaBigNumber - The exponent is an odd number, typically 3, 17 or 65537. -/// \return Returns 'OpenSSL RSA structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'RSA_free()' to avoid memory leak. -/// -RSA* QSimpleCrypto::QRsa::generateRsaKeys(const int& bits, const int& rsaBigNumber) -{ - try { - /* Initialize big number */ - std::unique_ptr bigNumber { BN_new(), BN_free }; - if (bigNumber == nullptr) { - throw std::runtime_error("Couldn't initialize \'bigNumber\'. BN_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return nullptr; - } - - /* Set big number */ - if (!BN_set_word(bigNumber.get(), rsaBigNumber)) { - throw std::runtime_error("Couldn't set bigNumber. BN_set_word(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Initialize RSA */ - RSA* rsa = nullptr; - if (!(rsa = RSA_new())) { - throw std::runtime_error("Couldn't initialize x509. X509_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Generate key pair and store it in RSA */ - if (!RSA_generate_key_ex(rsa, bits, bigNumber.get(), nullptr)) { - throw std::runtime_error("Couldn't generate RSA. RSA_generate_key_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - return rsa; - } catch (std::exception& exception) { - QSimpleCrypto::QRsa::error.setError(1, exception.what()); - return nullptr; - } catch (...) { - QSimpleCrypto::QRsa::error.setError(2, "Unknown error!"); - return nullptr; - } -} - -/// -/// \brief QSimpleCrypto::QRSA::savePublicKey - Saves to file RSA public key. -/// \param rsa - OpenSSL RSA structure. -/// \param publicKeyFileName - Public key file name. -/// -void QSimpleCrypto::QRsa::savePublicKey(RSA* rsa, const QByteArray& publicKeyFileName) -{ - try { - /* Initialize BIO */ - std::unique_ptr bioPublicKey { BIO_new_file(publicKeyFileName.data(), "w+"), BIO_free_all }; - if (bioPublicKey == nullptr) { - throw std::runtime_error("Couldn't initialize \'bioPublicKey\'. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write public key on file */ - if (!PEM_write_bio_RSA_PUBKEY(bioPublicKey.get(), rsa)) { - throw std::runtime_error("Couldn't save public key. PEM_write_bio_RSAPublicKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - } catch (std::exception& exception) { - QSimpleCrypto::QRsa::error.setError(1, exception.what()); - return; - } catch (...) { - QSimpleCrypto::QRsa::error.setError(2, "Unknown error!"); - return; - } -} - -/// -/// \brief QSimpleCrypto::QRSA::savePrivateKey - Saves to file RSA private key. -/// \param rsa - OpenSSL RSA structure. -/// \param privateKeyFileName - Private key file name. -/// \param password - Private key password. -/// \param cipher - Can be used with 'OpenSSL EVP_CIPHER' (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). -/// -void QSimpleCrypto::QRsa::savePrivateKey(RSA* rsa, const QByteArray& privateKeyFileName, QByteArray password, const EVP_CIPHER* cipher) -{ - try { - /* Initialize BIO */ - std::unique_ptr bioPrivateKey { BIO_new_file(privateKeyFileName.data(), "w+"), BIO_free_all }; - if (bioPrivateKey == nullptr) { - throw std::runtime_error("Couldn't initialize bioPrivateKey. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write private key to file */ - if (!PEM_write_bio_RSAPrivateKey(bioPrivateKey.get(), rsa, cipher, reinterpret_cast(password.data()), password.size(), nullptr, nullptr)) { - throw std::runtime_error("Couldn't save private key. PEM_write_bio_RSAPrivateKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - } catch (std::exception& exception) { - QSimpleCrypto::QRsa::error.setError(1, exception.what()); - return; - } catch (...) { - QSimpleCrypto::QRsa::error.setError(2, "Unknown error!"); - return; - } -} - -/// -/// \brief QSimpleCrypto::QRSA::getPublicKeyFromFile - Gets RSA public key from a file. -/// \param filePath - File path to public key file. -/// \return Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak. -/// -EVP_PKEY* QSimpleCrypto::QRsa::getPublicKeyFromFile(const QByteArray& filePath) -{ - try { - /* Initialize BIO */ - std::unique_ptr bioPublicKey { BIO_new_file(filePath.data(), "r"), BIO_free_all }; - if (bioPublicKey == nullptr) { - throw std::runtime_error("Couldn't initialize bioPublicKey. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Initialize EVP_PKEY */ - EVP_PKEY* keyStore = nullptr; - if (!(keyStore = EVP_PKEY_new())) { - throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write private key to file */ - if (!PEM_read_bio_PUBKEY(bioPublicKey.get(), &keyStore, nullptr, nullptr)) { - throw std::runtime_error("Couldn't read private key. PEM_read_bio_PrivateKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - return keyStore; - - } catch (std::exception& exception) { - QSimpleCrypto::QRsa::error.setError(1, exception.what()); - return nullptr; - } catch (...) { - QSimpleCrypto::QRsa::error.setError(2, "Unknown error!"); - return nullptr; - } -} - -/// -/// \brief QSimpleCrypto::QRSA::getPrivateKeyFromFile - Gets RSA private key from a file. -/// \param filePath - File path to private key file. -/// \param password - Private key password. -/// \return - Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak. -/// -EVP_PKEY* QSimpleCrypto::QRsa::getPrivateKeyFromFile(const QByteArray& filePath, const QByteArray& password) -{ - try { - /* Initialize BIO */ - std::unique_ptr bioPrivateKey { BIO_new_file(filePath.data(), "r"), BIO_free_all }; - if (bioPrivateKey == nullptr) { - throw std::runtime_error("Couldn't initialize bioPrivateKey. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Initialize EVP_PKEY */ - EVP_PKEY* keyStore = nullptr; - if (!(keyStore = EVP_PKEY_new())) { - throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write private key to file */ - if (!PEM_read_bio_PrivateKey(bioPrivateKey.get(), &keyStore, nullptr, (void*)password.data())) { - throw std::runtime_error("Couldn't read private key. PEM_read_bio_PrivateKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - return keyStore; - - } catch (std::exception& exception) { - QSimpleCrypto::QRsa::error.setError(1, exception.what()); - return nullptr; - } catch (...) { - QSimpleCrypto::QRsa::error.setError(2, "Unknown error!"); - return nullptr; - } -} - -/// -/// \brief QSimpleCrypto::QRSA::encrypt - Encrypt data with RSA algorithm. -/// \param plaintext - Text that must be encrypted. -/// \param rsa - OpenSSL RSA structure. -/// \param encryptType - Public or private encrypt type. (PUBLIC_ENCRYPT, PRIVATE_ENCRYPT). -/// \param padding - OpenSSL RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc. -/// \return Returns encrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QRsa::encrypt(QByteArray plainText, RSA* rsa, const int& encryptType, const int& padding) -{ - try { - /* Initialize array. Here encrypted data will be saved */ - std::unique_ptr cipherText { new unsigned char[RSA_size(rsa)]() }; - if (cipherText == nullptr) { - throw std::runtime_error("Couldn't allocate memory for 'cipherText'."); - } - - /* Result of encryption operation */ - short int result = 0; - - /* Execute encryption operation */ - if (encryptType == PublicDecrypt) { - result = RSA_public_encrypt(plainText.size(), reinterpret_cast(plainText.data()), cipherText.get(), rsa, padding); - } else if (encryptType == PrivateDecrypt) { - result = RSA_private_encrypt(plainText.size(), reinterpret_cast(plainText.data()), cipherText.get(), rsa, padding); - } - - /* Check for result */ - if (result <= -1) { - throw std::runtime_error("Couldn't encrypt data. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Get encrypted data */ - const QByteArray& encryptedData = QByteArray(reinterpret_cast(cipherText.get()), RSA_size(rsa)); - - return encryptedData; - } catch (std::exception& exception) { - QSimpleCrypto::QRsa::error.setError(1, exception.what()); - return ""; - } catch (...) { - QSimpleCrypto::QRsa::error.setError(2, "Unknown error!"); - return ""; - } -} - -/// -/// \brief QSimpleCrypto::QRSA::decrypt - Decrypt data with RSA algorithm. -/// \param cipherText - Text that must be decrypted. -/// \param rsa - OpenSSL RSA structure. -/// \param decryptType - Public or private type. (PUBLIC_DECRYPT, PRIVATE_DECRYPT). -/// \param padding - RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc. -/// \return - Returns decrypted data or "", if error happened. -/// -QByteArray QSimpleCrypto::QRsa::decrypt(QByteArray cipherText, RSA* rsa, const int& decryptType, const int& padding) -{ - try { - /* Initialize array. Here decrypted data will be saved */ - std::unique_ptr plainText { new unsigned char[cipherText.size()]() }; - if (plainText == nullptr) { - throw std::runtime_error("Couldn't allocate memory for 'plainText'."); - } - - /* Result of decryption operation */ - short int result = 0; - - /* Execute decryption operation */ - if (decryptType == PublicDecrypt) { - result = RSA_public_decrypt(RSA_size(rsa), reinterpret_cast(cipherText.data()), plainText.get(), rsa, padding); - } else if (decryptType == PrivateDecrypt) { - result = RSA_private_decrypt(RSA_size(rsa), reinterpret_cast(cipherText.data()), plainText.get(), rsa, padding); - } - - /* Check for result */ - if (result <= -1) { - throw std::runtime_error("Couldn't decrypt data. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Get decrypted data */ - const QByteArray& decryptedData = QByteArray(reinterpret_cast(plainText.get())); - - return decryptedData; - } catch (std::exception& exception) { - QSimpleCrypto::QRsa::error.setError(1, exception.what()); - return ""; - } catch (...) { - QSimpleCrypto::QRsa::error.setError(2, "Unknown error!"); - return ""; - } -} diff --git a/client/3rd/QSimpleCrypto/sources/QX509.cpp b/client/3rd/QSimpleCrypto/sources/QX509.cpp deleted file mode 100644 index ac4fd270..00000000 --- a/client/3rd/QSimpleCrypto/sources/QX509.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#include "include/QX509.h" - -QSimpleCrypto::QX509::QX509() -{ -} - -/// -/// \brief QSimpleCrypto::QX509::loadCertificateFromFile - Function load X509 from file and returns OpenSSL structure. -/// \param fileName - File path to certificate. -/// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak. -/// -X509* QSimpleCrypto::QX509::loadCertificateFromFile(const QByteArray& fileName) -{ - try { - /* Initialize X509 */ - X509* x509 = nullptr; - if (!(x509 = X509_new())) { - throw std::runtime_error("Couldn't initialize X509. X509_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Initialize BIO */ - std::unique_ptr certFile { BIO_new_file(fileName.data(), "r+"), BIO_free_all }; - if (certFile == nullptr) { - throw std::runtime_error("Couldn't initialize certFile. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Read file */ - if (!PEM_read_bio_X509(certFile.get(), &x509, nullptr, nullptr)) { - throw std::runtime_error("Couldn't read certificate file from disk. PEM_read_bio_X509(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - return x509; - } catch (std::exception& exception) { - QSimpleCrypto::QX509::error.setError(1, exception.what()); - return nullptr; - } catch (...) { - QSimpleCrypto::QX509::error.setError(2, "Unknown error!"); - return nullptr; - } -} - -/// -/// \brief QSimpleCrypto::QX509::signCertificate - Function signs X509 certificate and returns signed X509 OpenSSL structure. -/// \param endCertificate - Certificate that will be signed -/// \param caCertificate - CA certificate that will sign end certificate -/// \param caPrivateKey - CA certificate private key -/// \param fileName - With that name certificate will be saved. Leave "", if don't need to save it -/// \return Returns OpenSSL X509 structure or nullptr, if error happened. -/// -X509* QSimpleCrypto::QX509::signCertificate(X509* endCertificate, X509* caCertificate, EVP_PKEY* caPrivateKey, const QByteArray& fileName) -{ - try { - /* Set issuer to CA's subject. */ - if (!X509_set_issuer_name(endCertificate, X509_get_subject_name(caCertificate))) { - throw std::runtime_error("Couldn't set issuer name for X509. X509_set_issuer_name(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Sign the certificate with key. */ - if (!X509_sign(endCertificate, caPrivateKey, EVP_sha256())) { - throw std::runtime_error("Couldn't sign X509. X509_sign(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write certificate file on disk. If needed */ - if (!fileName.isEmpty()) { - /* Initialize BIO */ - std::unique_ptr certFile { BIO_new_file(fileName.data(), "w+"), BIO_free_all }; - if (certFile == nullptr) { - throw std::runtime_error("Couldn't initialize certFile. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write file on disk */ - if (!PEM_write_bio_X509(certFile.get(), endCertificate)) { - throw std::runtime_error("Couldn't write certificate file on disk. PEM_write_bio_X509(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - } - - return endCertificate; - } catch (std::exception& exception) { - QSimpleCrypto::QX509::error.setError(1, exception.what()); - return nullptr; - } catch (...) { - QSimpleCrypto::QX509::error.setError(2, "Unknown error!"); - return nullptr; - } -} - -/// -/// \brief QSimpleCrypto::QX509::verifyCertificate - Function verifies X509 certificate and returns verified X509 OpenSSL structure. -/// \param x509 - OpenSSL X509. That certificate will be verified. -/// \param store - Trusted certificate must be added to X509_Store with 'addCertificateToStore(X509_STORE* ctx, X509* x509)'. -/// \return Returns OpenSSL X509 structure or nullptr, if error happened -/// -X509* QSimpleCrypto::QX509::verifyCertificate(X509* x509, X509_STORE* store) -{ - try { - /* Initialize X509_STORE_CTX */ - std::unique_ptr ctx { X509_STORE_CTX_new(), X509_STORE_CTX_free }; - if (ctx == nullptr) { - throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set up CTX for a subsequent verification operation */ - if (!X509_STORE_CTX_init(ctx.get(), store, x509, nullptr)) { - throw std::runtime_error("Couldn't initialize X509_STORE_CTX. X509_STORE_CTX_init(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Verify X509 */ - if (!X509_verify_cert(ctx.get())) { - throw std::runtime_error("Couldn't verify cert. X509_verify_cert(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - return x509; - } catch (std::exception& exception) { - QSimpleCrypto::QX509::error.setError(1, exception.what()); - return nullptr; - } catch (...) { - QSimpleCrypto::QX509::error.setError(2, "Unknown error!"); - return nullptr; - } -} - -/// -/// \brief QSimpleCrypto::QX509::generateSelfSignedCertificate - Function generatesand returns self signed X509. -/// \param rsa - OpenSSL RSA. -/// \param additionalData - Certificate information. -/// \param certificateFileName - With that name certificate will be saved. Leave "", if don't need to save it. -/// \param md - OpenSSL EVP_MD structure. Example: EVP_sha512(). -/// \param serialNumber - X509 certificate serial number. -/// \param version - X509 certificate version. -/// \param notBefore - X509 start date. -/// \param notAfter - X509 end date. -/// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak. -/// -X509* QSimpleCrypto::QX509::generateSelfSignedCertificate(RSA* rsa, const QMap& additionalData, - const QByteArray& certificateFileName, const EVP_MD* md, - const long& serialNumber, const long& version, - const long& notBefore, const long& notAfter) -{ - try { - /* Initialize X509 */ - X509* x509 = nullptr; - if (!(x509 = X509_new())) { - throw std::runtime_error("Couldn't initialize X509. X509_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Initialize EVP_PKEY */ - std::unique_ptr keyStore { EVP_PKEY_new(), EVP_PKEY_free }; - if (keyStore == nullptr) { - throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Sign rsa key */ - if (!EVP_PKEY_assign_RSA(keyStore.get(), rsa)) { - throw std::runtime_error("Couldn't assign rsa. EVP_PKEY_assign_RSA(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set certificate serial number. */ - if (!ASN1_INTEGER_set(X509_get_serialNumber(x509), serialNumber)) { - throw std::runtime_error("Couldn't set serial number. ASN1_INTEGER_set(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set certificate version */ - if (!X509_set_version(x509, version)) { - throw std::runtime_error("Couldn't set version. X509_set_version(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Set certificate creation and expiration date */ - X509_gmtime_adj(X509_get_notBefore(x509), notBefore); - X509_gmtime_adj(X509_get_notAfter(x509), notAfter); - - /* Set certificate public key */ - if (!X509_set_pubkey(x509, keyStore.get())) { - throw std::runtime_error("Couldn't set public key. X509_set_pubkey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Initialize X509_NAME */ - X509_NAME* x509Name = X509_get_subject_name(x509); - if (x509Name == nullptr) { - throw std::runtime_error("Couldn't initialize X509_NAME. X509_NAME(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Add additional data to certificate */ - QMapIterator certificateInformationList(additionalData); - while (certificateInformationList.hasNext()) { - /* Read next item in list */ - certificateInformationList.next(); - - /* Set additional data */ - if (!X509_NAME_add_entry_by_txt(x509Name, certificateInformationList.key().data(), MBSTRING_UTF8, reinterpret_cast(certificateInformationList.value().data()), -1, -1, 0)) { - throw std::runtime_error("Couldn't set additional information. X509_NAME_add_entry_by_txt(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - } - - /* Set certificate info */ - if (!X509_set_issuer_name(x509, x509Name)) { - throw std::runtime_error("Couldn't set issuer name. X509_set_issuer_name(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Sign certificate */ - if (!X509_sign(x509, keyStore.get(), md)) { - throw std::runtime_error("Couldn't sign X509. X509_sign(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write certificate file on disk. If needed */ - if (!certificateFileName.isEmpty()) { - /* Initialize BIO */ - std::unique_ptr certFile { BIO_new_file(certificateFileName.data(), "w+"), BIO_free_all }; - if (certFile == nullptr) { - throw std::runtime_error("Couldn't initialize certFile. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - - /* Write file on disk */ - if (!PEM_write_bio_X509(certFile.get(), x509)) { - throw std::runtime_error("Couldn't write certificate file on disk. PEM_write_bio_X509(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - } - } - - return x509; - } catch (std::exception& exception) { - QSimpleCrypto::QX509::error.setError(1, exception.what()); - return nullptr; - } catch (...) { - QSimpleCrypto::QX509::error.setError(2, "Unknown error!"); - return nullptr; - } -} diff --git a/client/3rd/QSimpleCrypto/sources/QX509Store.cpp b/client/3rd/QSimpleCrypto/sources/QX509Store.cpp deleted file mode 100644 index bbbec1a8..00000000 --- a/client/3rd/QSimpleCrypto/sources/QX509Store.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution -**/ - -#include "include/QX509Store.h" - -QSimpleCrypto::QX509Store::QX509Store() -{ -} - -/// -/// \brief QSimpleCrypto::QX509::addCertificateToStore -/// \param store - OpenSSL X509_STORE. -/// \param x509 - OpenSSL X509. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::addCertificateToStore(X509_STORE* store, X509* x509) -{ - if (!X509_STORE_add_cert(store, x509)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't add certificate to X509_STORE. X509_STORE_add_cert(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::addLookup -/// \param store - OpenSSL X509_STORE. -/// \param method - OpenSSL X509_LOOKUP_METHOD. Example: X509_LOOKUP_file. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::addLookup(X509_STORE* store, X509_LOOKUP_METHOD* method) -{ - if (!X509_STORE_add_lookup(store, method)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't add lookup to X509_STORE. X509_STORE_add_lookup(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::setCertificateDepth -/// \param store - OpenSSL X509_STORE. -/// \param depth - That is the maximum number of untrusted CA certificates that can appear in a chain. Example: 0. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::setDepth(X509_STORE* store, const int& depth) -{ - if (!X509_STORE_set_depth(store, depth)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set depth for X509_STORE. X509_STORE_set_depth(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::setFlag -/// \param store - OpenSSL X509_STORE. -/// \param flag - The verification flags consists of zero or more of the following flags ored together. Example: X509_V_FLAG_CRL_CHECK. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::setFlag(X509_STORE* store, const unsigned long& flag) -{ - if (!X509_STORE_set_flags(store, flag)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set flag for X509_STORE. X509_STORE_set_flags(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::setFlag -/// \param store - OpenSSL X509_STORE. -/// \param purpose - Verification purpose in param to purpose. Example: X509_PURPOSE_ANY. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::setPurpose(X509_STORE* store, const int& purpose) -{ - if (!X509_STORE_set_purpose(store, purpose)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set purpose for X509_STORE. X509_STORE_set_purpose(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::setTrust -/// \param store - OpenSSL X509_STORE. -/// \param trust - Trust Level. Example: X509_TRUST_SSL_SERVER. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::setTrust(X509_STORE* store, const int& trust) -{ - if (!X509_STORE_set_trust(store, trust)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set trust for X509_STORE. X509_STORE_set_trust(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::setDefaultPaths -/// \param store - OpenSSL X509_STORE. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::setDefaultPaths(X509_STORE* store) -{ - if (!X509_STORE_set_default_paths(store)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set default paths for X509_STORE. X509_STORE_set_default_paths(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::loadLocations -/// \param store - OpenSSL X509_STORE. -/// \param fileName - File name. Example: "caCertificate.pem". -/// \param dirPath - Path to file. Example: "path/To/File". -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::loadLocations(X509_STORE* store, const QByteArray& fileName, const QByteArray& dirPath) -{ - if (!X509_STORE_load_locations(store, fileName, dirPath)) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't load locations for X509_STORE. X509_STORE_load_locations(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::loadLocations -/// \param store - OpenSSL X509_STORE. -/// \param file - Qt QFile that will be loaded. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::loadLocations(X509_STORE* store, const QFile& file) -{ - /* Initialize QFileInfo to read information about file */ - QFileInfo info(file); - - if (!X509_STORE_load_locations(store, info.fileName().toLocal8Bit(), info.absoluteDir().path().toLocal8Bit())) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't load locations for X509_STORE. X509_STORE_load_locations(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} - -/// -/// \brief QSimpleCrypto::QX509Store::loadLocations -/// \param store - OpenSSL X509_STORE. -/// \param fileInfo - Qt QFileInfo. -/// \return Returns 'true' on success and 'false', if error happened. -/// -bool QSimpleCrypto::QX509Store::loadLocations(X509_STORE* store, const QFileInfo& fileInfo) -{ - if (!X509_STORE_load_locations(store, fileInfo.fileName().toLocal8Bit(), fileInfo.absoluteDir().path().toLocal8Bit())) { - QSimpleCrypto::QX509Store::error.setError(1, "Couldn't load locations for X509_STORE. X509_STORE_load_locations(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); - return false; - } - - return true; -} diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1fc28b82..327a83f6 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -24,6 +24,9 @@ execute_process( add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}") +add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}") +add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}") + if(IOS) set(PACKAGES ${PACKAGES} Multimedia) endif() @@ -34,7 +37,7 @@ endif() find_package(Qt6 REQUIRED COMPONENTS ${PACKAGES}) -set(LIBS ${LIBS} +set(LIBS ${LIBS} Qt6::Core Qt6::Gui Qt6::Network Qt6::Xml Qt6::RemoteObjects Qt6::Quick Qt6::Svg Qt6::QuickControls2 @@ -136,6 +139,7 @@ set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.h ${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h ${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h + ${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h ) # Mozilla headres @@ -252,7 +256,7 @@ set(SOURCES ${SOURCES} if(WIN32) configure_file( - ${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in + ${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc ) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index c3adfe31..b8ce5b00 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -3,13 +3,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include "logger.h" #include "ui/models/installedAppsModel.h" @@ -116,7 +116,7 @@ void AmneziaApplication::init() } connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) { - m_pageController->replaceStartPage(); + m_pageController->goToPageHome(); m_importController->extractConfigFromData(data); m_pageController->goToPageViewConfig(); }); @@ -127,13 +127,13 @@ void AmneziaApplication::init() #ifdef Q_OS_IOS IosController::Instance()->initialize(); connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) { - m_pageController->replaceStartPage(); + m_pageController->goToPageHome(); m_importController->extractConfigFromData(data); m_pageController->goToPageViewConfig(); }); connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) { - m_pageController->replaceStartPage(); + m_pageController->goToPageHome(); m_pageController->goToPageSettingsBackup(); m_settingsController->importBackupFromOutside(filePath); }); @@ -361,6 +361,18 @@ void AmneziaApplication::initModels() m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(), &ServersModel::clearCachedProfile); + + m_apiServicesModel.reset(new ApiServicesModel(this)); + m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get()); + + m_apiCountryModel.reset(new ApiCountryModel(this)); + m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get()); + connect(m_serversModel.get(), &ServersModel::updateApiLanguageModel, this, [this]() { + m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(), + m_serversModel->getProcessedServerData("apiServerCountryCode").toString()); + }); + connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this, + [this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); }); } void AmneziaApplication::initControllers() @@ -369,25 +381,26 @@ void AmneziaApplication::initControllers() new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings)); m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); - connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, [this](const QString &errorMessage) { - emit m_pageController->showErrorMessage(errorMessage); - emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); - }); + connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, + [this](const QString &errorMessage) { + emit m_pageController->showErrorMessage(errorMessage); + emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); + }); - connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, [this](ErrorCode errorCode) { - emit m_pageController->showErrorMessage(errorCode); - emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); - }); + connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, + [this](ErrorCode errorCode) { + emit m_pageController->showErrorMessage(errorCode); + emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); + }); connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(), &ConnectionController::toggleConnection, Qt::QueuedConnection); - connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated); - m_pageController.reset(new PageController(m_serversModel, m_settings)); m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); - m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings)); + m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, + m_apiServicesModel, m_settings)); m_engine->rootContext()->setContextProperty("InstallController", m_installController.get()); connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(), &PageController::showPassphraseRequestDrawer); @@ -396,6 +409,30 @@ void AmneziaApplication::initControllers() connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(), &ConnectionController::onCurrentContainerUpdated); + connect(m_installController.get(), &InstallController::updateServerFromApiFinished, this, [this]() { + disconnect(m_reloadConfigErrorOccurredConnection); + emit m_connectionController->configFromApiUpdated(); + }); + + connect(m_connectionController.get(), &ConnectionController::updateApiConfigFromGateway, this, [this]() { + m_reloadConfigErrorOccurredConnection = connect( + m_installController.get(), qOverload(&InstallController::installationErrorOccurred), this, + [this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); }, + static_cast(Qt::AutoConnection || Qt::SingleShotConnection)); + m_installController->updateServiceFromApi(m_serversModel->getDefaultServerIndex(), "", ""); + }); + + connect(m_connectionController.get(), &ConnectionController::updateApiConfigFromTelegram, this, [this]() { + m_reloadConfigErrorOccurredConnection = connect( + m_installController.get(), qOverload(&InstallController::installationErrorOccurred), this, + [this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); }, + static_cast(Qt::AutoConnection || Qt::SingleShotConnection)); + m_serversModel->removeApiConfig(m_serversModel->getDefaultServerIndex()); + m_installController->updateServiceFromTelegram(m_serversModel->getDefaultServerIndex()); + }); + + connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated); + m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); diff --git a/client/amnezia_application.h b/client/amnezia_application.h index b15d55d7..6fb61f44 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -45,6 +45,8 @@ #include "ui/models/sites_model.h" #include "ui/models/clientManagementModel.h" #include "ui/models/appSplitTunnelingModel.h" +#include "ui/models/apiServicesModel.h" +#include "ui/models/apiCountryModel.h" #define amnApp (static_cast(QCoreApplication::instance())) @@ -103,6 +105,8 @@ private: QSharedPointer m_sitesModel; QSharedPointer m_appSplitTunnelingModel; QSharedPointer m_clientManagementModel; + QSharedPointer m_apiServicesModel; + QSharedPointer m_apiCountryModel; QScopedPointer m_openVpnConfigModel; QScopedPointer m_shadowSocksConfigModel; @@ -134,6 +138,8 @@ private: QScopedPointer m_appSplitTunnelingController; QNetworkAccessManager *m_nam; + + QMetaObject::Connection m_reloadConfigErrorOccurredConnection; }; #endif // AMNEZIA_APPLICATION_H diff --git a/client/cmake/3rdparty.cmake b/client/cmake/3rdparty.cmake index ec544764..087f4961 100644 --- a/client/cmake/3rdparty.cmake +++ b/client/cmake/3rdparty.cmake @@ -8,9 +8,9 @@ endif() add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel) set(LIBS ${LIBS} SortFilterProxyModel) +include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake) include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake) -include(${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/QSimpleCrypto.cmake) set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/") set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/") @@ -83,13 +83,12 @@ set(BUILD_WITH_QT6 ON) add_subdirectory(${CLIENT_ROOT_DIR}/3rd/qtkeychain) set(LIBS ${LIBS} qt6keychain) - include_directories( ${OPENSSL_INCLUDE_DIR} ${LIBSSH_INCLUDE_DIR}/include ${LIBSSH_ROOT_DIR}/include ${CLIENT_ROOT_DIR}/3rd/libssh/include - ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/include + ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src/include ${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain ${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain ${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include diff --git a/client/cmake/QSimpleCrypto.cmake b/client/cmake/QSimpleCrypto.cmake new file mode 100644 index 00000000..ec43cb83 --- /dev/null +++ b/client/cmake/QSimpleCrypto.cmake @@ -0,0 +1,21 @@ +set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..) +set(QSIMPLECRYPTO_DIR ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src) + +include_directories(${QSIMPLECRYPTO_DIR}) + +set(HEADERS ${HEADERS} + ${QSIMPLECRYPTO_DIR}/include/QAead.h + ${QSIMPLECRYPTO_DIR}/include/QBlockCipher.h + ${QSIMPLECRYPTO_DIR}/include/QRsa.h + ${QSIMPLECRYPTO_DIR}/include/QSimpleCrypto_global.h + ${QSIMPLECRYPTO_DIR}/include/QX509.h + ${QSIMPLECRYPTO_DIR}/include/QX509Store.h +) + +set(SOURCES ${SOURCES} + ${QSIMPLECRYPTO_DIR}/sources/QAead.cpp + ${QSIMPLECRYPTO_DIR}/sources/QBlockCipher.cpp + ${QSIMPLECRYPTO_DIR}/sources/QRsa.cpp + ${QSIMPLECRYPTO_DIR}/sources/QX509.cpp + ${QSIMPLECRYPTO_DIR}/sources/QX509Store.cpp +) diff --git a/client/core/controllers/apiController.cpp b/client/core/controllers/apiController.cpp index 35b459be..8e5f8ed5 100644 --- a/client/core/controllers/apiController.cpp +++ b/client/core/controllers/apiController.cpp @@ -5,7 +5,11 @@ #include #include +#include "QBlockCipher.h" +#include "QRsa.h" + #include "amnezia_application.h" +#include "core/enums/apiEnums.h" #include "configurators/wireguard_configurator.h" #include "version.h" @@ -25,25 +29,74 @@ namespace constexpr char uuid[] = "installation_uuid"; constexpr char osVersion[] = "os_version"; constexpr char appVersion[] = "app_version"; + + constexpr char userCountryCode[] = "user_country_code"; + constexpr char serverCountryCode[] = "server_country_code"; + constexpr char serviceType[] = "service_type"; + + constexpr char aesKey[] = "aes_key"; + constexpr char aesIv[] = "aes_iv"; + constexpr char aesSalt[] = "aes_salt"; + + constexpr char apiPayload[] = "api_payload"; + constexpr char keyPayload[] = "key_payload"; + } + + const QStringList proxyStorageUrl = {""}; + + ErrorCode checkErrors(const QList &sslErrors, QNetworkReply *reply) + { + if (!sslErrors.empty()) { + qDebug().noquote() << sslErrors; + return ErrorCode::ApiConfigSslError; + } else if (reply->error() == QNetworkReply::NoError) { + return ErrorCode::NoError; + } else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError + || reply->error() == QNetworkReply::NetworkError::TimeoutError) { + return ErrorCode::ApiConfigTimeoutError; + } else { + QString err = reply->errorString(); + qDebug() << QString::fromUtf8(reply->readAll()); + qDebug() << reply->error(); + qDebug() << err; + qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + return ErrorCode::ApiConfigDownloadError; + } } } -ApiController::ApiController(QObject *parent) : QObject(parent) +ApiController::ApiController(const QString &gatewayEndpoint, QObject *parent) : QObject(parent), m_gatewayEndpoint(gatewayEndpoint) { } -void ApiController::processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config) +void ApiController::fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, + const QByteArray &apiResponseBody, QJsonObject &serverConfig) { - if (protocol == configKey::cloak) { - config.replace("", "\n"); - config.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey); + QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString(); + + data.replace("vpn://", ""); + QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + + if (ba.isEmpty()) { + emit errorOccurred(ErrorCode::ApiConfigEmptyError); return; + } + + QByteArray ba_uncompressed = qUncompress(ba); + if (!ba_uncompressed.isEmpty()) { + ba = ba_uncompressed; + } + + QString configStr = ba; + if (protocol == configKey::cloak) { + configStr.replace("", "\n"); + configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey); } else if (protocol == configKey::awg) { - config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); - auto serverConfig = QJsonDocument::fromJson(config.toUtf8()).object(); + configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); + auto serverConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); auto containers = serverConfig.value(config_key::containers).toArray(); if (containers.isEmpty()) { - return; + return; // todo process error } auto container = containers.at(0).toObject(); QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg); @@ -61,11 +114,75 @@ void ApiController::processApiConfig(const QString &protocol, const ApiControlle container[containerName] = containerConfig; containers.replace(0, container); serverConfig[config_key::containers] = containers; - config = QString(QJsonDocument(serverConfig).toJson()); + configStr = QString(QJsonDocument(serverConfig).toJson()); } + + QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); + serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1); + serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2); + serverConfig[config_key::containers] = apiConfig.value(config_key::containers); + serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName); + + if (apiConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) { + serverConfig[config_key::configVersion] = apiConfig.value(config_key::configVersion); + serverConfig[config_key::description] = apiConfig.value(config_key::description); + serverConfig[config_key::name] = apiConfig.value(config_key::name); + } + + auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString(); + serverConfig[config_key::defaultContainer] = defaultContainer; + return; } +QStringList ApiController::getProxyUrls() +{ + QNetworkRequest request; + request.setTransferTimeout(7000); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QEventLoop wait; + QList sslErrors; + QNetworkReply* reply; + + for (const auto &proxyStorageUrl : proxyStorageUrl) { + request.setUrl(proxyStorageUrl); + reply = amnApp->manager()->get(request); + + connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); + connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); + wait.exec(); + + if (reply->error() == QNetworkReply::NetworkError::NoError) { + break; + } + reply->deleteLater(); + } + + auto encryptedResponseBody = reply->readAll(); + reply->deleteLater(); + + EVP_PKEY *privateKey = nullptr; + QByteArray responseBody; + try { + QByteArray key = PROD_PROXY_STORAGE_KEY; + QSimpleCrypto::QRsa rsa; + privateKey = rsa.getPrivateKeyFromByteArray(key, ""); + responseBody = rsa.decrypt(encryptedResponseBody, privateKey, RSA_PKCS1_PADDING); + } catch (...) { + qCritical() << "error loading private key from environment variables or decrypting payload"; + return {}; + } + + auto endpointsArray = QJsonDocument::fromJson(responseBody).array(); + + QStringList endpoints; + for (const auto &endpoint : endpointsArray) { + endpoints.push_back(endpoint.toString()); + } + return endpoints; +} + ApiController::ApiPayloadData ApiController::generateApiPayloadData(const QString &protocol) { ApiController::ApiPayloadData apiPayload; @@ -101,8 +218,6 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c QThread::msleep(10); #endif - auto containerConfig = serverConfig.value(config_key::containers).toArray(); - if (serverConfig.value(config_key::configVersion).toInt()) { QNetworkRequest request; request.setTransferTimeout(7000); @@ -120,39 +235,13 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c QByteArray requestBody = QJsonDocument(apiPayload).toJson(); - QNetworkReply *reply = amnApp->manager()->post(request, requestBody); // ?? + QNetworkReply *reply = amnApp->manager()->post(request, requestBody); QObject::connect(reply, &QNetworkReply::finished, [this, reply, protocol, apiPayloadData, serverIndex, serverConfig]() mutable { if (reply->error() == QNetworkReply::NoError) { - QString contents = QString::fromUtf8(reply->readAll()); - QString data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString(); - - data.replace("vpn://", ""); - QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - - if (ba.isEmpty()) { - emit errorOccurred(ErrorCode::ApiConfigEmptyError); - return; - } - - QByteArray ba_uncompressed = qUncompress(ba); - if (!ba_uncompressed.isEmpty()) { - ba = ba_uncompressed; - } - - QString configStr = ba; - processApiConfig(protocol, apiPayloadData, configStr); - - QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); - serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1); - serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2); - serverConfig[config_key::containers] = apiConfig.value(config_key::containers); - serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName); - - auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString(); - serverConfig[config_key::defaultContainer] = defaultContainer; - - emit configUpdated(true, serverConfig, serverIndex); + auto apiResponseBody = reply->readAll(); + fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig); + emit finished(serverConfig, serverIndex); } else { if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) { @@ -178,3 +267,154 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c }); } } + +ErrorCode ApiController::getServicesList(QByteArray &responseBody) +{ +#ifdef Q_OS_IOS + IosController::Instance()->requestInetAccess(); + QThread::msleep(10); +#endif + + QNetworkRequest request; + request.setTransferTimeout(7000); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint)); + + QNetworkReply* reply; + reply = amnApp->manager()->get(request); + + QEventLoop wait; + QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); + + QList sslErrors; + connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); + wait.exec(); + + if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) { + m_proxyUrls = getProxyUrls(); + for (const QString &proxyUrl : m_proxyUrls) { + request.setUrl(QString("%1v1/services").arg(proxyUrl)); + reply = amnApp->manager()->get(request); + + QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); + connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); + wait.exec(); + if (reply->error() != QNetworkReply::NetworkError::TimeoutError && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) { + break; + } + reply->deleteLater(); + } + } + + responseBody = reply->readAll(); + auto errorCode = checkErrors(sslErrors, reply); + reply->deleteLater(); + return errorCode; +} + +ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, + const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig) +{ +#ifdef Q_OS_IOS + IosController::Instance()->requestInetAccess(); + QThread::msleep(10); +#endif + + QNetworkAccessManager manager; + QNetworkRequest request; + request.setTransferTimeout(7000); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint)); + + ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); + + QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = userCountryCode; + if (!serverCountryCode.isEmpty()) { + apiPayload[configKey::serverCountryCode] = serverCountryCode; + } + apiPayload[configKey::serviceType] = serviceType; + apiPayload[configKey::uuid] = installationUuid; + + QSimpleCrypto::QBlockCipher blockCipher; + QByteArray key = blockCipher.generatePrivateSalt(32); + QByteArray iv = blockCipher.generatePrivateSalt(32); + QByteArray salt = blockCipher.generatePrivateSalt(8); + + QJsonObject keyPayload; + keyPayload[configKey::aesKey] = QString(key.toBase64()); + keyPayload[configKey::aesIv] = QString(iv.toBase64()); + keyPayload[configKey::aesSalt] = QString(salt.toBase64()); + + QByteArray encryptedKeyPayload; + QByteArray encryptedApiPayload; + try { + QSimpleCrypto::QRsa rsa; + + EVP_PKEY *publicKey = nullptr; + try { + QByteArray key = PROD_AGW_PUBLIC_KEY; + QSimpleCrypto::QRsa rsa; + publicKey = rsa.getPublicKeyFromByteArray(key); + } catch (...) { + qCritical() << "error loading public key from environment variables"; + return ErrorCode::ApiMissingAgwPublicKey; + } + + encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING); + EVP_PKEY_free(publicKey); + + encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt); + } catch (...) { // todo change error handling in QSimpleCrypto? + qCritical() << "error when encrypting the request body"; + } + + QJsonObject requestBody; + requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64()); + requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64()); + + QNetworkReply* reply = manager.post(request, QJsonDocument(requestBody).toJson()); + + QEventLoop wait; + connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); + + QList sslErrors; + connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); + wait.exec(); + + if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) { + if (m_proxyUrls.isEmpty()) { + m_proxyUrls = getProxyUrls(); + } + for (const QString &proxyUrl : m_proxyUrls) { + request.setUrl(QString("%1v1/config").arg(proxyUrl)); + reply = manager.post(request, QJsonDocument(requestBody).toJson()); + + QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); + connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); + wait.exec(); + if (reply->error() != QNetworkReply::NetworkError::TimeoutError && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) { + break; + } + reply->deleteLater(); + } + } + + auto errorCode = checkErrors(sslErrors, reply); + if (errorCode) { + return errorCode; + } + + auto encryptedResponseBody = reply->readAll(); + reply->deleteLater(); + try { + auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt); + fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig); + } catch (...) { // todo change error handling in QSimpleCrypto? + qCritical() << "error when decrypting the request body"; + } + + return errorCode; +} diff --git a/client/core/controllers/apiController.h b/client/core/controllers/apiController.h index cc5d9f31..6cfde983 100644 --- a/client/core/controllers/apiController.h +++ b/client/core/controllers/apiController.h @@ -14,14 +14,18 @@ class ApiController : public QObject Q_OBJECT public: - explicit ApiController(QObject *parent = nullptr); + explicit ApiController(const QString &gatewayEndpoint, QObject *parent = nullptr); public slots: void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig); + ErrorCode getServicesList(QByteArray &responseBody); + ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, + const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig); + signals: void errorOccurred(ErrorCode errorCode); - void configUpdated(const bool updateConfig, const QJsonObject &config, const int serverIndex); + void finished(const QJsonObject &config, const int serverIndex); private: struct ApiPayloadData @@ -34,7 +38,12 @@ private: ApiPayloadData generateApiPayloadData(const QString &protocol); QJsonObject fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData); - void processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config); + void fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody, + QJsonObject &serverConfig); + QStringList getProxyUrls(); + + QString m_gatewayEndpoint; + QStringList m_proxyUrls; }; #endif // APICONTROLLER_H diff --git a/client/core/defs.h b/client/core/defs.h index a441ee1c..ebc07f4b 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -106,6 +106,7 @@ namespace amnezia ApiConfigEmptyError = 1102, ApiConfigTimeoutError = 1103, ApiConfigSslError = 1104, + ApiMissingAgwPublicKey = 1105, // QFile errors OpenError = 1200, diff --git a/client/core/enums/apiEnums.h b/client/core/enums/apiEnums.h new file mode 100644 index 00000000..1f050007 --- /dev/null +++ b/client/core/enums/apiEnums.h @@ -0,0 +1,9 @@ +#ifndef APIENUMS_H +#define APIENUMS_H + +enum ApiConfigSources { + Telegram = 1, + AmneziaGateway +}; + +#endif // APIENUMS_H diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 645ec6c5..8c16d786 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -60,6 +60,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiConfigEmptyError): errorMessage = QObject::tr("In the response from the server, an empty config was received"); break; case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break; case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break; + case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/images/amneziaBigLogo.svg b/client/images/amneziaBigLogo.svg deleted file mode 100644 index c50c7743..00000000 --- a/client/images/amneziaBigLogo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/animation.gif b/client/images/animation.gif deleted file mode 100644 index 6f7f38e8..00000000 Binary files a/client/images/animation.gif and /dev/null differ diff --git a/client/images/arrow_left.png b/client/images/arrow_left.png deleted file mode 100644 index 3a4d149d..00000000 Binary files a/client/images/arrow_left.png and /dev/null differ diff --git a/client/images/background_connected.png b/client/images/background_connected.png deleted file mode 100644 index 62480d5f..00000000 Binary files a/client/images/background_connected.png and /dev/null differ diff --git a/client/images/background_connected@2x.png b/client/images/background_connected@2x.png deleted file mode 100644 index 4f76b956..00000000 Binary files a/client/images/background_connected@2x.png and /dev/null differ diff --git a/client/images/check.png b/client/images/check.png deleted file mode 100644 index 43039eb1..00000000 Binary files a/client/images/check.png and /dev/null differ diff --git a/client/images/close.png b/client/images/close.png deleted file mode 100644 index 072232c1..00000000 Binary files a/client/images/close.png and /dev/null differ diff --git a/client/images/connected.png b/client/images/connected.png deleted file mode 100644 index b3c907c8..00000000 Binary files a/client/images/connected.png and /dev/null differ diff --git a/client/images/connectionOff.svg b/client/images/connectionOff.svg deleted file mode 100644 index 27905ff9..00000000 --- a/client/images/connectionOff.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/client/images/connectionOn.svg b/client/images/connectionOn.svg deleted file mode 100644 index ef317622..00000000 --- a/client/images/connectionOn.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/client/images/connectionProgress.svg b/client/images/connectionProgress.svg deleted file mode 100644 index 8c4024c9..00000000 --- a/client/images/connectionProgress.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/images/controls/archive-restore.svg b/client/images/controls/archive-restore.svg new file mode 100644 index 00000000..d3ad8c9e --- /dev/null +++ b/client/images/controls/archive-restore.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/client/images/controls/bug.svg b/client/images/controls/bug.svg new file mode 100644 index 00000000..80fd2277 --- /dev/null +++ b/client/images/controls/bug.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/client/images/controls/folder-search-2.svg b/client/images/controls/folder-search-2.svg new file mode 100644 index 00000000..f77ce57d --- /dev/null +++ b/client/images/controls/folder-search-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/client/images/controls/gauge.svg b/client/images/controls/gauge.svg new file mode 100644 index 00000000..4b9c1444 --- /dev/null +++ b/client/images/controls/gauge.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/images/controls/help-circle.svg b/client/images/controls/help-circle.svg new file mode 100644 index 00000000..7bcd4450 --- /dev/null +++ b/client/images/controls/help-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/client/images/controls/history.svg b/client/images/controls/history.svg new file mode 100644 index 00000000..73beb8b3 --- /dev/null +++ b/client/images/controls/history.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/client/images/controls/info.svg b/client/images/controls/info.svg new file mode 100644 index 00000000..43a40245 --- /dev/null +++ b/client/images/controls/info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/client/images/controls/map-pin.svg b/client/images/controls/map-pin.svg new file mode 100644 index 00000000..64b75b48 --- /dev/null +++ b/client/images/controls/map-pin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/images/controls/refresh-cw.svg b/client/images/controls/refresh-cw.svg new file mode 100644 index 00000000..9572e3e2 --- /dev/null +++ b/client/images/controls/refresh-cw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/client/images/controls/scan-line.svg b/client/images/controls/scan-line.svg new file mode 100644 index 00000000..be4acc2a --- /dev/null +++ b/client/images/controls/scan-line.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/client/images/controls/tag.svg b/client/images/controls/tag.svg new file mode 100644 index 00000000..88a9db8d --- /dev/null +++ b/client/images/controls/tag.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/images/delete.png b/client/images/delete.png deleted file mode 100644 index 59687bd2..00000000 Binary files a/client/images/delete.png and /dev/null differ diff --git a/client/images/disconnected.png b/client/images/disconnected.png deleted file mode 100644 index 199f71dc..00000000 Binary files a/client/images/disconnected.png and /dev/null differ diff --git a/client/images/download.png b/client/images/download.png deleted file mode 100644 index 0e949133..00000000 Binary files a/client/images/download.png and /dev/null differ diff --git a/client/images/favorites_disabled.png b/client/images/favorites_disabled.png deleted file mode 100644 index 12a821ac..00000000 Binary files a/client/images/favorites_disabled.png and /dev/null differ diff --git a/client/images/favorites_enabled.png b/client/images/favorites_enabled.png deleted file mode 100644 index 61e28f42..00000000 Binary files a/client/images/favorites_enabled.png and /dev/null differ diff --git a/client/images/favorites_hover.png b/client/images/favorites_hover.png deleted file mode 100644 index 71e7a1b2..00000000 Binary files a/client/images/favorites_hover.png and /dev/null differ diff --git a/client/images/flagKit/AD.svg b/client/images/flagKit/AD.svg new file mode 100644 index 00000000..4855f9fb --- /dev/null +++ b/client/images/flagKit/AD.svg @@ -0,0 +1,35 @@ + + + + AD + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AE.svg b/client/images/flagKit/AE.svg new file mode 100644 index 00000000..3095fe31 --- /dev/null +++ b/client/images/flagKit/AE.svg @@ -0,0 +1,33 @@ + + + + AE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AF.svg b/client/images/flagKit/AF.svg new file mode 100644 index 00000000..75216b74 --- /dev/null +++ b/client/images/flagKit/AF.svg @@ -0,0 +1,34 @@ + + + + AF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AG.svg b/client/images/flagKit/AG.svg new file mode 100644 index 00000000..ac56b808 --- /dev/null +++ b/client/images/flagKit/AG.svg @@ -0,0 +1,44 @@ + + + + AG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AI.svg b/client/images/flagKit/AI.svg new file mode 100644 index 00000000..7f53e464 --- /dev/null +++ b/client/images/flagKit/AI.svg @@ -0,0 +1,50 @@ + + + + AI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AL.svg b/client/images/flagKit/AL.svg new file mode 100644 index 00000000..43ff1a3b --- /dev/null +++ b/client/images/flagKit/AL.svg @@ -0,0 +1,27 @@ + + + + AL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AM.svg b/client/images/flagKit/AM.svg new file mode 100644 index 00000000..5224d30f --- /dev/null +++ b/client/images/flagKit/AM.svg @@ -0,0 +1,32 @@ + + + + AM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AO.svg b/client/images/flagKit/AO.svg new file mode 100644 index 00000000..86044f3b --- /dev/null +++ b/client/images/flagKit/AO.svg @@ -0,0 +1,37 @@ + + + + AO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AR.svg b/client/images/flagKit/AR.svg new file mode 100644 index 00000000..4dbc96f1 --- /dev/null +++ b/client/images/flagKit/AR.svg @@ -0,0 +1,26 @@ + + + + AR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AS.svg b/client/images/flagKit/AS.svg new file mode 100644 index 00000000..afb37540 --- /dev/null +++ b/client/images/flagKit/AS.svg @@ -0,0 +1,36 @@ + + + + AS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AT.svg b/client/images/flagKit/AT.svg new file mode 100644 index 00000000..627245e3 --- /dev/null +++ b/client/images/flagKit/AT.svg @@ -0,0 +1,24 @@ + + + + AT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AU.svg b/client/images/flagKit/AU.svg new file mode 100644 index 00000000..aad6b1e6 --- /dev/null +++ b/client/images/flagKit/AU.svg @@ -0,0 +1,36 @@ + + + + AU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AW.svg b/client/images/flagKit/AW.svg new file mode 100644 index 00000000..892d8aa0 --- /dev/null +++ b/client/images/flagKit/AW.svg @@ -0,0 +1,30 @@ + + + + AW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AX.svg b/client/images/flagKit/AX.svg new file mode 100644 index 00000000..577cd268 --- /dev/null +++ b/client/images/flagKit/AX.svg @@ -0,0 +1,32 @@ + + + + AX + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/AZ.svg b/client/images/flagKit/AZ.svg new file mode 100644 index 00000000..3f082f33 --- /dev/null +++ b/client/images/flagKit/AZ.svg @@ -0,0 +1,33 @@ + + + + AZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BA.svg b/client/images/flagKit/BA.svg new file mode 100644 index 00000000..a16324e1 --- /dev/null +++ b/client/images/flagKit/BA.svg @@ -0,0 +1,32 @@ + + + + BA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BB.svg b/client/images/flagKit/BB.svg new file mode 100644 index 00000000..5c89e132 --- /dev/null +++ b/client/images/flagKit/BB.svg @@ -0,0 +1,38 @@ + + + + BB + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BD.svg b/client/images/flagKit/BD.svg new file mode 100644 index 00000000..e1a3cd31 --- /dev/null +++ b/client/images/flagKit/BD.svg @@ -0,0 +1,27 @@ + + + + BD + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BE.svg b/client/images/flagKit/BE.svg new file mode 100644 index 00000000..ac00173d --- /dev/null +++ b/client/images/flagKit/BE.svg @@ -0,0 +1,32 @@ + + + + BE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BF.svg b/client/images/flagKit/BF.svg new file mode 100644 index 00000000..5b4286bb --- /dev/null +++ b/client/images/flagKit/BF.svg @@ -0,0 +1,28 @@ + + + + BF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BG.svg b/client/images/flagKit/BG.svg new file mode 100644 index 00000000..e8256f47 --- /dev/null +++ b/client/images/flagKit/BG.svg @@ -0,0 +1,28 @@ + + + + BG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BH.svg b/client/images/flagKit/BH.svg new file mode 100644 index 00000000..e1c11093 --- /dev/null +++ b/client/images/flagKit/BH.svg @@ -0,0 +1,23 @@ + + + + BH + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BI.svg b/client/images/flagKit/BI.svg new file mode 100644 index 00000000..2f208253 --- /dev/null +++ b/client/images/flagKit/BI.svg @@ -0,0 +1,36 @@ + + + + BI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BJ.svg b/client/images/flagKit/BJ.svg new file mode 100644 index 00000000..b21c46e0 --- /dev/null +++ b/client/images/flagKit/BJ.svg @@ -0,0 +1,32 @@ + + + + BJ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BL.svg b/client/images/flagKit/BL.svg new file mode 100644 index 00000000..b99bc2c7 --- /dev/null +++ b/client/images/flagKit/BL.svg @@ -0,0 +1,42 @@ + + + + BL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BM.svg b/client/images/flagKit/BM.svg new file mode 100644 index 00000000..798dd8b9 --- /dev/null +++ b/client/images/flagKit/BM.svg @@ -0,0 +1,49 @@ + + + + BM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BN.svg b/client/images/flagKit/BN.svg new file mode 100644 index 00000000..1fe9afc4 --- /dev/null +++ b/client/images/flagKit/BN.svg @@ -0,0 +1,28 @@ + + + + BN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BO.svg b/client/images/flagKit/BO.svg new file mode 100644 index 00000000..7ee247bd --- /dev/null +++ b/client/images/flagKit/BO.svg @@ -0,0 +1,32 @@ + + + + BO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BR.svg b/client/images/flagKit/BR.svg new file mode 100644 index 00000000..17edb103 --- /dev/null +++ b/client/images/flagKit/BR.svg @@ -0,0 +1,35 @@ + + + + BR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BS.svg b/client/images/flagKit/BS.svg new file mode 100644 index 00000000..767423af --- /dev/null +++ b/client/images/flagKit/BS.svg @@ -0,0 +1,33 @@ + + + + BS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BT.svg b/client/images/flagKit/BT.svg new file mode 100644 index 00000000..d2f749bd --- /dev/null +++ b/client/images/flagKit/BT.svg @@ -0,0 +1,27 @@ + + + + BT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BV.svg b/client/images/flagKit/BV.svg new file mode 100644 index 00000000..00a47ee5 --- /dev/null +++ b/client/images/flagKit/BV.svg @@ -0,0 +1,28 @@ + + + + BV + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BW.svg b/client/images/flagKit/BW.svg new file mode 100644 index 00000000..ccac652b --- /dev/null +++ b/client/images/flagKit/BW.svg @@ -0,0 +1,29 @@ + + + + BW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BY.svg b/client/images/flagKit/BY.svg new file mode 100644 index 00000000..d584988d --- /dev/null +++ b/client/images/flagKit/BY.svg @@ -0,0 +1,30 @@ + + + + BY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/BZ.svg b/client/images/flagKit/BZ.svg new file mode 100644 index 00000000..8758df23 --- /dev/null +++ b/client/images/flagKit/BZ.svg @@ -0,0 +1,30 @@ + + + + BZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CA.svg b/client/images/flagKit/CA.svg new file mode 100644 index 00000000..786b609b --- /dev/null +++ b/client/images/flagKit/CA.svg @@ -0,0 +1,25 @@ + + + + CA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CC.svg b/client/images/flagKit/CC.svg new file mode 100644 index 00000000..b96f3016 --- /dev/null +++ b/client/images/flagKit/CC.svg @@ -0,0 +1,33 @@ + + + + CC + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CD.svg b/client/images/flagKit/CD.svg new file mode 100644 index 00000000..0d351c30 --- /dev/null +++ b/client/images/flagKit/CD.svg @@ -0,0 +1,31 @@ + + + + CD + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CF.svg b/client/images/flagKit/CF.svg new file mode 100644 index 00000000..68566a2e --- /dev/null +++ b/client/images/flagKit/CF.svg @@ -0,0 +1,43 @@ + + + + CF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CG.svg b/client/images/flagKit/CG.svg new file mode 100644 index 00000000..bc4eb95b --- /dev/null +++ b/client/images/flagKit/CG.svg @@ -0,0 +1,34 @@ + + + + CG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CH.svg b/client/images/flagKit/CH.svg new file mode 100644 index 00000000..772f4fa3 --- /dev/null +++ b/client/images/flagKit/CH.svg @@ -0,0 +1,23 @@ + + + + CH + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CI.svg b/client/images/flagKit/CI.svg new file mode 100644 index 00000000..096d98ab --- /dev/null +++ b/client/images/flagKit/CI.svg @@ -0,0 +1,28 @@ + + + + CI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CK.svg b/client/images/flagKit/CK.svg new file mode 100644 index 00000000..c1ea3734 --- /dev/null +++ b/client/images/flagKit/CK.svg @@ -0,0 +1,31 @@ + + + + CK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CL.svg b/client/images/flagKit/CL.svg new file mode 100644 index 00000000..d456d951 --- /dev/null +++ b/client/images/flagKit/CL.svg @@ -0,0 +1,29 @@ + + + + CL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CM.svg b/client/images/flagKit/CM.svg new file mode 100644 index 00000000..482f4a97 --- /dev/null +++ b/client/images/flagKit/CM.svg @@ -0,0 +1,38 @@ + + + + CM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CN.svg b/client/images/flagKit/CN.svg new file mode 100644 index 00000000..883ba157 --- /dev/null +++ b/client/images/flagKit/CN.svg @@ -0,0 +1,32 @@ + + + + CN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CO.svg b/client/images/flagKit/CO.svg new file mode 100644 index 00000000..be492e3d --- /dev/null +++ b/client/images/flagKit/CO.svg @@ -0,0 +1,32 @@ + + + + CO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CR.svg b/client/images/flagKit/CR.svg new file mode 100644 index 00000000..271204eb --- /dev/null +++ b/client/images/flagKit/CR.svg @@ -0,0 +1,29 @@ + + + + CR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CU.svg b/client/images/flagKit/CU.svg new file mode 100644 index 00000000..23750cd9 --- /dev/null +++ b/client/images/flagKit/CU.svg @@ -0,0 +1,32 @@ + + + + CU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CV.svg b/client/images/flagKit/CV.svg new file mode 100644 index 00000000..4b6152fb --- /dev/null +++ b/client/images/flagKit/CV.svg @@ -0,0 +1,30 @@ + + + + CV + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CW.svg b/client/images/flagKit/CW.svg new file mode 100644 index 00000000..14acd27f --- /dev/null +++ b/client/images/flagKit/CW.svg @@ -0,0 +1,29 @@ + + + + CW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CX.svg b/client/images/flagKit/CX.svg new file mode 100644 index 00000000..b3fe73d9 --- /dev/null +++ b/client/images/flagKit/CX.svg @@ -0,0 +1,38 @@ + + + + CX + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CY.svg b/client/images/flagKit/CY.svg new file mode 100644 index 00000000..b7860aa9 --- /dev/null +++ b/client/images/flagKit/CY.svg @@ -0,0 +1,24 @@ + + + + CY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/CZ.svg b/client/images/flagKit/CZ.svg new file mode 100644 index 00000000..d56c61b8 --- /dev/null +++ b/client/images/flagKit/CZ.svg @@ -0,0 +1,28 @@ + + + + CZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/DE.svg b/client/images/flagKit/DE.svg new file mode 100644 index 00000000..4ff1ebd5 --- /dev/null +++ b/client/images/flagKit/DE.svg @@ -0,0 +1,32 @@ + + + + DE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/DJ.svg b/client/images/flagKit/DJ.svg new file mode 100644 index 00000000..c0a019f9 --- /dev/null +++ b/client/images/flagKit/DJ.svg @@ -0,0 +1,33 @@ + + + + DJ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/DK.svg b/client/images/flagKit/DK.svg new file mode 100644 index 00000000..27900e15 --- /dev/null +++ b/client/images/flagKit/DK.svg @@ -0,0 +1,23 @@ + + + + DK + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/DM.svg b/client/images/flagKit/DM.svg new file mode 100644 index 00000000..d5c401eb --- /dev/null +++ b/client/images/flagKit/DM.svg @@ -0,0 +1,41 @@ + + + + DM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/DO.svg b/client/images/flagKit/DO.svg new file mode 100644 index 00000000..9188e0be --- /dev/null +++ b/client/images/flagKit/DO.svg @@ -0,0 +1,33 @@ + + + + DO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/DZ.svg b/client/images/flagKit/DZ.svg new file mode 100644 index 00000000..0920d712 --- /dev/null +++ b/client/images/flagKit/DZ.svg @@ -0,0 +1,29 @@ + + + + DZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/EC.svg b/client/images/flagKit/EC.svg new file mode 100644 index 00000000..0fbd3ea6 --- /dev/null +++ b/client/images/flagKit/EC.svg @@ -0,0 +1,39 @@ + + + + EC + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/EE.svg b/client/images/flagKit/EE.svg new file mode 100644 index 00000000..63605223 --- /dev/null +++ b/client/images/flagKit/EE.svg @@ -0,0 +1,28 @@ + + + + EE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/EG.svg b/client/images/flagKit/EG.svg new file mode 100644 index 00000000..32d4447e --- /dev/null +++ b/client/images/flagKit/EG.svg @@ -0,0 +1,30 @@ + + + + EG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ER.svg b/client/images/flagKit/ER.svg new file mode 100644 index 00000000..bb70368b --- /dev/null +++ b/client/images/flagKit/ER.svg @@ -0,0 +1,40 @@ + + + + ER + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ES.svg b/client/images/flagKit/ES.svg new file mode 100644 index 00000000..883554f8 --- /dev/null +++ b/client/images/flagKit/ES.svg @@ -0,0 +1,34 @@ + + + + ES + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ET.svg b/client/images/flagKit/ET.svg new file mode 100644 index 00000000..c4387b9f --- /dev/null +++ b/client/images/flagKit/ET.svg @@ -0,0 +1,42 @@ + + + + ET + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/EU.svg b/client/images/flagKit/EU.svg new file mode 100644 index 00000000..db74ffaf --- /dev/null +++ b/client/images/flagKit/EU.svg @@ -0,0 +1,27 @@ + + + + EU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/FI.svg b/client/images/flagKit/FI.svg new file mode 100644 index 00000000..9d243ed5 --- /dev/null +++ b/client/images/flagKit/FI.svg @@ -0,0 +1,22 @@ + + + + FI + Created with sketchtool. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/FJ.svg b/client/images/flagKit/FJ.svg new file mode 100644 index 00000000..e3ebc9bb --- /dev/null +++ b/client/images/flagKit/FJ.svg @@ -0,0 +1,51 @@ + + + + FJ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/FK.svg b/client/images/flagKit/FK.svg new file mode 100644 index 00000000..01b0f2a8 --- /dev/null +++ b/client/images/flagKit/FK.svg @@ -0,0 +1,58 @@ + + + + FK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/FM.svg b/client/images/flagKit/FM.svg new file mode 100644 index 00000000..befd157c --- /dev/null +++ b/client/images/flagKit/FM.svg @@ -0,0 +1,23 @@ + + + + FM + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/FO.svg b/client/images/flagKit/FO.svg new file mode 100644 index 00000000..77618c05 --- /dev/null +++ b/client/images/flagKit/FO.svg @@ -0,0 +1,27 @@ + + + + FO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/FR.svg b/client/images/flagKit/FR.svg new file mode 100644 index 00000000..940de616 --- /dev/null +++ b/client/images/flagKit/FR.svg @@ -0,0 +1,28 @@ + + + + FR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GA.svg b/client/images/flagKit/GA.svg new file mode 100644 index 00000000..45c68087 --- /dev/null +++ b/client/images/flagKit/GA.svg @@ -0,0 +1,32 @@ + + + + GA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GB.svg b/client/images/flagKit/GB.svg new file mode 100644 index 00000000..679d27c7 --- /dev/null +++ b/client/images/flagKit/GB.svg @@ -0,0 +1,32 @@ + + + + GB + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GD.svg b/client/images/flagKit/GD.svg new file mode 100644 index 00000000..210dc3fd --- /dev/null +++ b/client/images/flagKit/GD.svg @@ -0,0 +1,49 @@ + + + + GD + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GE.svg b/client/images/flagKit/GE.svg new file mode 100644 index 00000000..818f3f5b --- /dev/null +++ b/client/images/flagKit/GE.svg @@ -0,0 +1,26 @@ + + + + GE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GF.svg b/client/images/flagKit/GF.svg new file mode 100644 index 00000000..bae1448d --- /dev/null +++ b/client/images/flagKit/GF.svg @@ -0,0 +1,32 @@ + + + + GF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GG.svg b/client/images/flagKit/GG.svg new file mode 100644 index 00000000..fa428535 --- /dev/null +++ b/client/images/flagKit/GG.svg @@ -0,0 +1,27 @@ + + + + GG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GH.svg b/client/images/flagKit/GH.svg new file mode 100644 index 00000000..528473ff --- /dev/null +++ b/client/images/flagKit/GH.svg @@ -0,0 +1,37 @@ + + + + GH + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GI.svg b/client/images/flagKit/GI.svg new file mode 100644 index 00000000..ecd8530a --- /dev/null +++ b/client/images/flagKit/GI.svg @@ -0,0 +1,38 @@ + + + + GI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GL.svg b/client/images/flagKit/GL.svg new file mode 100644 index 00000000..33b22333 --- /dev/null +++ b/client/images/flagKit/GL.svg @@ -0,0 +1,33 @@ + + + + GL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GM.svg b/client/images/flagKit/GM.svg new file mode 100644 index 00000000..b6330f52 --- /dev/null +++ b/client/images/flagKit/GM.svg @@ -0,0 +1,33 @@ + + + + GM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GN.svg b/client/images/flagKit/GN.svg new file mode 100644 index 00000000..2d20595e --- /dev/null +++ b/client/images/flagKit/GN.svg @@ -0,0 +1,32 @@ + + + + GN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GP.svg b/client/images/flagKit/GP.svg new file mode 100644 index 00000000..3dbdcc13 --- /dev/null +++ b/client/images/flagKit/GP.svg @@ -0,0 +1,40 @@ + + + + GP + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GQ.svg b/client/images/flagKit/GQ.svg new file mode 100644 index 00000000..e2d5c67d --- /dev/null +++ b/client/images/flagKit/GQ.svg @@ -0,0 +1,34 @@ + + + + GQ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GR.svg b/client/images/flagKit/GR.svg new file mode 100644 index 00000000..a9b12c00 --- /dev/null +++ b/client/images/flagKit/GR.svg @@ -0,0 +1,22 @@ + + + + GR + Created with sketchtool. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GS.svg b/client/images/flagKit/GS.svg new file mode 100644 index 00000000..03984521 --- /dev/null +++ b/client/images/flagKit/GS.svg @@ -0,0 +1,112 @@ + + + + GS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GT.svg b/client/images/flagKit/GT.svg new file mode 100644 index 00000000..be45ee89 --- /dev/null +++ b/client/images/flagKit/GT.svg @@ -0,0 +1,26 @@ + + + + GT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GU.svg b/client/images/flagKit/GU.svg new file mode 100644 index 00000000..6233a0bb --- /dev/null +++ b/client/images/flagKit/GU.svg @@ -0,0 +1,65 @@ + + + + GU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GW.svg b/client/images/flagKit/GW.svg new file mode 100644 index 00000000..b09530d4 --- /dev/null +++ b/client/images/flagKit/GW.svg @@ -0,0 +1,37 @@ + + + + GW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/GY.svg b/client/images/flagKit/GY.svg new file mode 100644 index 00000000..e5937c24 --- /dev/null +++ b/client/images/flagKit/GY.svg @@ -0,0 +1,42 @@ + + + + GY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/HK.svg b/client/images/flagKit/HK.svg new file mode 100644 index 00000000..f99b8882 --- /dev/null +++ b/client/images/flagKit/HK.svg @@ -0,0 +1,23 @@ + + + + HK + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/HM.svg b/client/images/flagKit/HM.svg new file mode 100644 index 00000000..8ef4f346 --- /dev/null +++ b/client/images/flagKit/HM.svg @@ -0,0 +1,36 @@ + + + + HM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/HN.svg b/client/images/flagKit/HN.svg new file mode 100644 index 00000000..50a48cd9 --- /dev/null +++ b/client/images/flagKit/HN.svg @@ -0,0 +1,33 @@ + + + + HN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/HR.svg b/client/images/flagKit/HR.svg new file mode 100644 index 00000000..a6cf5daa --- /dev/null +++ b/client/images/flagKit/HR.svg @@ -0,0 +1,35 @@ + + + + HR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/HT.svg b/client/images/flagKit/HT.svg new file mode 100644 index 00000000..0cd82be1 --- /dev/null +++ b/client/images/flagKit/HT.svg @@ -0,0 +1,46 @@ + + + + HT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/HU.svg b/client/images/flagKit/HU.svg new file mode 100644 index 00000000..795319ea --- /dev/null +++ b/client/images/flagKit/HU.svg @@ -0,0 +1,28 @@ + + + + HU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ID.svg b/client/images/flagKit/ID.svg new file mode 100644 index 00000000..8101da05 --- /dev/null +++ b/client/images/flagKit/ID.svg @@ -0,0 +1,23 @@ + + + + ID + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IE.svg b/client/images/flagKit/IE.svg new file mode 100644 index 00000000..60d9af87 --- /dev/null +++ b/client/images/flagKit/IE.svg @@ -0,0 +1,28 @@ + + + + IE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IL.svg b/client/images/flagKit/IL.svg new file mode 100644 index 00000000..7646f91e --- /dev/null +++ b/client/images/flagKit/IL.svg @@ -0,0 +1,26 @@ + + + + IL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IM.svg b/client/images/flagKit/IM.svg new file mode 100644 index 00000000..ecc7c12e --- /dev/null +++ b/client/images/flagKit/IM.svg @@ -0,0 +1,30 @@ + + + + IM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IN.svg b/client/images/flagKit/IN.svg new file mode 100644 index 00000000..3726ceb7 --- /dev/null +++ b/client/images/flagKit/IN.svg @@ -0,0 +1,31 @@ + + + + IN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IO.svg b/client/images/flagKit/IO.svg new file mode 100644 index 00000000..4d8b5229 --- /dev/null +++ b/client/images/flagKit/IO.svg @@ -0,0 +1,33 @@ + + + + IO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IQ.svg b/client/images/flagKit/IQ.svg new file mode 100644 index 00000000..16c4cf18 --- /dev/null +++ b/client/images/flagKit/IQ.svg @@ -0,0 +1,33 @@ + + + + IQ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IR.svg b/client/images/flagKit/IR.svg new file mode 100644 index 00000000..af325017 --- /dev/null +++ b/client/images/flagKit/IR.svg @@ -0,0 +1,31 @@ + + + + IR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IS.svg b/client/images/flagKit/IS.svg new file mode 100644 index 00000000..385a2bf9 --- /dev/null +++ b/client/images/flagKit/IS.svg @@ -0,0 +1,28 @@ + + + + IS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/IT.svg b/client/images/flagKit/IT.svg new file mode 100644 index 00000000..9e76f24c --- /dev/null +++ b/client/images/flagKit/IT.svg @@ -0,0 +1,28 @@ + + + + IT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/JE.svg b/client/images/flagKit/JE.svg new file mode 100644 index 00000000..6663c504 --- /dev/null +++ b/client/images/flagKit/JE.svg @@ -0,0 +1,32 @@ + + + + JE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/JM.svg b/client/images/flagKit/JM.svg new file mode 100644 index 00000000..54779e77 --- /dev/null +++ b/client/images/flagKit/JM.svg @@ -0,0 +1,33 @@ + + + + JM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/JO.svg b/client/images/flagKit/JO.svg new file mode 100644 index 00000000..b0788e76 --- /dev/null +++ b/client/images/flagKit/JO.svg @@ -0,0 +1,34 @@ + + + + JO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/JP.svg b/client/images/flagKit/JP.svg new file mode 100644 index 00000000..0a655c04 --- /dev/null +++ b/client/images/flagKit/JP.svg @@ -0,0 +1,22 @@ + + + + JP + Created with sketchtool. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KE.svg b/client/images/flagKit/KE.svg new file mode 100644 index 00000000..6c6a6cf6 --- /dev/null +++ b/client/images/flagKit/KE.svg @@ -0,0 +1,43 @@ + + + + KE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KG.svg b/client/images/flagKit/KG.svg new file mode 100644 index 00000000..12e6a244 --- /dev/null +++ b/client/images/flagKit/KG.svg @@ -0,0 +1,28 @@ + + + + KG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KH.svg b/client/images/flagKit/KH.svg new file mode 100644 index 00000000..9ea454bb --- /dev/null +++ b/client/images/flagKit/KH.svg @@ -0,0 +1,29 @@ + + + + KH + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KI.svg b/client/images/flagKit/KI.svg new file mode 100644 index 00000000..e00e2352 --- /dev/null +++ b/client/images/flagKit/KI.svg @@ -0,0 +1,35 @@ + + + + KI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KM.svg b/client/images/flagKit/KM.svg new file mode 100644 index 00000000..2da152d5 --- /dev/null +++ b/client/images/flagKit/KM.svg @@ -0,0 +1,39 @@ + + + + KM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KN.svg b/client/images/flagKit/KN.svg new file mode 100644 index 00000000..e65b7b61 --- /dev/null +++ b/client/images/flagKit/KN.svg @@ -0,0 +1,39 @@ + + + + KN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KP.svg b/client/images/flagKit/KP.svg new file mode 100644 index 00000000..649feb27 --- /dev/null +++ b/client/images/flagKit/KP.svg @@ -0,0 +1,30 @@ + + + + KP + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KR.svg b/client/images/flagKit/KR.svg new file mode 100644 index 00000000..078665a5 --- /dev/null +++ b/client/images/flagKit/KR.svg @@ -0,0 +1,38 @@ + + + + KR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KW.svg b/client/images/flagKit/KW.svg new file mode 100644 index 00000000..a73b0113 --- /dev/null +++ b/client/images/flagKit/KW.svg @@ -0,0 +1,33 @@ + + + + KW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KY.svg b/client/images/flagKit/KY.svg new file mode 100644 index 00000000..2240dbc6 --- /dev/null +++ b/client/images/flagKit/KY.svg @@ -0,0 +1,44 @@ + + + + KY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/KZ.svg b/client/images/flagKit/KZ.svg new file mode 100644 index 00000000..6076ac54 --- /dev/null +++ b/client/images/flagKit/KZ.svg @@ -0,0 +1,29 @@ + + + + KZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LA.svg b/client/images/flagKit/LA.svg new file mode 100644 index 00000000..5b740da7 --- /dev/null +++ b/client/images/flagKit/LA.svg @@ -0,0 +1,29 @@ + + + + LA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LB.svg b/client/images/flagKit/LB.svg new file mode 100644 index 00000000..401a235d --- /dev/null +++ b/client/images/flagKit/LB.svg @@ -0,0 +1,29 @@ + + + + LB + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LC.svg b/client/images/flagKit/LC.svg new file mode 100644 index 00000000..8d809d3e --- /dev/null +++ b/client/images/flagKit/LC.svg @@ -0,0 +1,33 @@ + + + + LC + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LI.svg b/client/images/flagKit/LI.svg new file mode 100644 index 00000000..1160975a --- /dev/null +++ b/client/images/flagKit/LI.svg @@ -0,0 +1,27 @@ + + + + LI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LICENSE b/client/images/flagKit/LICENSE new file mode 100644 index 00000000..59f47bf1 --- /dev/null +++ b/client/images/flagKit/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Bowtie AB + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/client/images/flagKit/LK.svg b/client/images/flagKit/LK.svg new file mode 100644 index 00000000..55386d5c --- /dev/null +++ b/client/images/flagKit/LK.svg @@ -0,0 +1,43 @@ + + + + LK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LR.svg b/client/images/flagKit/LR.svg new file mode 100644 index 00000000..3d6cef1e --- /dev/null +++ b/client/images/flagKit/LR.svg @@ -0,0 +1,36 @@ + + + + LR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LS.svg b/client/images/flagKit/LS.svg new file mode 100644 index 00000000..3ec5277d --- /dev/null +++ b/client/images/flagKit/LS.svg @@ -0,0 +1,34 @@ + + + + LS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LT.svg b/client/images/flagKit/LT.svg new file mode 100644 index 00000000..8e592267 --- /dev/null +++ b/client/images/flagKit/LT.svg @@ -0,0 +1,32 @@ + + + + LT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LU.svg b/client/images/flagKit/LU.svg new file mode 100644 index 00000000..860e730b --- /dev/null +++ b/client/images/flagKit/LU.svg @@ -0,0 +1,28 @@ + + + + LU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LV.svg b/client/images/flagKit/LV.svg new file mode 100644 index 00000000..5d0255e6 --- /dev/null +++ b/client/images/flagKit/LV.svg @@ -0,0 +1,24 @@ + + + + LV + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/LY.svg b/client/images/flagKit/LY.svg new file mode 100644 index 00000000..4b9f2a0e --- /dev/null +++ b/client/images/flagKit/LY.svg @@ -0,0 +1,33 @@ + + + + LY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MA.svg b/client/images/flagKit/MA.svg new file mode 100644 index 00000000..cb22ba95 --- /dev/null +++ b/client/images/flagKit/MA.svg @@ -0,0 +1,23 @@ + + + + MA + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MC.svg b/client/images/flagKit/MC.svg new file mode 100644 index 00000000..207590a7 --- /dev/null +++ b/client/images/flagKit/MC.svg @@ -0,0 +1,23 @@ + + + + MC + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MD.svg b/client/images/flagKit/MD.svg new file mode 100644 index 00000000..301e93ee --- /dev/null +++ b/client/images/flagKit/MD.svg @@ -0,0 +1,42 @@ + + + + MD + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ME.svg b/client/images/flagKit/ME.svg new file mode 100644 index 00000000..9b0838e9 --- /dev/null +++ b/client/images/flagKit/ME.svg @@ -0,0 +1,29 @@ + + + + ME + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MF.svg b/client/images/flagKit/MF.svg new file mode 100644 index 00000000..c45b62a0 --- /dev/null +++ b/client/images/flagKit/MF.svg @@ -0,0 +1,28 @@ + + + + MF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MG.svg b/client/images/flagKit/MG.svg new file mode 100644 index 00000000..c173fdd5 --- /dev/null +++ b/client/images/flagKit/MG.svg @@ -0,0 +1,28 @@ + + + + MG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MH.svg b/client/images/flagKit/MH.svg new file mode 100644 index 00000000..e6b66091 --- /dev/null +++ b/client/images/flagKit/MH.svg @@ -0,0 +1,29 @@ + + + + MH + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MK.svg b/client/images/flagKit/MK.svg new file mode 100644 index 00000000..35b92297 --- /dev/null +++ b/client/images/flagKit/MK.svg @@ -0,0 +1,29 @@ + + + + MK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ML.svg b/client/images/flagKit/ML.svg new file mode 100644 index 00000000..babc6e59 --- /dev/null +++ b/client/images/flagKit/ML.svg @@ -0,0 +1,32 @@ + + + + ML + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MM.svg b/client/images/flagKit/MM.svg new file mode 100644 index 00000000..eb3c18a3 --- /dev/null +++ b/client/images/flagKit/MM.svg @@ -0,0 +1,33 @@ + + + + MM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MN.svg b/client/images/flagKit/MN.svg new file mode 100644 index 00000000..8af15a51 --- /dev/null +++ b/client/images/flagKit/MN.svg @@ -0,0 +1,33 @@ + + + + MN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MO.svg b/client/images/flagKit/MO.svg new file mode 100644 index 00000000..be4bc875 --- /dev/null +++ b/client/images/flagKit/MO.svg @@ -0,0 +1,26 @@ + + + + MO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MP.svg b/client/images/flagKit/MP.svg new file mode 100644 index 00000000..33151489 --- /dev/null +++ b/client/images/flagKit/MP.svg @@ -0,0 +1,29 @@ + + + + MP + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MQ.svg b/client/images/flagKit/MQ.svg new file mode 100644 index 00000000..adc82074 --- /dev/null +++ b/client/images/flagKit/MQ.svg @@ -0,0 +1,27 @@ + + + + MQ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MR.svg b/client/images/flagKit/MR.svg new file mode 100644 index 00000000..da5adee6 --- /dev/null +++ b/client/images/flagKit/MR.svg @@ -0,0 +1,27 @@ + + + + MR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MS.svg b/client/images/flagKit/MS.svg new file mode 100644 index 00000000..184c9178 --- /dev/null +++ b/client/images/flagKit/MS.svg @@ -0,0 +1,47 @@ + + + + MS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MT.svg b/client/images/flagKit/MT.svg new file mode 100644 index 00000000..5ce0b3fe --- /dev/null +++ b/client/images/flagKit/MT.svg @@ -0,0 +1,29 @@ + + + + MT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MU.svg b/client/images/flagKit/MU.svg new file mode 100644 index 00000000..f2c6f3f8 --- /dev/null +++ b/client/images/flagKit/MU.svg @@ -0,0 +1,37 @@ + + + + MU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MV.svg b/client/images/flagKit/MV.svg new file mode 100644 index 00000000..f10e07d5 --- /dev/null +++ b/client/images/flagKit/MV.svg @@ -0,0 +1,28 @@ + + + + MV + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MW.svg b/client/images/flagKit/MW.svg new file mode 100644 index 00000000..5b0cc5c6 --- /dev/null +++ b/client/images/flagKit/MW.svg @@ -0,0 +1,33 @@ + + + + MW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MX.svg b/client/images/flagKit/MX.svg new file mode 100644 index 00000000..7ed245bc --- /dev/null +++ b/client/images/flagKit/MX.svg @@ -0,0 +1,30 @@ + + + + MX + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MY.svg b/client/images/flagKit/MY.svg new file mode 100644 index 00000000..e7ff885f --- /dev/null +++ b/client/images/flagKit/MY.svg @@ -0,0 +1,32 @@ + + + + MY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/MZ.svg b/client/images/flagKit/MZ.svg new file mode 100644 index 00000000..7f553b00 --- /dev/null +++ b/client/images/flagKit/MZ.svg @@ -0,0 +1,43 @@ + + + + MZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NA.svg b/client/images/flagKit/NA.svg new file mode 100644 index 00000000..cb0ba69f --- /dev/null +++ b/client/images/flagKit/NA.svg @@ -0,0 +1,75 @@ + + + + NA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NC.svg b/client/images/flagKit/NC.svg new file mode 100644 index 00000000..bae580e8 --- /dev/null +++ b/client/images/flagKit/NC.svg @@ -0,0 +1,42 @@ + + + + NC + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NE.svg b/client/images/flagKit/NE.svg new file mode 100644 index 00000000..12bcf8a0 --- /dev/null +++ b/client/images/flagKit/NE.svg @@ -0,0 +1,33 @@ + + + + NE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NF.svg b/client/images/flagKit/NF.svg new file mode 100644 index 00000000..b707e52d --- /dev/null +++ b/client/images/flagKit/NF.svg @@ -0,0 +1,29 @@ + + + + NF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NG.svg b/client/images/flagKit/NG.svg new file mode 100644 index 00000000..4063ff84 --- /dev/null +++ b/client/images/flagKit/NG.svg @@ -0,0 +1,24 @@ + + + + NG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NI.svg b/client/images/flagKit/NI.svg new file mode 100644 index 00000000..7adb4ba4 --- /dev/null +++ b/client/images/flagKit/NI.svg @@ -0,0 +1,26 @@ + + + + NI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NL.svg b/client/images/flagKit/NL.svg new file mode 100644 index 00000000..c62f42ad --- /dev/null +++ b/client/images/flagKit/NL.svg @@ -0,0 +1,28 @@ + + + + NL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NO.svg b/client/images/flagKit/NO.svg new file mode 100644 index 00000000..cdc23f49 --- /dev/null +++ b/client/images/flagKit/NO.svg @@ -0,0 +1,28 @@ + + + + NO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NP.svg b/client/images/flagKit/NP.svg new file mode 100644 index 00000000..c879fa80 --- /dev/null +++ b/client/images/flagKit/NP.svg @@ -0,0 +1,35 @@ + + + + NP + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NR.svg b/client/images/flagKit/NR.svg new file mode 100644 index 00000000..1a6c3a21 --- /dev/null +++ b/client/images/flagKit/NR.svg @@ -0,0 +1,28 @@ + + + + NR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NU.svg b/client/images/flagKit/NU.svg new file mode 100644 index 00000000..3d9bc80c --- /dev/null +++ b/client/images/flagKit/NU.svg @@ -0,0 +1,41 @@ + + + + NU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/NZ.svg b/client/images/flagKit/NZ.svg new file mode 100644 index 00000000..c1f624df --- /dev/null +++ b/client/images/flagKit/NZ.svg @@ -0,0 +1,34 @@ + + + + NZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/OM.svg b/client/images/flagKit/OM.svg new file mode 100644 index 00000000..cb08ac82 --- /dev/null +++ b/client/images/flagKit/OM.svg @@ -0,0 +1,29 @@ + + + + OM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PA.svg b/client/images/flagKit/PA.svg new file mode 100644 index 00000000..d8516682 --- /dev/null +++ b/client/images/flagKit/PA.svg @@ -0,0 +1,30 @@ + + + + PA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PE.svg b/client/images/flagKit/PE.svg new file mode 100644 index 00000000..98a26cf2 --- /dev/null +++ b/client/images/flagKit/PE.svg @@ -0,0 +1,24 @@ + + + + PE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PF.svg b/client/images/flagKit/PF.svg new file mode 100644 index 00000000..b29385f4 --- /dev/null +++ b/client/images/flagKit/PF.svg @@ -0,0 +1,52 @@ + + + + PF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PG.svg b/client/images/flagKit/PG.svg new file mode 100644 index 00000000..0630fab6 --- /dev/null +++ b/client/images/flagKit/PG.svg @@ -0,0 +1,36 @@ + + + + PG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PH.svg b/client/images/flagKit/PH.svg new file mode 100644 index 00000000..4c1087b8 --- /dev/null +++ b/client/images/flagKit/PH.svg @@ -0,0 +1,33 @@ + + + + PH + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PK.svg b/client/images/flagKit/PK.svg new file mode 100644 index 00000000..7ecb09cf --- /dev/null +++ b/client/images/flagKit/PK.svg @@ -0,0 +1,32 @@ + + + + PK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PL.svg b/client/images/flagKit/PL.svg new file mode 100644 index 00000000..fadbd2d6 --- /dev/null +++ b/client/images/flagKit/PL.svg @@ -0,0 +1,23 @@ + + + + PL + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PM.svg b/client/images/flagKit/PM.svg new file mode 100644 index 00000000..1f39fd0a --- /dev/null +++ b/client/images/flagKit/PM.svg @@ -0,0 +1,66 @@ + + + + PM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PN.svg b/client/images/flagKit/PN.svg new file mode 100644 index 00000000..f2b2cc4f --- /dev/null +++ b/client/images/flagKit/PN.svg @@ -0,0 +1,51 @@ + + + + PN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PR.svg b/client/images/flagKit/PR.svg new file mode 100644 index 00000000..7d120445 --- /dev/null +++ b/client/images/flagKit/PR.svg @@ -0,0 +1,30 @@ + + + + PR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PS.svg b/client/images/flagKit/PS.svg new file mode 100644 index 00000000..e68583ba --- /dev/null +++ b/client/images/flagKit/PS.svg @@ -0,0 +1,33 @@ + + + + PS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PT.svg b/client/images/flagKit/PT.svg new file mode 100644 index 00000000..49b59be2 --- /dev/null +++ b/client/images/flagKit/PT.svg @@ -0,0 +1,38 @@ + + + + PT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PW.svg b/client/images/flagKit/PW.svg new file mode 100644 index 00000000..4ab7f166 --- /dev/null +++ b/client/images/flagKit/PW.svg @@ -0,0 +1,27 @@ + + + + PW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/PY.svg b/client/images/flagKit/PY.svg new file mode 100644 index 00000000..2ae00546 --- /dev/null +++ b/client/images/flagKit/PY.svg @@ -0,0 +1,30 @@ + + + + PY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/QA.svg b/client/images/flagKit/QA.svg new file mode 100644 index 00000000..985171d1 --- /dev/null +++ b/client/images/flagKit/QA.svg @@ -0,0 +1,23 @@ + + + + QA + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/RE.svg b/client/images/flagKit/RE.svg new file mode 100644 index 00000000..7e130938 --- /dev/null +++ b/client/images/flagKit/RE.svg @@ -0,0 +1,28 @@ + + + + RE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/RO.svg b/client/images/flagKit/RO.svg new file mode 100644 index 00000000..dd82b266 --- /dev/null +++ b/client/images/flagKit/RO.svg @@ -0,0 +1,32 @@ + + + + RO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/RS.svg b/client/images/flagKit/RS.svg new file mode 100644 index 00000000..892dd5e4 --- /dev/null +++ b/client/images/flagKit/RS.svg @@ -0,0 +1,39 @@ + + + + RS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/RU.svg b/client/images/flagKit/RU.svg new file mode 100644 index 00000000..a9ba65b5 --- /dev/null +++ b/client/images/flagKit/RU.svg @@ -0,0 +1,28 @@ + + + + RU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/RW.svg b/client/images/flagKit/RW.svg new file mode 100644 index 00000000..43b26156 --- /dev/null +++ b/client/images/flagKit/RW.svg @@ -0,0 +1,37 @@ + + + + RW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SA.svg b/client/images/flagKit/SA.svg new file mode 100644 index 00000000..735b986f --- /dev/null +++ b/client/images/flagKit/SA.svg @@ -0,0 +1,26 @@ + + + + SA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SB.svg b/client/images/flagKit/SB.svg new file mode 100644 index 00000000..768c45c0 --- /dev/null +++ b/client/images/flagKit/SB.svg @@ -0,0 +1,39 @@ + + + + SB + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SC.svg b/client/images/flagKit/SC.svg new file mode 100644 index 00000000..62b380b8 --- /dev/null +++ b/client/images/flagKit/SC.svg @@ -0,0 +1,43 @@ + + + + SC + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SD.svg b/client/images/flagKit/SD.svg new file mode 100644 index 00000000..c68d6b1b --- /dev/null +++ b/client/images/flagKit/SD.svg @@ -0,0 +1,33 @@ + + + + SD + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SE.svg b/client/images/flagKit/SE.svg new file mode 100644 index 00000000..bb4f4e11 --- /dev/null +++ b/client/images/flagKit/SE.svg @@ -0,0 +1,27 @@ + + + + SE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SG.svg b/client/images/flagKit/SG.svg new file mode 100644 index 00000000..27011483 --- /dev/null +++ b/client/images/flagKit/SG.svg @@ -0,0 +1,24 @@ + + + + SG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SH.svg b/client/images/flagKit/SH.svg new file mode 100644 index 00000000..e0dde764 --- /dev/null +++ b/client/images/flagKit/SH.svg @@ -0,0 +1,53 @@ + + + + SH + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SI.svg b/client/images/flagKit/SI.svg new file mode 100644 index 00000000..497f8705 --- /dev/null +++ b/client/images/flagKit/SI.svg @@ -0,0 +1,28 @@ + + + + SI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SJ.svg b/client/images/flagKit/SJ.svg new file mode 100644 index 00000000..bef7e505 --- /dev/null +++ b/client/images/flagKit/SJ.svg @@ -0,0 +1,28 @@ + + + + SJ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SK.svg b/client/images/flagKit/SK.svg new file mode 100644 index 00000000..2b8ba801 --- /dev/null +++ b/client/images/flagKit/SK.svg @@ -0,0 +1,46 @@ + + + + SK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SL.svg b/client/images/flagKit/SL.svg new file mode 100644 index 00000000..817419ef --- /dev/null +++ b/client/images/flagKit/SL.svg @@ -0,0 +1,28 @@ + + + + SL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SM.svg b/client/images/flagKit/SM.svg new file mode 100644 index 00000000..abf62171 --- /dev/null +++ b/client/images/flagKit/SM.svg @@ -0,0 +1,25 @@ + + + + SM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SN.svg b/client/images/flagKit/SN.svg new file mode 100644 index 00000000..09484160 --- /dev/null +++ b/client/images/flagKit/SN.svg @@ -0,0 +1,33 @@ + + + + SN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SO.svg b/client/images/flagKit/SO.svg new file mode 100644 index 00000000..6372e377 --- /dev/null +++ b/client/images/flagKit/SO.svg @@ -0,0 +1,23 @@ + + + + SO + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SR.svg b/client/images/flagKit/SR.svg new file mode 100644 index 00000000..97963b0c --- /dev/null +++ b/client/images/flagKit/SR.svg @@ -0,0 +1,34 @@ + + + + SR + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SS.svg b/client/images/flagKit/SS.svg new file mode 100644 index 00000000..e8d68dd0 --- /dev/null +++ b/client/images/flagKit/SS.svg @@ -0,0 +1,44 @@ + + + + SS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ST.svg b/client/images/flagKit/ST.svg new file mode 100644 index 00000000..4b355d71 --- /dev/null +++ b/client/images/flagKit/ST.svg @@ -0,0 +1,39 @@ + + + + ST + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SV.svg b/client/images/flagKit/SV.svg new file mode 100644 index 00000000..9bfdd5ce --- /dev/null +++ b/client/images/flagKit/SV.svg @@ -0,0 +1,30 @@ + + + + SV + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SX.svg b/client/images/flagKit/SX.svg new file mode 100644 index 00000000..ccefe037 --- /dev/null +++ b/client/images/flagKit/SX.svg @@ -0,0 +1,45 @@ + + + + SX + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SY.svg b/client/images/flagKit/SY.svg new file mode 100644 index 00000000..040530b6 --- /dev/null +++ b/client/images/flagKit/SY.svg @@ -0,0 +1,34 @@ + + + + SY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/SZ.svg b/client/images/flagKit/SZ.svg new file mode 100644 index 00000000..fc4120de --- /dev/null +++ b/client/images/flagKit/SZ.svg @@ -0,0 +1,47 @@ + + + + SZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TC.svg b/client/images/flagKit/TC.svg new file mode 100644 index 00000000..c3ea149a --- /dev/null +++ b/client/images/flagKit/TC.svg @@ -0,0 +1,40 @@ + + + + TC + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TD.svg b/client/images/flagKit/TD.svg new file mode 100644 index 00000000..74756faf --- /dev/null +++ b/client/images/flagKit/TD.svg @@ -0,0 +1,32 @@ + + + + TD + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TF.svg b/client/images/flagKit/TF.svg new file mode 100644 index 00000000..d1ea6918 --- /dev/null +++ b/client/images/flagKit/TF.svg @@ -0,0 +1,35 @@ + + + + TF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TG.svg b/client/images/flagKit/TG.svg new file mode 100644 index 00000000..e9f6360f --- /dev/null +++ b/client/images/flagKit/TG.svg @@ -0,0 +1,33 @@ + + + + TG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TH.svg b/client/images/flagKit/TH.svg new file mode 100644 index 00000000..1bf403a2 --- /dev/null +++ b/client/images/flagKit/TH.svg @@ -0,0 +1,29 @@ + + + + TH + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TJ.svg b/client/images/flagKit/TJ.svg new file mode 100644 index 00000000..77d6728b --- /dev/null +++ b/client/images/flagKit/TJ.svg @@ -0,0 +1,29 @@ + + + + TJ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TK.svg b/client/images/flagKit/TK.svg new file mode 100644 index 00000000..3cde9608 --- /dev/null +++ b/client/images/flagKit/TK.svg @@ -0,0 +1,31 @@ + + + + TK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TL.svg b/client/images/flagKit/TL.svg new file mode 100644 index 00000000..41b89521 --- /dev/null +++ b/client/images/flagKit/TL.svg @@ -0,0 +1,33 @@ + + + + TL + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TM.svg b/client/images/flagKit/TM.svg new file mode 100644 index 00000000..dac62a13 --- /dev/null +++ b/client/images/flagKit/TM.svg @@ -0,0 +1,74 @@ + + + + TM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TN.svg b/client/images/flagKit/TN.svg new file mode 100644 index 00000000..3ff74a9e --- /dev/null +++ b/client/images/flagKit/TN.svg @@ -0,0 +1,23 @@ + + + + TN + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TO.svg b/client/images/flagKit/TO.svg new file mode 100644 index 00000000..e0e42ee2 --- /dev/null +++ b/client/images/flagKit/TO.svg @@ -0,0 +1,28 @@ + + + + TO + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TR.svg b/client/images/flagKit/TR.svg new file mode 100644 index 00000000..e5c0924d --- /dev/null +++ b/client/images/flagKit/TR.svg @@ -0,0 +1,23 @@ + + + + TR + Created with sketchtool. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TT.svg b/client/images/flagKit/TT.svg new file mode 100644 index 00000000..69bdb9a9 --- /dev/null +++ b/client/images/flagKit/TT.svg @@ -0,0 +1,28 @@ + + + + TT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TV.svg b/client/images/flagKit/TV.svg new file mode 100644 index 00000000..839c97f1 --- /dev/null +++ b/client/images/flagKit/TV.svg @@ -0,0 +1,36 @@ + + + + TV + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TW.svg b/client/images/flagKit/TW.svg new file mode 100644 index 00000000..488d1120 --- /dev/null +++ b/client/images/flagKit/TW.svg @@ -0,0 +1,28 @@ + + + + TW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/TZ.svg b/client/images/flagKit/TZ.svg new file mode 100644 index 00000000..d652e211 --- /dev/null +++ b/client/images/flagKit/TZ.svg @@ -0,0 +1,37 @@ + + + + TZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/UA.svg b/client/images/flagKit/UA.svg new file mode 100644 index 00000000..8dac8366 --- /dev/null +++ b/client/images/flagKit/UA.svg @@ -0,0 +1,27 @@ + + + + UA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/UG.svg b/client/images/flagKit/UG.svg new file mode 100644 index 00000000..7fabd77c --- /dev/null +++ b/client/images/flagKit/UG.svg @@ -0,0 +1,37 @@ + + + + UG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/UM.svg b/client/images/flagKit/UM.svg new file mode 100644 index 00000000..1a8fc6a7 --- /dev/null +++ b/client/images/flagKit/UM.svg @@ -0,0 +1,28 @@ + + + + UM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/US.svg b/client/images/flagKit/US.svg new file mode 100644 index 00000000..846ec9d2 --- /dev/null +++ b/client/images/flagKit/US.svg @@ -0,0 +1,28 @@ + + + + US + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/UY.svg b/client/images/flagKit/UY.svg new file mode 100644 index 00000000..81c28154 --- /dev/null +++ b/client/images/flagKit/UY.svg @@ -0,0 +1,29 @@ + + + + UY + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/UZ.svg b/client/images/flagKit/UZ.svg new file mode 100644 index 00000000..f6cf2140 --- /dev/null +++ b/client/images/flagKit/UZ.svg @@ -0,0 +1,29 @@ + + + + UZ + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/VA.svg b/client/images/flagKit/VA.svg new file mode 100644 index 00000000..14c78aaa --- /dev/null +++ b/client/images/flagKit/VA.svg @@ -0,0 +1,39 @@ + + + + VA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/VC.svg b/client/images/flagKit/VC.svg new file mode 100644 index 00000000..22cc1d53 --- /dev/null +++ b/client/images/flagKit/VC.svg @@ -0,0 +1,37 @@ + + + + VC + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/VE.svg b/client/images/flagKit/VE.svg new file mode 100644 index 00000000..1a14634f --- /dev/null +++ b/client/images/flagKit/VE.svg @@ -0,0 +1,33 @@ + + + + VE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/VG.svg b/client/images/flagKit/VG.svg new file mode 100644 index 00000000..c3c31ed1 --- /dev/null +++ b/client/images/flagKit/VG.svg @@ -0,0 +1,42 @@ + + + + VG + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/VI.svg b/client/images/flagKit/VI.svg new file mode 100644 index 00000000..071cf62c --- /dev/null +++ b/client/images/flagKit/VI.svg @@ -0,0 +1,49 @@ + + + + VI + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/VN.svg b/client/images/flagKit/VN.svg new file mode 100644 index 00000000..2bb79564 --- /dev/null +++ b/client/images/flagKit/VN.svg @@ -0,0 +1,27 @@ + + + + VN + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/VU.svg b/client/images/flagKit/VU.svg new file mode 100644 index 00000000..26e02981 --- /dev/null +++ b/client/images/flagKit/VU.svg @@ -0,0 +1,38 @@ + + + + VU + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/WF.svg b/client/images/flagKit/WF.svg new file mode 100644 index 00000000..26a5e414 --- /dev/null +++ b/client/images/flagKit/WF.svg @@ -0,0 +1,28 @@ + + + + WF + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/WS.svg b/client/images/flagKit/WS.svg new file mode 100644 index 00000000..756c78f5 --- /dev/null +++ b/client/images/flagKit/WS.svg @@ -0,0 +1,28 @@ + + + + WS + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/XK.svg b/client/images/flagKit/XK.svg new file mode 100644 index 00000000..a9c245fd --- /dev/null +++ b/client/images/flagKit/XK.svg @@ -0,0 +1,28 @@ + + + + XK + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/YE.svg b/client/images/flagKit/YE.svg new file mode 100644 index 00000000..535406f9 --- /dev/null +++ b/client/images/flagKit/YE.svg @@ -0,0 +1,28 @@ + + + + YE + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/YT.svg b/client/images/flagKit/YT.svg new file mode 100644 index 00000000..be67985d --- /dev/null +++ b/client/images/flagKit/YT.svg @@ -0,0 +1,77 @@ + + + + YT + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ZA.svg b/client/images/flagKit/ZA.svg new file mode 100644 index 00000000..f3ad3726 --- /dev/null +++ b/client/images/flagKit/ZA.svg @@ -0,0 +1,44 @@ + + + + ZA + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ZM.svg b/client/images/flagKit/ZM.svg new file mode 100644 index 00000000..3e6f42a8 --- /dev/null +++ b/client/images/flagKit/ZM.svg @@ -0,0 +1,42 @@ + + + + ZM + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/flagKit/ZW.svg b/client/images/flagKit/ZW.svg new file mode 100644 index 00000000..dfaf1f3f --- /dev/null +++ b/client/images/flagKit/ZW.svg @@ -0,0 +1,43 @@ + + + + ZW + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/images/folder.png b/client/images/folder.png deleted file mode 100644 index 07407596..00000000 Binary files a/client/images/folder.png and /dev/null differ diff --git a/client/images/icon_src.png b/client/images/icon_src.png deleted file mode 100644 index 1201a89a..00000000 Binary files a/client/images/icon_src.png and /dev/null differ diff --git a/client/images/icon_src.svg b/client/images/icon_src.svg deleted file mode 100644 index b27d1360..00000000 --- a/client/images/icon_src.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - diff --git a/client/images/settings.png b/client/images/settings.png deleted file mode 100644 index a64c6116..00000000 Binary files a/client/images/settings.png and /dev/null differ diff --git a/client/images/settings_grey.png b/client/images/settings_grey.png deleted file mode 100644 index 60127b5c..00000000 Binary files a/client/images/settings_grey.png and /dev/null differ diff --git a/client/images/share.png b/client/images/share.png deleted file mode 100644 index e1451e06..00000000 Binary files a/client/images/share.png and /dev/null differ diff --git a/client/images/svg/close_black_24dp.svg b/client/images/svg/close_black_24dp.svg deleted file mode 100644 index 5f1267d7..00000000 --- a/client/images/svg/close_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/control_point_black_24dp.svg b/client/images/svg/control_point_black_24dp.svg deleted file mode 100644 index 75b25e67..00000000 --- a/client/images/svg/control_point_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/delete_black_24dp.svg b/client/images/svg/delete_black_24dp.svg deleted file mode 100644 index 69a68354..00000000 --- a/client/images/svg/delete_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/density_small_black_24dp.svg b/client/images/svg/density_small_black_24dp.svg deleted file mode 100644 index f79483de..00000000 --- a/client/images/svg/density_small_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/done_black_24dp.svg b/client/images/svg/done_black_24dp.svg deleted file mode 100644 index b7e19d35..00000000 --- a/client/images/svg/done_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/format_list_bulleted_black_24dp.svg b/client/images/svg/format_list_bulleted_black_24dp.svg deleted file mode 100644 index 21821a14..00000000 --- a/client/images/svg/format_list_bulleted_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/gpp_good_black_24dp.svg b/client/images/svg/gpp_good_black_24dp.svg deleted file mode 100644 index 45d4a819..00000000 --- a/client/images/svg/gpp_good_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/gpp_maybe_black_24dp.svg b/client/images/svg/gpp_maybe_black_24dp.svg deleted file mode 100644 index dceeac79..00000000 --- a/client/images/svg/gpp_maybe_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/logout_black_24dp.svg b/client/images/svg/logout_black_24dp.svg deleted file mode 100644 index 1b785f84..00000000 --- a/client/images/svg/logout_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/miscellaneous_services_black_24dp.svg b/client/images/svg/miscellaneous_services_black_24dp.svg deleted file mode 100644 index 425990e5..00000000 --- a/client/images/svg/miscellaneous_services_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/refresh_black_24dp.svg b/client/images/svg/refresh_black_24dp.svg deleted file mode 100644 index f31411f5..00000000 --- a/client/images/svg/refresh_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/settings_black_24dp.svg b/client/images/svg/settings_black_24dp.svg deleted file mode 100644 index 4165162b..00000000 --- a/client/images/svg/settings_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/settings_suggest_black_24dp.svg b/client/images/svg/settings_suggest_black_24dp.svg deleted file mode 100644 index 80053d0e..00000000 --- a/client/images/svg/settings_suggest_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/share_black_24dp.svg b/client/images/svg/share_black_24dp.svg deleted file mode 100644 index 4c5fa323..00000000 --- a/client/images/svg/share_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/svg/vpn_key_black_24dp.svg b/client/images/svg/vpn_key_black_24dp.svg deleted file mode 100644 index 2c18df46..00000000 --- a/client/images/svg/vpn_key_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/images/uncheck.png b/client/images/uncheck.png deleted file mode 100644 index b6723a44..00000000 Binary files a/client/images/uncheck.png and /dev/null differ diff --git a/client/images/upload.png b/client/images/upload.png deleted file mode 100644 index 185e7a7b..00000000 Binary files a/client/images/upload.png and /dev/null differ diff --git a/client/resources.qrc b/client/resources.qrc index 98f04802..fed42a00 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -1,18 +1,9 @@ - images/close.png - images/settings.png - images/favorites_disabled.png - images/favorites_enabled.png - images/favorites_hover.png - images/download.png - images/upload.png images/tray/active.png images/tray/default.png images/tray/error.png - images/arrow_left.png images/AmneziaVPN.png - images/share.png server_scripts/remove_container.sh server_scripts/setup_host_firewall.sh server_scripts/openvpn_cloak/Dockerfile @@ -22,9 +13,6 @@ server_scripts/install_docker.sh server_scripts/build_container.sh server_scripts/prepare_host.sh - images/check.png - images/uncheck.png - images/settings_grey.png server_scripts/check_connection.sh server_scripts/remove_all_containers.sh server_scripts/openvpn_cloak/run_container.sh @@ -38,7 +26,6 @@ server_scripts/openvpn_shadowsocks/run_container.sh server_scripts/openvpn_shadowsocks/start.sh server_scripts/openvpn_shadowsocks/template.ovpn - images/folder.png server_scripts/wireguard/configure_container.sh server_scripts/wireguard/Dockerfile server_scripts/wireguard/run_container.sh @@ -61,26 +48,6 @@ server_scripts/ipsec/start.sh server_scripts/ipsec/mobileconfig.plist server_scripts/ipsec/strongswan.profile - images/background_connected.png - images/background_connected@2x.png - images/delete.png - images/animation.gif - images/connected.png - images/disconnected.png - images/svg/gpp_good_black_24dp.svg - images/svg/gpp_maybe_black_24dp.svg - images/svg/close_black_24dp.svg - images/svg/delete_black_24dp.svg - images/svg/done_black_24dp.svg - images/svg/format_list_bulleted_black_24dp.svg - images/svg/logout_black_24dp.svg - images/svg/miscellaneous_services_black_24dp.svg - images/svg/refresh_black_24dp.svg - images/svg/settings_black_24dp.svg - images/svg/share_black_24dp.svg - images/svg/vpn_key_black_24dp.svg - images/svg/control_point_black_24dp.svg - images/svg/settings_suggest_black_24dp.svg server_scripts/website_tor/Dockerfile server_scripts/check_user_in_sudo.sh ui/qml/Controls2/BasicButtonType.qml @@ -96,7 +63,6 @@ ui/qml/Pages2/PageSetupWizardStart.qml ui/qml/main2.qml images/amneziaBigLogo.png - images/amneziaBigLogo.svg ui/qml/Controls2/FlickableType.qml ui/qml/Pages2/PageSetupWizardCredentials.qml ui/qml/Controls2/HeaderType.qml @@ -132,9 +98,6 @@ ui/qml/Controls2/Header2Type.qml images/controls/plus.svg ui/qml/Components/ConnectButton.qml - images/connectionProgress.svg - images/connectionOff.svg - images/connectionOn.svg images/controls/download.svg ui/qml/Controls2/ProgressBarType.qml ui/qml/Components/ConnectionTypeSelectionDrawer.qml @@ -236,5 +199,274 @@ server_scripts/socks5_proxy/Dockerfile server_scripts/socks5_proxy/configure_container.sh server_scripts/socks5_proxy/start.sh + ui/qml/Pages2/PageSetupWizardApiServicesList.qml + ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml + ui/qml/Controls2/CardWithIconsType.qml + images/controls/tag.svg + images/controls/history.svg + images/controls/gauge.svg + images/controls/map-pin.svg + ui/qml/Controls2/LabelWithImageType.qml + images/controls/info.svg + ui/qml/Controls2/TextAreaWithFooterType.qml + images/controls/scan-line.svg + images/controls/folder-search-2.svg + ui/qml/Pages2/PageSettingsApiServerInfo.qml + images/controls/bug.svg + ui/qml/Pages2/PageDevMenu.qml + images/controls/refresh-cw.svg + ui/qml/Pages2/PageSettingsApiLanguageList.qml + images/controls/archive-restore.svg + images/controls/help-circle.svg + + + images/flagKit/ZW.svg + images/flagKit/ZM.svg + images/flagKit/ZA.svg + images/flagKit/YT.svg + images/flagKit/YE.svg + images/flagKit/XK.svg + images/flagKit/WS.svg + images/flagKit/WF.svg + images/flagKit/VU.svg + images/flagKit/VN.svg + images/flagKit/VI.svg + images/flagKit/VG.svg + images/flagKit/VE.svg + images/flagKit/VC.svg + images/flagKit/VA.svg + images/flagKit/UZ.svg + images/flagKit/UY.svg + images/flagKit/US.svg + images/flagKit/UM.svg + images/flagKit/UG.svg + images/flagKit/UA.svg + images/flagKit/TZ.svg + images/flagKit/TW.svg + images/flagKit/TV.svg + images/flagKit/TT.svg + images/flagKit/TR.svg + images/flagKit/TO.svg + images/flagKit/TN.svg + images/flagKit/TM.svg + images/flagKit/TL.svg + images/flagKit/TK.svg + images/flagKit/TJ.svg + images/flagKit/TH.svg + images/flagKit/TG.svg + images/flagKit/TF.svg + images/flagKit/TD.svg + images/flagKit/TC.svg + images/flagKit/SZ.svg + images/flagKit/SY.svg + images/flagKit/SX.svg + images/flagKit/SV.svg + images/flagKit/ST.svg + images/flagKit/SS.svg + images/flagKit/SR.svg + images/flagKit/SO.svg + images/flagKit/SN.svg + images/flagKit/SM.svg + images/flagKit/SL.svg + images/flagKit/SK.svg + images/flagKit/SJ.svg + images/flagKit/SI.svg + images/flagKit/SH.svg + images/flagKit/SG.svg + images/flagKit/SE.svg + images/flagKit/SD.svg + images/flagKit/SC.svg + images/flagKit/SB.svg + images/flagKit/SA.svg + images/flagKit/RW.svg + images/flagKit/RU.svg + images/flagKit/RS.svg + images/flagKit/RO.svg + images/flagKit/RE.svg + images/flagKit/QA.svg + images/flagKit/PY.svg + images/flagKit/PW.svg + images/flagKit/PT.svg + images/flagKit/PS.svg + images/flagKit/PR.svg + images/flagKit/PN.svg + images/flagKit/PM.svg + images/flagKit/PL.svg + images/flagKit/PK.svg + images/flagKit/PH.svg + images/flagKit/PG.svg + images/flagKit/PF.svg + images/flagKit/PE.svg + images/flagKit/PA.svg + images/flagKit/OM.svg + images/flagKit/NZ.svg + images/flagKit/NU.svg + images/flagKit/NR.svg + images/flagKit/NP.svg + images/flagKit/NO.svg + images/flagKit/NL.svg + images/flagKit/NI.svg + images/flagKit/NG.svg + images/flagKit/NF.svg + images/flagKit/NE.svg + images/flagKit/NC.svg + images/flagKit/NA.svg + images/flagKit/MZ.svg + images/flagKit/MY.svg + images/flagKit/MX.svg + images/flagKit/MW.svg + images/flagKit/MV.svg + images/flagKit/MU.svg + images/flagKit/MT.svg + images/flagKit/MS.svg + images/flagKit/MR.svg + images/flagKit/MQ.svg + images/flagKit/MP.svg + images/flagKit/MO.svg + images/flagKit/MN.svg + images/flagKit/MM.svg + images/flagKit/ML.svg + images/flagKit/MK.svg + images/flagKit/MH.svg + images/flagKit/MG.svg + images/flagKit/MF.svg + images/flagKit/ME.svg + images/flagKit/MD.svg + images/flagKit/MC.svg + images/flagKit/MA.svg + images/flagKit/LY.svg + images/flagKit/LV.svg + images/flagKit/LU.svg + images/flagKit/LT.svg + images/flagKit/LS.svg + images/flagKit/LR.svg + images/flagKit/LK.svg + images/flagKit/LI.svg + images/flagKit/LC.svg + images/flagKit/LB.svg + images/flagKit/LA.svg + images/flagKit/KZ.svg + images/flagKit/KY.svg + images/flagKit/KW.svg + images/flagKit/KR.svg + images/flagKit/KP.svg + images/flagKit/KN.svg + images/flagKit/KM.svg + images/flagKit/KI.svg + images/flagKit/KH.svg + images/flagKit/KG.svg + images/flagKit/KE.svg + images/flagKit/JP.svg + images/flagKit/JO.svg + images/flagKit/JM.svg + images/flagKit/JE.svg + images/flagKit/IT.svg + images/flagKit/IS.svg + images/flagKit/IR.svg + images/flagKit/IQ.svg + images/flagKit/IO.svg + images/flagKit/IN.svg + images/flagKit/IM.svg + images/flagKit/IL.svg + images/flagKit/IE.svg + images/flagKit/ID.svg + images/flagKit/HU.svg + images/flagKit/HT.svg + images/flagKit/HR.svg + images/flagKit/HN.svg + images/flagKit/HM.svg + images/flagKit/HK.svg + images/flagKit/GY.svg + images/flagKit/GW.svg + images/flagKit/GU.svg + images/flagKit/GT.svg + images/flagKit/GS.svg + images/flagKit/GR.svg + images/flagKit/GQ.svg + images/flagKit/GP.svg + images/flagKit/GN.svg + images/flagKit/GM.svg + images/flagKit/GL.svg + images/flagKit/GI.svg + images/flagKit/GH.svg + images/flagKit/GG.svg + images/flagKit/GF.svg + images/flagKit/GE.svg + images/flagKit/GD.svg + images/flagKit/GB.svg + images/flagKit/GA.svg + images/flagKit/FR.svg + images/flagKit/FO.svg + images/flagKit/FM.svg + images/flagKit/FK.svg + images/flagKit/FJ.svg + images/flagKit/FI.svg + images/flagKit/EU.svg + images/flagKit/ET.svg + images/flagKit/ES.svg + images/flagKit/ER.svg + images/flagKit/EG.svg + images/flagKit/EE.svg + images/flagKit/EC.svg + images/flagKit/DZ.svg + images/flagKit/DO.svg + images/flagKit/DM.svg + images/flagKit/DK.svg + images/flagKit/DJ.svg + images/flagKit/DE.svg + images/flagKit/CZ.svg + images/flagKit/CY.svg + images/flagKit/CX.svg + images/flagKit/CW.svg + images/flagKit/CV.svg + images/flagKit/CU.svg + images/flagKit/CR.svg + images/flagKit/CO.svg + images/flagKit/CN.svg + images/flagKit/CM.svg + images/flagKit/CL.svg + images/flagKit/CK.svg + images/flagKit/CI.svg + images/flagKit/CH.svg + images/flagKit/CG.svg + images/flagKit/CF.svg + images/flagKit/CD.svg + images/flagKit/CC.svg + images/flagKit/CA.svg + images/flagKit/BZ.svg + images/flagKit/BY.svg + images/flagKit/BW.svg + images/flagKit/BV.svg + images/flagKit/BT.svg + images/flagKit/BS.svg + images/flagKit/BR.svg + images/flagKit/BO.svg + images/flagKit/BN.svg + images/flagKit/BM.svg + images/flagKit/BL.svg + images/flagKit/BJ.svg + images/flagKit/BI.svg + images/flagKit/BH.svg + images/flagKit/BG.svg + images/flagKit/BF.svg + images/flagKit/BE.svg + images/flagKit/BD.svg + images/flagKit/BB.svg + images/flagKit/BA.svg + images/flagKit/AZ.svg + images/flagKit/AX.svg + images/flagKit/AW.svg + images/flagKit/AU.svg + images/flagKit/AT.svg + images/flagKit/AS.svg + images/flagKit/AR.svg + images/flagKit/AO.svg + images/flagKit/AM.svg + images/flagKit/AL.svg + images/flagKit/AI.svg + images/flagKit/AG.svg + images/flagKit/AF.svg + images/flagKit/AE.svg + images/flagKit/AD.svg diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp index 029d3740..1e2a2273 100644 --- a/client/secure_qsettings.cpp +++ b/client/secure_qsettings.cpp @@ -200,7 +200,7 @@ QByteArray SecureQSettings::getEncKey() const if (m_key.isEmpty()) { // Create new key QSimpleCrypto::QBlockCipher cipher; - QByteArray key = cipher.generateSecureRandomBytes(32); + QByteArray key = cipher.generatePrivateSalt(32); if (key.isEmpty()) { qCritical() << "SecureQSettings::getEncKey Unable to generate new enc key"; } @@ -226,7 +226,7 @@ QByteArray SecureQSettings::getEncIv() const if (m_iv.isEmpty()) { // Create new IV QSimpleCrypto::QBlockCipher cipher; - QByteArray iv = cipher.generateSecureRandomBytes(32); + QByteArray iv = cipher.generatePrivateSalt(32); if (iv.isEmpty()) { qCritical() << "SecureQSettings::getEncIv Unable to generate new enc IV"; } diff --git a/client/settings.cpp b/client/settings.cpp index 872adec7..490ede52 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -1,7 +1,7 @@ #include "settings.h" -#include "QThread" #include "QCoreApplication" +#include "QThread" #include "core/networkUtilities.h" #include "version.h" @@ -9,8 +9,13 @@ #include "containers/containers_defs.h" #include "logger.h" -const char Settings::cloudFlareNs1[] = "1.1.1.1"; -const char Settings::cloudFlareNs2[] = "1.0.0.1"; +namespace +{ + const char cloudFlareNs1[] = "1.1.1.1"; + const char cloudFlareNs2[] = "1.0.0.1"; + + constexpr char gatewayEndpoint[] = "http://gw.amnezia.org:80/"; +} Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this) { @@ -37,6 +42,8 @@ Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_N m_settings.remove("Server/serverPort"); } } + + m_gatewayEndpoint = gatewayEndpoint; } int Settings::serversCount() const @@ -109,8 +116,7 @@ QMap Settings::containers(int serverIndex) const QMap containersMap; for (const QJsonValue &val : containers) { - containersMap.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()), - val.toObject()); + containersMap.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()), val.toObject()); } return containersMap; @@ -442,6 +448,17 @@ QString Settings::getInstallationUuid(const bool needCreate) auto uuid = value("Conf/installationUuid", "").toString(); if (needCreate && uuid.isEmpty()) { uuid = QUuid::createUuid().toString(); + + //remove {} from uuid + uuid.remove(0, 1); + uuid.chop(1); + + setInstallationUuid(uuid); + } else if (uuid.contains("{") && uuid.contains("}")) { + //remove {} from old uuid + uuid.remove(0, 1); + uuid.chop(1); + setInstallationUuid(uuid); } return uuid; @@ -476,11 +493,8 @@ QVariant Settings::value(const QString &key, const QVariant &defaultValue) const if (QThread::currentThread() == QCoreApplication::instance()->thread()) { returnValue = m_settings.value(key, defaultValue); } else { - QMetaObject::invokeMethod(&m_settings, "value", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QVariant, returnValue), - Q_ARG(const QString&, key), - Q_ARG(const QVariant&, defaultValue)); + QMetaObject::invokeMethod(&m_settings, "value", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, returnValue), + Q_ARG(const QString &, key), Q_ARG(const QVariant &, defaultValue)); } return returnValue; } @@ -490,9 +504,22 @@ void Settings::setValue(const QString &key, const QVariant &value) if (QThread::currentThread() == QCoreApplication::instance()->thread()) { m_settings.setValue(key, value); } else { - QMetaObject::invokeMethod(&m_settings, "setValue", - Qt::BlockingQueuedConnection, - Q_ARG(const QString&, key), - Q_ARG(const QVariant&, value)); + QMetaObject::invokeMethod(&m_settings, "setValue", Qt::BlockingQueuedConnection, Q_ARG(const QString &, key), + Q_ARG(const QVariant &, value)); } } + +void Settings::resetGatewayEndpoint() +{ + m_gatewayEndpoint = gatewayEndpoint; +} + +void Settings::setGatewayEndpoint(const QString &endpoint) +{ + m_gatewayEndpoint = endpoint; +} + +QString Settings::getGatewayEndpoint() +{ + return m_gatewayEndpoint; +} diff --git a/client/settings.h b/client/settings.h index 74d1b4b9..ee10c3b8 100644 --- a/client/settings.h +++ b/client/settings.h @@ -160,9 +160,6 @@ public: setValue("Conf/secondaryDns", secondaryDns); } - static const char cloudFlareNs1[]; - static const char cloudFlareNs2[]; - // static constexpr char openNicNs5[] = "94.103.153.176"; // static constexpr char openNicNs13[] = "144.76.103.143"; @@ -218,6 +215,10 @@ public: void setKillSwitchEnabled(bool enabled); QString getInstallationUuid(const bool needCreate); + void resetGatewayEndpoint(); + void setGatewayEndpoint(const QString &endpoint); + QString getGatewayEndpoint(); + signals: void saveLogsChanged(bool enabled); void screenshotsEnabledChanged(bool enabled); @@ -231,6 +232,8 @@ private: void setInstallationUuid(const QString &uuid); mutable SecureQSettings m_settings; + + QString m_gatewayEndpoint; }; #endif // SETTINGS_H diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 216775ba..c7f95000 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -8,6 +8,7 @@ #include #include "core/controllers/vpnConfigurationController.h" +#include "core/enums/apiEnums.h" #include "version.h" ConnectionController::ConnectionController(const QSharedPointer &serversModel, @@ -16,7 +17,6 @@ ConnectionController::ConnectionController(const QSharedPointer &s const QSharedPointer &vpnConnection, const std::shared_ptr &settings, QObject *parent) : QObject(parent), - m_apiController(this), m_serversModel(serversModel), m_containersModel(containersModel), m_clientManagementModel(clientManagementModel), @@ -27,9 +27,7 @@ ConnectionController::ConnectionController(const QSharedPointer &s connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn, Qt::QueuedConnection); connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn, Qt::QueuedConnection); - connect(&m_apiController, &ApiController::configUpdated, this, - static_cast(&ConnectionController::openConnection)); - connect(&m_apiController, qOverload(&ApiController::errorOccurred), this, qOverload(&ConnectionController::connectionErrorOccurred)); + connect(this, &ConnectionController::configFromApiUpdated, this, &ConnectionController::continueConnection); m_state = Vpn::ConnectionState::Disconnected; } @@ -46,14 +44,22 @@ void ConnectionController::openConnection() int serverIndex = m_serversModel->getDefaultServerIndex(); QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex); + auto configVersion = serverConfig.value(config_key::configVersion).toInt(); emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing); - if (serverConfig.value(config_key::configVersion).toInt() + if (configVersion == ApiConfigSources::Telegram && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { - m_apiController.updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverIndex, serverConfig); + emit updateApiConfigFromTelegram(); + } else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) { + qDebug() << "attempt to update api config by end_date event"; + if (configVersion == ApiConfigSources::Telegram) { + emit updateApiConfigFromTelegram(); + } else { + emit updateApiConfigFromGateway(); + } } else { - openConnection(false, serverConfig, serverIndex); + continueConnection(); } } @@ -185,12 +191,11 @@ bool ConnectionController::isProtocolConfigExists(const QJsonObject &containerCo return true; } -void ConnectionController::openConnection(const bool updateConfig, const QJsonObject &config, const int serverIndex) +void ConnectionController::continueConnection() { - // Update config for this server as it was received from API - if (updateConfig) { - m_serversModel->editServer(config, serverIndex); - } + int serverIndex = m_serversModel->getDefaultServerIndex(); + QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex); + auto configVersion = serverConfig.value(config_key::configVersion).toInt(); if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { emit noInstalledContainers(); @@ -223,7 +228,7 @@ void ConnectionController::openConnection(const bool updateConfig, const QJsonOb auto dns = m_serversModel->getDnsPair(serverIndex); - auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, config, containerConfig, container, errorCode); + auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container, errorCode); if (errorCode != ErrorCode::NoError) { emit connectionErrorOccurred(tr("unable to create configuration")); return; diff --git a/client/ui/controllers/connectionController.h b/client/ui/controllers/connectionController.h index bc8f7e2f..25d4d74a 100644 --- a/client/ui/controllers/connectionController.h +++ b/client/ui/controllers/connectionController.h @@ -1,7 +1,6 @@ #ifndef CONNECTIONCONTROLLER_H #define CONNECTIONCONTROLLER_H -#include "core/controllers/apiController.h" #include "protocols/vpnprotocol.h" #include "ui/models/clientManagementModel.h" #include "ui/models/containers_model.h" @@ -58,13 +57,15 @@ signals: void connectButtonClicked(); void preparingConfig(); + void updateApiConfigFromGateway(); + void updateApiConfigFromTelegram(); + void configFromApiUpdated(); + private: Vpn::ConnectionState getCurrentConnectionState(); bool isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container); - void openConnection(const bool updateConfig, const QJsonObject &config, const int serverIndex); - - ApiController m_apiController; + void continueConnection(); QSharedPointer m_serversModel; QSharedPointer m_containersModel; diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index a5605674..20c32409 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -133,7 +133,7 @@ ErrorCode ExportController::generateNativeConfig(const DockerContainer container int serverIndex = m_serversModel->getProcessedServerIndex(); ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); auto dns = m_serversModel->getDnsPair(serverIndex); - bool isApiConfig = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::IsServerFromApiRole)); + bool isApiConfig = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::IsServerFromTelegramApiRole)); QJsonObject containerConfig = m_containersModel->getContainerConfig(container); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 6396b364..66e04520 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -7,6 +7,7 @@ #include #include +#include "core/controllers/apiController.h" #include "core/controllers/serverController.h" #include "core/controllers/vpnConfigurationController.h" #include "core/networkUtilities.h" @@ -15,14 +16,24 @@ #include "ui/models/protocols/wireguardConfigModel.h" #include "utilities.h" -#ifdef Q_OS_IOS - #include -#endif - namespace { Logger logger("ServerController"); + namespace configKey + { + constexpr char serviceInfo[] = "service_info"; + constexpr char serviceType[] = "service_type"; + constexpr char serviceProtocol[] = "service_protocol"; + constexpr char userCountryCode[] = "user_country_code"; + + constexpr char serverCountryCode[] = "server_country_code"; + constexpr char serverCountryName[] = "server_country_name"; + constexpr char availableCountries[] = "available_countries"; + + constexpr char apiConfig[] = "api_config"; + } + #ifdef Q_OS_WINDOWS QString getNextDriverLetter() { @@ -52,12 +63,14 @@ namespace InstallController::InstallController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, const QSharedPointer &protocolsModel, const QSharedPointer &clientManagementModel, - const std::shared_ptr &settings, QObject *parent) + const QSharedPointer &apiServicesModel, const std::shared_ptr &settings, + QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_protocolModel(protocolsModel), m_clientManagementModel(clientManagementModel), + m_apiServicesModel(apiServicesModel), m_settings(settings) { } @@ -432,7 +445,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia containerConfig.insert(config_key::password, password); } else if (protocol == Proto::Socks5Proxy) { QString proxyConfig = serverController->getTextFileFromContainer(container, credentials, - protocols::socks5Proxy::proxyConfigPath, errorCode); + protocols::socks5Proxy::proxyConfigPath, errorCode); const static QRegularExpression usernameAndPasswordRegExp("users (\\w+):CL:(\\w+)"); QRegularExpressionMatch usernameAndPasswordMatch = usernameAndPasswordRegExp.match(proxyConfig); @@ -591,25 +604,8 @@ void InstallController::removeProcessedContainer() void InstallController::removeApiConfig(const int serverIndex) { - auto serverConfig = m_serversModel->getServerConfig(serverIndex); - -#ifdef Q_OS_IOS - QString vpncName = QString("%1 (%2) %3") - .arg(serverConfig[config_key::description].toString()) - .arg(serverConfig[config_key::hostName].toString()) - .arg(serverConfig[config_key::vpnproto].toString()); - - AmneziaVPN::removeVPNC(vpncName.toStdString()); -#endif - - serverConfig.remove(config_key::dns1); - serverConfig.remove(config_key::dns2); - serverConfig.remove(config_key::containers); - serverConfig.remove(config_key::hostName); - - serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None)); - - m_serversModel->editServer(serverConfig, serverIndex); + m_serversModel->removeApiConfig(serverIndex); + emit apiConfigRemoved(tr("Api config removed")); } void InstallController::clearCachedProfile(QSharedPointer serverController) @@ -801,6 +797,110 @@ void InstallController::addEmptyServer() emit installServerFinished(tr("Server added successfully")); } +bool InstallController::fillAvailableServices() +{ + ApiController apiController(m_settings->getGatewayEndpoint()); + + QByteArray responseBody; + ErrorCode errorCode = apiController.getServicesList(responseBody); + if (errorCode != ErrorCode::NoError) { + emit installationErrorOccurred(errorCode); + return false; + } + + QJsonObject data = QJsonDocument::fromJson(responseBody).object(); + m_apiServicesModel->updateModel(data); + return true; +} + +bool InstallController::installServiceFromApi() +{ + if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(), + m_apiServicesModel->getSelectedServiceProtocol())) { + emit installationErrorOccurred(ErrorCode::ApiConfigAlreadyAdded); + return false; + } + + ApiController apiController(m_settings->getGatewayEndpoint()); + QJsonObject serverConfig; + + ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(), + m_apiServicesModel->getSelectedServiceType(), + m_apiServicesModel->getSelectedServiceProtocol(), "", serverConfig); + if (errorCode != ErrorCode::NoError) { + emit installationErrorOccurred(errorCode); + return false; + } + + auto serviceInfo = m_apiServicesModel->getSelectedServiceInfo(); + QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + apiConfig.insert(configKey::serviceInfo, serviceInfo); + apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode()); + apiConfig.insert(configKey::serviceType, m_apiServicesModel->getSelectedServiceType()); + apiConfig.insert(configKey::serviceProtocol, m_apiServicesModel->getSelectedServiceProtocol()); + + serverConfig.insert(configKey::apiConfig, apiConfig); + + m_serversModel->addServer(serverConfig); + emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName())); + return true; +} + +bool InstallController::updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, + bool reloadServiceConfig) +{ + ApiController apiController(m_settings->getGatewayEndpoint()); + + auto serverConfig = m_serversModel->getServerConfig(serverIndex); + auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + + QJsonObject newServerConfig; + ErrorCode errorCode = + apiController.getConfigForService(m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), + apiConfig.value(configKey::serviceType).toString(), + apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, newServerConfig); + if (errorCode != ErrorCode::NoError) { + emit installationErrorOccurred(errorCode); + return false; + } + + QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject(); + newApiConfig.insert(configKey::serviceInfo, apiConfig.value(configKey::serviceInfo)); + newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode)); + newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType)); + newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol)); + + newServerConfig.insert(configKey::apiConfig, newApiConfig); + m_serversModel->editServer(newServerConfig, serverIndex); + + if (reloadServiceConfig) { + emit reloadServerFromApiFinished(tr("API config reloaded")); + } else if (newCountryName.isEmpty()) { + emit updateServerFromApiFinished(); + } else { + emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName)); + } + return true; +} + +void InstallController::updateServiceFromTelegram(const int serverIndex) +{ + ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint()); + + auto serverConfig = m_serversModel->getServerConfig(serverIndex); + + apiController->updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverIndex, serverConfig); + connect(apiController, &ApiController::finished, this, [this, apiController](const QJsonObject &config, const int serverIndex) { + m_serversModel->editServer(config, serverIndex); + emit updateServerFromApiFinished(); + apiController->deleteLater(); + }); + connect(apiController, &ApiController::errorOccurred, this, [this, apiController](ErrorCode errorCode) { + emit installationErrorOccurred(errorCode); + apiController->deleteLater(); + }); +} + bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig) { diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index 5e7fd41d..7eea216a 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -10,6 +10,7 @@ #include "ui/models/containers_model.h" #include "ui/models/protocols_model.h" #include "ui/models/servers_model.h" +#include "ui/models/apiServicesModel.h" class InstallController : public QObject { @@ -18,6 +19,7 @@ public: explicit InstallController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, const QSharedPointer &protocolsModel, const QSharedPointer &clientManagementModel, + const QSharedPointer &apiServicesModel, const std::shared_ptr &settings, QObject *parent = nullptr); ~InstallController(); @@ -50,11 +52,21 @@ public slots: void addEmptyServer(); + bool fillAvailableServices(); + bool installServiceFromApi(); + bool updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool reloadServiceConfig = false); + + void updateServiceFromTelegram(const int serverIndex); + signals: void installContainerFinished(const QString &finishMessage, bool isServiceInstall); void installServerFinished(const QString &finishMessage); + void installServerFromApiFinished(const QString &message); void updateContainerFinished(const QString &message); + void updateServerFromApiFinished(); + void changeApiCountryFinished(const QString &message); + void reloadServerFromApiFinished(const QString &message); void scanServerFinished(bool isInstalledContainerFound); @@ -77,6 +89,7 @@ signals: void currentContainerUpdated(); void cachedProfileCleared(const QString &message); + void apiConfigRemoved(const QString &message); private: void installServer(const DockerContainer container, const QMap &installedContainers, @@ -95,6 +108,8 @@ private: QSharedPointer m_containersModel; QSharedPointer m_protocolModel; QSharedPointer m_clientManagementModel; + QSharedPointer m_apiServicesModel; + std::shared_ptr m_settings; ServerCredentials m_processedServerCredentials; diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index 3c6583d3..b9561600 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -46,16 +46,16 @@ PageController::PageController(const QSharedPointer &serversModel, m_isTriggeredByConnectButton = false; } -QString PageController::getInitialPage() +bool PageController::isStartPageVisible() { if (m_serversModel->getServersCount()) { if (m_serversModel->getDefaultServerIndex() < 0) { auto defaultServerIndex = m_serversModel->index(0); m_serversModel->setData(defaultServerIndex, true, ServersModel::Roles::IsDefaultRole); } - return getPagePath(PageLoader::PageEnum::PageStart); + return false; } else { - return getPagePath(PageLoader::PageEnum::PageSetupWizardStart); + return true; } } diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index c9d655ba..2cc2d983 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -47,6 +47,8 @@ namespace PageLoader PageSetupWizardTextKey, PageSetupWizardViewConfig, PageSetupWizardQrReader, + PageSetupWizardApiServicesList, + PageSetupWizardApiServiceInfo, PageProtocolOpenVpnSettings, PageProtocolShadowSocksSettings, @@ -57,7 +59,9 @@ namespace PageLoader PageProtocolIKev2Settings, PageProtocolRaw, - PageShareFullAccess + PageShareFullAccess, + + PageDevMenu }; Q_ENUM_NS(PageEnum) @@ -75,7 +79,7 @@ public: QObject *parent = nullptr); public slots: - QString getInitialPage(); + bool isStartPageVisible(); QString getPagePath(PageLoader::PageEnum page); void closeWindow(); @@ -110,7 +114,6 @@ signals: void closePage(); void restorePageHomeState(bool isContainerInstalled = false); - void replaceStartPage(); void showErrorMessage(amnezia::ErrorCode); void showErrorMessage(const QString &errorMessage); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index aceac551..93fd8971 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -252,3 +252,36 @@ void SettingsController::requestNotificationPermission() AndroidController::instance()->requestNotificationPermission(); #endif } + +QString SettingsController::getInstallationUuid() +{ + return m_settings->getInstallationUuid(false); +} + +void SettingsController::enableDevMode() +{ + m_isDevModeEnabled = true; + emit devModeEnabled(); +} + +bool SettingsController::isDevModeEnabled() +{ + return m_isDevModeEnabled; +} + +void SettingsController::resetGatewayEndpoint() +{ + m_settings->resetGatewayEndpoint(); + emit gatewayEndpointChanged(m_settings->getGatewayEndpoint()); +} + +void SettingsController::setGatewayEndpoint(const QString &endpoint) +{ + m_settings->setGatewayEndpoint(endpoint); + emit gatewayEndpointChanged(endpoint); +} + +QString SettingsController::getGatewayEndpoint() +{ + return m_settings->getGatewayEndpoint(); +} diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index 43ad10e8..a18888a9 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -25,6 +25,9 @@ public: Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged) Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged) + Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled) + Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged) + public slots: void toggleAmneziaDns(bool enable); bool isAmneziaDnsEnabled(); @@ -70,6 +73,15 @@ public slots: bool isNotificationPermissionGranted(); void requestNotificationPermission(); + QString getInstallationUuid(); + + void enableDevMode(); + bool isDevModeEnabled(); + + void resetGatewayEndpoint(); + void setGatewayEndpoint(const QString &endpoint); + QString getGatewayEndpoint(); + signals: void primaryDnsChanged(); void secondaryDnsChanged(); @@ -89,6 +101,9 @@ signals: void onNotificationStateChanged(); + void devModeEnabled(); + void gatewayEndpointChanged(const QString &endpoint); + private: QSharedPointer m_serversModel; QSharedPointer m_containersModel; @@ -101,6 +116,8 @@ private: QDateTime m_loggingDisableDate; + bool m_isDevModeEnabled = false; + void checkIfNeedDisableLogs(); }; diff --git a/client/ui/macos_util.h b/client/ui/macos_util.h index 15677e42..ff16390a 100644 --- a/client/ui/macos_util.h +++ b/client/ui/macos_util.h @@ -3,7 +3,7 @@ #ifndef Q_OS_IOS #include -#include +#include void setDockIconVisible(bool visible); void fixWidget(QWidget *widget); diff --git a/client/ui/models/apiCountryModel.cpp b/client/ui/models/apiCountryModel.cpp new file mode 100644 index 00000000..ae58329f --- /dev/null +++ b/client/ui/models/apiCountryModel.cpp @@ -0,0 +1,80 @@ +#include "apiCountryModel.h" + +#include + +#include "logger.h" + +namespace +{ + Logger logger("ApiCountryModel"); + + namespace configKey + { + constexpr char serverCountryCode[] = "server_country_code"; + constexpr char serverCountryName[] = "server_country_name"; + } +} + +ApiCountryModel::ApiCountryModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int ApiCountryModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_countries.size(); +} + +QVariant ApiCountryModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(rowCount())) + return QVariant(); + + QJsonObject countryInfo = m_countries.at(index.row()).toObject(); + + switch (role) { + case CountryCodeRole: { + return countryInfo.value(configKey::serverCountryCode).toString(); + } + case CountryNameRole: { + return countryInfo.value(configKey::serverCountryName).toString(); + } + } + + return QVariant(); +} + +void ApiCountryModel::updateModel(const QJsonArray &data, const QString ¤tCountryCode) +{ + beginResetModel(); + + m_countries = data; + for (int i = 0; i < m_countries.size(); i++) { + if (m_countries.at(i).toObject().value(configKey::serverCountryCode).toString() == currentCountryCode) { + m_currentIndex = i; + emit currentIndexChanged(m_currentIndex); + break; + } + } + + endResetModel(); +} + +int ApiCountryModel::getCurrentIndex() +{ + return m_currentIndex; +} + +void ApiCountryModel::setCurrentIndex(const int i) +{ + m_currentIndex = i; + emit currentIndexChanged(m_currentIndex); +} + +QHash ApiCountryModel::roleNames() const +{ + QHash roles; + roles[CountryNameRole] = "countryName"; + roles[CountryCodeRole] = "countryCode"; + return roles; +} diff --git a/client/ui/models/apiCountryModel.h b/client/ui/models/apiCountryModel.h new file mode 100644 index 00000000..8789158b --- /dev/null +++ b/client/ui/models/apiCountryModel.h @@ -0,0 +1,42 @@ +#ifndef APICOUNTRYMODEL_H +#define APICOUNTRYMODEL_H + +#include +#include + +class ApiCountryModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + CountryNameRole = Qt::UserRole + 1, + CountryCodeRole + }; + + explicit ApiCountryModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + Q_PROPERTY(int currentIndex READ getCurrentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + +public slots: + void updateModel(const QJsonArray &data, const QString ¤tCountryCode); + + int getCurrentIndex(); + void setCurrentIndex(const int i); + +signals: + void currentIndexChanged(const int index); + +protected: + QHash roleNames() const override; + +private: + QJsonArray m_countries; + int m_currentIndex; +}; + +#endif // APICOUNTRYMODEL_H diff --git a/client/ui/models/apiServicesModel.cpp b/client/ui/models/apiServicesModel.cpp new file mode 100644 index 00000000..3e74d259 --- /dev/null +++ b/client/ui/models/apiServicesModel.cpp @@ -0,0 +1,203 @@ +#include "apiServicesModel.h" + +#include + +#include "logger.h" + +namespace +{ + Logger logger("ApiServicesModel"); + + namespace configKey + { + constexpr char userCountryCode[] = "user_country_code"; + constexpr char services[] = "services"; + constexpr char serviceInfo[] = "service_info"; + constexpr char serviceType[] = "service_type"; + constexpr char serviceProtocol[] = "service_protocol"; + + constexpr char name[] = "name"; + constexpr char price[] = "price"; + constexpr char speed[] = "speed"; + constexpr char timelimit[] = "timelimit"; + constexpr char region[] = "region"; + + constexpr char availableCountries[] = "available_countries"; + + constexpr char storeEndpoint[] = "store_endpoint"; + } + + namespace serviceType + { + constexpr char amneziaFree[] = "amnezia-free"; + constexpr char amneziaPremium[] = "amnezia-premium"; + } +} + +ApiServicesModel::ApiServicesModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int ApiServicesModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_services.size(); +} + +QVariant ApiServicesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(rowCount())) + return QVariant(); + + QJsonObject service = m_services.at(index.row()).toObject(); + QJsonObject serviceInfo = service.value(configKey::serviceInfo).toObject(); + auto serviceType = service.value(configKey::serviceType).toString(); + + switch (role) { + case NameRole: { + return serviceInfo.value(configKey::name).toString(); + } + case CardDescriptionRole: { + auto speed = serviceInfo.value(configKey::speed).toString(); + if (serviceType == serviceType::amneziaPremium) { + return tr("Classic VPN for comfortable work, downloading large files and watching videos. " + "Works for any sites. Speed up to %1 MBit/s") + .arg(speed); + } else { + return tr("VPN to access blocked sites in regions with high levels of Internet censorship. "); + } + } + case ServiceDescriptionRole: { + if (serviceType == serviceType::amneziaPremium) { + return tr("Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. " + "It works for all websites, even in countries with the highest level of internet censorship."); + } else { + return tr("Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship"); + } + } + case SpeedRole: { + auto speed = serviceInfo.value(configKey::speed).toString(); + return tr("%1 MBit/s").arg(speed); + } + case WorkPeriodRole: { + auto timelimit = serviceInfo.value(configKey::timelimit).toString(); + if (timelimit == "0") { + return ""; + } + return tr("%1 days").arg(timelimit); + } + case RegionRole: { + return serviceInfo.value(configKey::region).toString(); + } + case FeaturesRole: { + if (serviceType == serviceType::amneziaPremium) { + return tr(""); + } else { + return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. " + "Other sites will be opened from your real IP address, " + "more details on the website."); + } + } + case PriceRole: { + auto price = serviceInfo.value(configKey::price).toString(); + if (price == "free") { + return tr("Free"); + } + return tr("%1 $/month").arg(price); + } + } + + return QVariant(); +} + +void ApiServicesModel::updateModel(const QJsonObject &data) +{ + beginResetModel(); + + m_countryCode = data.value(configKey::userCountryCode).toString(); + m_services = data.value(configKey::services).toArray(); + if (m_services.isEmpty()) { + QJsonObject service; + service.insert(configKey::serviceInfo, data.value(configKey::serviceInfo)); + service.insert(configKey::serviceType, data.value(configKey::serviceType)); + + m_services.push_back(service); + m_selectedServiceIndex = 0; + } + + endResetModel(); +} + +void ApiServicesModel::setServiceIndex(const int index) +{ + m_selectedServiceIndex = index; +} + +QJsonObject ApiServicesModel::getSelectedServiceInfo() +{ + QJsonObject service = m_services.at(m_selectedServiceIndex).toObject(); + return service.value(configKey::serviceInfo).toObject(); +} + +QString ApiServicesModel::getSelectedServiceType() +{ + QJsonObject service = m_services.at(m_selectedServiceIndex).toObject(); + return service.value(configKey::serviceType).toString(); +} + +QString ApiServicesModel::getSelectedServiceProtocol() +{ + QJsonObject service = m_services.at(m_selectedServiceIndex).toObject(); + return service.value(configKey::serviceProtocol).toString(); +} + +QString ApiServicesModel::getSelectedServiceName() +{ + auto modelIndex = index(m_selectedServiceIndex, 0); + return data(modelIndex, ApiServicesModel::Roles::NameRole).toString(); +} + +QJsonArray ApiServicesModel::getSelectedServiceCountries() +{ + QJsonObject service = m_services.at(m_selectedServiceIndex).toObject(); + return service.value(configKey::availableCountries).toArray(); +} + +QString ApiServicesModel::getCountryCode() +{ + return m_countryCode; +} + +QString ApiServicesModel::getStoreEndpoint() +{ + QJsonObject service = m_services.at(m_selectedServiceIndex).toObject(); + return service.value(configKey::storeEndpoint).toString(); +} + +QVariant ApiServicesModel::getSelectedServiceData(const QString roleString) +{ + QModelIndex modelIndex = index(m_selectedServiceIndex); + auto roles = roleNames(); + for (auto it = roles.begin(); it != roles.end(); it++) { + if (QString(it.value()) == roleString) { + return data(modelIndex, it.key()); + } + } + + return {}; +} + +QHash ApiServicesModel::roleNames() const +{ + QHash roles; + roles[NameRole] = "name"; + roles[CardDescriptionRole] = "cardDescription"; + roles[ServiceDescriptionRole] = "serviceDescription"; + roles[SpeedRole] = "speed"; + roles[WorkPeriodRole] = "workPeriod"; + roles[RegionRole] = "region"; + roles[FeaturesRole] = "features"; + roles[PriceRole] = "price"; + + return roles; +} diff --git a/client/ui/models/apiServicesModel.h b/client/ui/models/apiServicesModel.h new file mode 100644 index 00000000..64676be6 --- /dev/null +++ b/client/ui/models/apiServicesModel.h @@ -0,0 +1,56 @@ +#ifndef APISERVICESMODEL_H +#define APISERVICESMODEL_H + +#include +#include + +class ApiServicesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + NameRole = Qt::UserRole + 1, + CardDescriptionRole, + ServiceDescriptionRole, + SpeedRole, + WorkPeriodRole, + RegionRole, + FeaturesRole, + PriceRole + }; + + explicit ApiServicesModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +public slots: + void updateModel(const QJsonObject &data); + + void setServiceIndex(const int index); + + QJsonObject getSelectedServiceInfo(); + QString getSelectedServiceType(); + QString getSelectedServiceProtocol(); + QString getSelectedServiceName(); + QJsonArray getSelectedServiceCountries(); + + QString getCountryCode(); + + QString getStoreEndpoint(); + + QVariant getSelectedServiceData(const QString roleString); + +protected: + QHash roleNames() const override; + +private: + QString m_countryCode; + QJsonArray m_services; + + int m_selectedServiceIndex; +}; + +#endif // APISERVICESMODEL_H diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index 734f1e2d..41d26bc7 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -94,6 +94,26 @@ bool ContainersModel::isServiceContainer(const int containerIndex) return qvariant_cast(data(index(containerIndex), ServiceTypeRole) == ServiceType::Other); } +bool ContainersModel::hasInstalledServices() +{ + for (const auto &container : m_containers.keys()) { + if (ContainerProps::containerService(container) == ServiceType::Other) { + return true; + } + } + return false; +} + +bool ContainersModel::hasInstalledProtocols() +{ + for (const auto &container : m_containers.keys()) { + if (ContainerProps::containerService(container) == ServiceType::Vpn) { + return true; + } + } + return false; +} + QHash ContainersModel::roleNames() const { QHash roles; diff --git a/client/ui/models/containers_model.h b/client/ui/models/containers_model.h index 2a8117ea..3bd0ddc1 100644 --- a/client/ui/models/containers_model.h +++ b/client/ui/models/containers_model.h @@ -54,6 +54,9 @@ public slots: bool isSupportedByCurrentPlatform(const int containerIndex); bool isServiceContainer(const int containerIndex); + bool hasInstalledServices(); + bool hasInstalledProtocols(); + protected: QHash roleNames() const override; diff --git a/client/ui/models/languageModel.cpp b/client/ui/models/languageModel.cpp index f90b47b7..fe6f7a6c 100644 --- a/client/ui/models/languageModel.cpp +++ b/client/ui/models/languageModel.cpp @@ -103,3 +103,12 @@ QString LanguageModel::getCurrentLanguageName() { return m_availableLanguages[getCurrentLanguageIndex()].name; } + +QString LanguageModel::getCurrentSiteUrl() +{ + auto language = static_cast(getCurrentLanguageIndex()); + switch (language) { + case LanguageSettings::AvailableLanguageEnum::Russian: return "https://storage.googleapis.com/kldscp/amnezia.org"; + default: return "https://amnezia.org"; + } +} diff --git a/client/ui/models/languageModel.h b/client/ui/models/languageModel.h index ea29dda5..2c80880a 100644 --- a/client/ui/models/languageModel.h +++ b/client/ui/models/languageModel.h @@ -59,6 +59,7 @@ public slots: int getCurrentLanguageIndex(); int getLineHeightAppend(); QString getCurrentLanguageName(); + QString getCurrentSiteUrl(); signals: void updateTranslations(const QLocale &locale); diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 3f167029..85e5dae2 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -1,8 +1,31 @@ #include "servers_model.h" #include "core/controllers/serverController.h" +#include "core/enums/apiEnums.h" #include "core/networkUtilities.h" +#ifdef Q_OS_IOS + #include +#endif + +namespace +{ + namespace configKey + { + constexpr char apiConfig[] = "api_config"; + constexpr char serviceInfo[] = "service_info"; + constexpr char availableCountries[] = "available_countries"; + constexpr char serverCountryCode[] = "server_country_code"; + constexpr char serverCountryName[] = "server_country_name"; + constexpr char userCountryCode[] = "user_country_code"; + constexpr char serviceType[] = "service_type"; + constexpr char serviceProtocol[] = "service_protocol"; + + constexpr char publicKeyInfo[] = "public_key"; + constexpr char endDate[] = "end_date"; + } +} + ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) { m_isAmneziaDnsEnabled = m_settings->useAmneziaDns(); @@ -63,6 +86,7 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const } const QJsonObject server = m_servers.at(index.row()).toObject(); + const auto apiConfig = server.value(configKey::apiConfig).toObject(); const auto configVersion = server.value(config_key::configVersion).toInt(); switch (role) { case NameRole: { @@ -98,8 +122,23 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const case HasInstalledContainers: { return serverHasInstalledContainers(index.row()); } - case IsServerFromApiRole: { - return server.value(config_key::configVersion).toInt(); + case IsServerFromTelegramApiRole: { + return server.value(config_key::configVersion).toInt() == ApiConfigSources::Telegram; + } + case IsServerFromGatewayApiRole: { + return server.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway; + } + case ApiConfigRole: { + return apiConfig; + } + case IsCountrySelectionAvailableRole: { + return !apiConfig.value(configKey::availableCountries).toArray().isEmpty(); + } + case ApiAvailableCountriesRole: { + return apiConfig.value(configKey::availableCountries).toArray(); + } + case ApiServerCountryCodeRole: { + return apiConfig.value(configKey::serverCountryCode).toString(); } case HasAmneziaDns: { QString primaryDns = server.value(config_key::dns1).toString(); @@ -146,10 +185,13 @@ const QString ServersModel::getDefaultServerName() QString ServersModel::getServerDescription(const QJsonObject &server, const int index) const { const auto configVersion = server.value(config_key::configVersion).toInt(); + const auto apiConfig = server.value(configKey::apiConfig).toObject(); QString description; - if (configVersion) { + if (configVersion && !apiConfig.value(configKey::serverCountryCode).toString().isEmpty()) { + return apiConfig.value(configKey::serverCountryName).toString(); + } else if (configVersion) { return server.value(config_key::description).toString(); } else if (data(index, HasWriteAccessRole).toBool()) { if (m_isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) { @@ -208,6 +250,12 @@ void ServersModel::setProcessedServerIndex(const int index) { m_processedServerIndex = index; updateContainersModel(); + if (data(index, IsServerFromGatewayApiRole).toBool()) { + if (data(index, IsCountrySelectionAvailableRole).toBool()) { + emit updateApiLanguageModel(); + } + emit updateApiServicesModel(); + } emit processedServerIndexChanged(m_processedServerIndex); } @@ -233,7 +281,8 @@ bool ServersModel::isDefaultServerCurrentlyProcessed() bool ServersModel::isDefaultServerFromApi() { - return qvariant_cast(data(m_defaultServerIndex, IsServerFromApiRole)); + return data(m_defaultServerIndex, IsServerFromTelegramApiRole).toBool() + || data(m_defaultServerIndex, IsServerFromGatewayApiRole).toBool(); } bool ServersModel::isProcessedServerHasWriteAccess() @@ -315,7 +364,12 @@ QHash ServersModel::roleNames() const roles[DefaultContainerRole] = "defaultContainer"; roles[HasInstalledContainers] = "hasInstalledContainers"; - roles[IsServerFromApiRole] = "isServerFromApi"; + roles[IsServerFromTelegramApiRole] = "isServerFromTelegramApi"; + roles[IsServerFromGatewayApiRole] = "isServerFromGatewayApi"; + roles[ApiConfigRole] = "apiConfig"; + roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable"; + roles[ApiAvailableCountriesRole] = "apiAvailableCountries"; + roles[ApiServerCountryCodeRole] = "apiServerCountryCode"; return roles; } @@ -399,8 +453,7 @@ void ServersModel::addContainerConfig(const int containerIndex, const QJsonObjec auto defaultContainer = server.value(config_key::defaultContainer).toString(); if (ContainerProps::containerFromString(defaultContainer) == DockerContainer::None - && ContainerProps::containerService(container) != ServiceType::Other - && ContainerProps::isSupportedByCurrentPlatform(container)) { + && ContainerProps::containerService(container) != ServiceType::Other && ContainerProps::isSupportedByCurrentPlatform(container)) { server.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); } @@ -565,7 +618,7 @@ void ServersModel::toggleAmneziaDns(bool enabled) bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc) { - for (const auto &server : qAsConst(m_servers)) { + for (const auto &server : std::as_const(m_servers)) { if (static_cast(server.toObject().value(config_key::crc).toInt()) == crc) { return true; } @@ -573,6 +626,19 @@ bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc) return false; } +bool ServersModel::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) +{ + for (const auto &server : std::as_const(m_servers)) { + const auto apiConfig = server.toObject().value(configKey::apiConfig).toObject(); + if (apiConfig.value(configKey::userCountryCode).toString() == userCountryCode + && apiConfig.value(configKey::serviceType).toString() == serviceType + && apiConfig.value(configKey::serviceProtocol).toString() == serviceProtocol) { + return true; + } + } + return false; +} + bool ServersModel::serverHasInstalledContainers(const int serverIndex) const { QJsonObject server = m_servers.at(serverIndex).toObject(); @@ -621,14 +687,89 @@ bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling() auto containers = server.value(config_key::containers).toArray(); for (auto i = 0; i < containers.size(); i++) { auto container = containers.at(i).toObject(); + if (container.value(config_key::container).toString() != ContainerProps::containerToString(defaultContainer)) { + continue; + } if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) { - auto containerConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject(); - return !(containerConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0")); + QJsonObject serverProtocolConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject(); + QString clientProtocolConfigString = serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject clientProtocolConfig = QJsonDocument::fromJson(clientProtocolConfigString.toUtf8()).object(); + return (clientProtocolConfigString.contains("AllowedIPs") && !clientProtocolConfigString.contains("AllowedIPs = 0.0.0.0/0, ::/0")) + || (!clientProtocolConfig.value(config_key::allowed_ips).toArray().isEmpty() + && !clientProtocolConfig.value(config_key::allowed_ips).toArray().contains("0.0.0.0/0")); } else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn || defaultContainer == DockerContainer::ShadowSocks) { - auto containerConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject(); - return !(containerConfig.value(config_key::last_config).toString().contains("redirect-gateway")); + auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject(); + QString clientProtocolConfigString = serverProtocolConfig.value(config_key::last_config).toString(); + return !clientProtocolConfigString.isEmpty() && !clientProtocolConfigString.contains("redirect-gateway"); } } return false; } + +bool ServersModel::isServerFromApi(const int serverIndex) +{ + return data(serverIndex, IsServerFromTelegramApiRole).toBool() || data(serverIndex, IsServerFromGatewayApiRole).toBool(); +} + +bool ServersModel::isApiKeyExpired(const int serverIndex) +{ + auto serverConfig = m_servers.at(serverIndex).toObject(); + auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + + auto publicKeyInfo = apiConfig.value(configKey::publicKeyInfo).toObject(); + const QString endDate = publicKeyInfo.value(configKey::endDate).toString(); + if (endDate.isEmpty()) { + publicKeyInfo.insert(configKey::endDate, QDateTime::currentDateTimeUtc().addDays(1).toString(Qt::ISODate)); + apiConfig.insert(configKey::publicKeyInfo, publicKeyInfo); + serverConfig.insert(configKey::apiConfig, apiConfig); + editServer(serverConfig, serverIndex); + + return false; + } + + auto endDateDateTime = QDateTime::fromString(endDate, Qt::ISODate).toUTC(); + if (endDateDateTime < QDateTime::currentDateTimeUtc()) { + return true; + } + return false; +} + +void ServersModel::removeApiConfig(const int serverIndex) +{ + auto serverConfig = getServerConfig(serverIndex); + +#ifdef Q_OS_IOS + QString vpncName = QString("%1 (%2) %3") + .arg(serverConfig[config_key::description].toString()) + .arg(serverConfig[config_key::hostName].toString()) + .arg(serverConfig[config_key::vpnproto].toString()); + + AmneziaVPN::removeVPNC(vpncName.toStdString()); +#endif + + serverConfig.remove(config_key::dns1); + serverConfig.remove(config_key::dns2); + serverConfig.remove(config_key::containers); + serverConfig.remove(config_key::hostName); + + auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + apiConfig.remove(configKey::publicKeyInfo); + serverConfig.insert(configKey::apiConfig, apiConfig); + + serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None)); + + editServer(serverConfig, serverIndex); +} + +const QString ServersModel::getDefaultServerImagePathCollapsed() +{ + const auto server = m_servers.at(m_defaultServerIndex).toObject(); + const auto apiConfig = server.value(configKey::apiConfig).toObject(); + const auto countryCode = apiConfig.value(configKey::serverCountryCode).toString(); + + if (countryCode.isEmpty()) { + return ""; + } + return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode); +} diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index e6b9226b..0f18ea30 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -3,8 +3,8 @@ #include -#include "settings.h" #include "core/controllers/serverController.h" +#include "settings.h" class ServersModel : public QAbstractListModel { @@ -30,7 +30,13 @@ public: DefaultContainerRole, HasInstalledContainers, - IsServerFromApiRole, + + IsServerFromTelegramApiRole, + IsServerFromGatewayApiRole, + ApiConfigRole, + IsCountrySelectionAvailableRole, + ApiAvailableCountriesRole, + ApiServerCountryCodeRole, HasAmneziaDns }; @@ -49,8 +55,10 @@ public: Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerNameChanged) Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerDefaultContainerChanged) Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerDefaultContainerChanged) + Q_PROPERTY(QString defaultServerImagePathCollapsed READ getDefaultServerImagePathCollapsed NOTIFY defaultServerDefaultContainerChanged) Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerDefaultContainerChanged) - Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerDefaultContainerChanged) + Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY + defaultServerDefaultContainerChanged) Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged) Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged) @@ -60,6 +68,7 @@ public slots: const int getDefaultServerIndex(); const QString getDefaultServerName(); const QString getDefaultServerDescriptionCollapsed(); + const QString getDefaultServerImagePathCollapsed(); const QString getDefaultServerDescriptionExpanded(); const QString getDefaultServerDefaultContainerName(); bool isDefaultServerCurrentlyProcessed(); @@ -101,6 +110,7 @@ public slots: QPair getDnsPair(const int serverIndex); bool isServerFromApiAlreadyExists(const quint16 crc); + bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol); QVariant getDefaultServerData(const QString roleString); @@ -108,6 +118,10 @@ public slots: bool isDefaultServerDefaultContainerHasSplitTunneling(); + bool isServerFromApi(const int serverIndex); + bool isApiKeyExpired(const int serverIndex); + void removeApiConfig(const int serverIndex); + protected: QHash roleNames() const override; @@ -121,6 +135,9 @@ signals: void defaultServerContainersUpdated(const QJsonArray &containers); void defaultServerDefaultContainerChanged(const int containerIndex); + void updateApiLanguageModel(); + void updateApiServicesModel(); + private: ServerCredentials serverCredentials(int index) const; diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index dcf8caa3..cb706158 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -11,9 +11,9 @@ import Style 1.0 Button { id: root - property string defaultButtonColor: AmneziaStyle.color.white - property string progressButtonColor: AmneziaStyle.color.white - property string connectedButtonColor: AmneziaStyle.color.orange + property string defaultButtonColor: AmneziaStyle.color.paleGray + property string progressButtonColor: AmneziaStyle.color.paleGray + property string connectedButtonColor: AmneziaStyle.color.goldenApricot implicitWidth: 190 implicitHeight: 190 @@ -50,13 +50,13 @@ Button { verticalOffset: 0 radius: 10 samples: 25 - color: root.activeFocus ? AmneziaStyle.color.white : AmneziaStyle.color.orange + color: root.activeFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot source: backgroundCircle } ShapePath { fillColor: AmneziaStyle.color.transparent - strokeColor: AmneziaStyle.color.white + strokeColor: AmneziaStyle.color.paleGray strokeWidth: root.activeFocus ? 1 : 0 capStyle: ShapePath.RoundCap @@ -74,7 +74,7 @@ Button { fillColor: AmneziaStyle.color.transparent strokeColor: { if (ConnectionController.isConnectionInProgress) { - return AmneziaStyle.color.connectionInProgress + return AmneziaStyle.color.darkCharcoal } else if (ConnectionController.isConnected) { return connectedButtonColor } else { @@ -115,7 +115,7 @@ Button { ShapePath { fillColor: AmneziaStyle.color.transparent - strokeColor: AmneziaStyle.color.white + strokeColor: AmneziaStyle.color.paleGray strokeWidth: 3 capStyle: ShapePath.RoundCap diff --git a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml index 5a3bf475..405d4eda 100644 --- a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml +++ b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml @@ -53,7 +53,7 @@ DrawerType2 { Layout.fillWidth: true Layout.topMargin: 16 - visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi") + visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling text: qsTr("Split tunneling on the server") descriptionText: qsTr("Enabled \nCan't be disabled for current server") @@ -68,7 +68,7 @@ DrawerType2 { } DividerType { - visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi") + visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling } LabelWithButtonType { diff --git a/client/ui/qml/Components/InstalledAppsDrawer.qml b/client/ui/qml/Components/InstalledAppsDrawer.qml index 1024b054..e5d10055 100644 --- a/client/ui/qml/Components/InstalledAppsDrawer.qml +++ b/client/ui/qml/Components/InstalledAppsDrawer.qml @@ -134,7 +134,7 @@ DrawerType2 { anchors.rightMargin: 16 anchors.leftMargin: 16 - backgroundColor: AmneziaStyle.color.greyDark + backgroundColor: AmneziaStyle.color.slateGray textFieldPlaceholderText: qsTr("application name") } diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index 1ea1417e..a0e86dbc 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -89,10 +89,10 @@ DrawerType2 { Layout.leftMargin: 16 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: noButtonText diff --git a/client/ui/qml/Components/SelectLanguageDrawer.qml b/client/ui/qml/Components/SelectLanguageDrawer.qml index 38331079..4d9d7f0e 100644 --- a/client/ui/qml/Components/SelectLanguageDrawer.qml +++ b/client/ui/qml/Components/SelectLanguageDrawer.qml @@ -147,8 +147,8 @@ DrawerType2 { indicator: Rectangle { width: parent.width - 1 height: parent.height - color: radioButton.hovered ? AmneziaStyle.color.greyDark : AmneziaStyle.color.blackLight - border.color: radioButton.focus ? AmneziaStyle.color.white : AmneziaStyle.color.transparent + color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack + border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent border.width: radioButton.focus ? 1 : 0 Behavior on color { diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index 8bb38893..3235ad0a 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -113,10 +113,10 @@ DrawerType2 { Layout.topMargin: 8 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Copy") @@ -136,10 +136,10 @@ DrawerType2 { visible: false defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Copy config string") @@ -155,10 +155,10 @@ DrawerType2 { Layout.topMargin: 24 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Show connection settings") @@ -282,9 +282,9 @@ DrawerType2 { readOnly: true activeFocusOnTab: false - color: AmneziaStyle.color.white - selectionColor: AmneziaStyle.color.brown - selectedTextColor: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray + selectionColor: AmneziaStyle.color.richBrown + selectedTextColor: AmneziaStyle.color.paleGray font.pixelSize: 16 font.weight: Font.Medium diff --git a/client/ui/qml/Components/TransportProtoSelector.qml b/client/ui/qml/Components/TransportProtoSelector.qml index adb7371f..e40dd4bb 100644 --- a/client/ui/qml/Components/TransportProtoSelector.qml +++ b/client/ui/qml/Components/TransportProtoSelector.qml @@ -16,7 +16,7 @@ Rectangle { implicitWidth: transportProtoButtonGroup.implicitWidth implicitHeight: transportProtoButtonGroup.implicitHeight - color: AmneziaStyle.color.blackLight + color: AmneziaStyle.color.onyxBlack radius: 16 onFocusChanged: { diff --git a/client/ui/qml/Controls2/BackButtonType.qml b/client/ui/qml/Controls2/BackButtonType.qml index ff898e08..86fc86a2 100644 --- a/client/ui/qml/Controls2/BackButtonType.qml +++ b/client/ui/qml/Controls2/BackButtonType.qml @@ -30,7 +30,7 @@ Item { ImageButtonType { id: backButton image: backButtonImage - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray implicitWidth: 40 implicitHeight: 40 diff --git a/client/ui/qml/Controls2/BasicButtonType.qml b/client/ui/qml/Controls2/BasicButtonType.qml index 0b6c4a6f..5c599013 100644 --- a/client/ui/qml/Controls2/BasicButtonType.qml +++ b/client/ui/qml/Controls2/BasicButtonType.qml @@ -10,15 +10,15 @@ import "TextTypes" Button { id: root - property string hoveredColor: AmneziaStyle.color.whiteHovered - property string defaultColor: AmneziaStyle.color.white - property string disabledColor: AmneziaStyle.color.greyDisabled - property string pressedColor: AmneziaStyle.color.grey + property string hoveredColor: AmneziaStyle.color.lightGray + property string defaultColor: AmneziaStyle.color.paleGray + property string disabledColor: AmneziaStyle.color.charcoalGray + property string pressedColor: AmneziaStyle.color.mutedGray - property string textColor: AmneziaStyle.color.black + property string textColor: AmneziaStyle.color.midnightBlack - property string borderColor: AmneziaStyle.color.white - property string borderFocusedColor: AmneziaStyle.color.white + property string borderColor: AmneziaStyle.color.paleGray + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderWidth: 0 property int borderFocusedWidth: 1 diff --git a/client/ui/qml/Controls2/BusyIndicatorType.qml b/client/ui/qml/Controls2/BusyIndicatorType.qml index 590006ee..55af280f 100644 --- a/client/ui/qml/Controls2/BusyIndicatorType.qml +++ b/client/ui/qml/Controls2/BusyIndicatorType.qml @@ -43,7 +43,7 @@ Popup { ShapePath { fillColor: AmneziaStyle.color.transparent - strokeColor: AmneziaStyle.color.greyDisabled + strokeColor: AmneziaStyle.color.charcoalGray strokeWidth: 3 capStyle: ShapePath.RoundCap diff --git a/client/ui/qml/Controls2/CardType.qml b/client/ui/qml/Controls2/CardType.qml index 1e623853..50f84dbf 100644 --- a/client/ui/qml/Controls2/CardType.qml +++ b/client/ui/qml/Controls2/CardType.qml @@ -11,16 +11,16 @@ RadioButton { property string bodyText property string footerText - property string hoveredColor: AmneziaStyle.color.blackHovered + property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite property string defaultColor: AmneziaStyle.color.transparent property string disabledColor: AmneziaStyle.color.transparent - property string pressedColor: AmneziaStyle.color.blackPressed + property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite property string selectedColor: AmneziaStyle.color.transparent - property string textColor: AmneziaStyle.color.black + property string textColor: AmneziaStyle.color.midnightBlack property string pressedBorderColor: Qt.rgba(251/255, 178/255, 106/255, 0.3) - property string selectedBorderColor: AmneziaStyle.color.orange + property string selectedBorderColor: AmneziaStyle.color.goldenApricot property string defaultBodredColor: AmneziaStyle.color.transparent property int borderWidth: 0 @@ -84,7 +84,7 @@ RadioButton { Text { text: root.headerText wrapMode: Text.WordWrap - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 25 font.weight: 700 font.family: "PT Root UI VF" @@ -99,7 +99,7 @@ RadioButton { Text { text: root.bodyText wrapMode: Text.WordWrap - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 16 font.weight: 400 font.family: "PT Root UI VF" @@ -115,7 +115,7 @@ RadioButton { text: root.footerText wrapMode: Text.WordWrap visible: root.footerText !== "" - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray font.pixelSize: 13 font.weight: 400 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/CardWithIconsType.qml b/client/ui/qml/Controls2/CardWithIconsType.qml new file mode 100644 index 00000000..8630434b --- /dev/null +++ b/client/ui/qml/Controls2/CardWithIconsType.qml @@ -0,0 +1,177 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 + +import "TextTypes" + +Button { + id: root + + property string headerText + property string bodyText + property string footerText + + property string hoveredColor: AmneziaStyle.color.slateGray + property string defaultColor: AmneziaStyle.color.onyxBlack + + property string textColor: AmneziaStyle.color.midnightBlack + + property string rightImageSource + property string rightImageColor: AmneziaStyle.color.paleGray + + property string leftImageSource + + property real textOpacity: 1.0 + + hoverEnabled: true + + background: Rectangle { + id: backgroundRect + anchors.fill: parent + radius: 16 + + color: defaultColor + + Behavior on color { + PropertyAnimation { duration: 200 } + } + } + + contentItem: Item { + anchors.left: parent.left + anchors.right: parent.right + + implicitHeight: content.implicitHeight + RowLayout { + id: content + anchors.fill: parent + + Image { + id: leftImage + source: leftImageSource + + visible: leftImageSource !== "" + + Layout.alignment: Qt.AlignLeft | Qt.AlignTop + Layout.topMargin: 24 + Layout.bottomMargin: 24 + Layout.leftMargin: 24 + } + + ColumnLayout { + ListItemTitleType { + text: root.headerText + visible: text !== "" + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.topMargin: 16 + Layout.bottomMargin: root.bodyText !== "" ? 0 : 16 + + opacity: root.textOpacity + } + + CaptionTextType { + text: root.bodyText + visible: text !== "" + + color: AmneziaStyle.color.mutedGray + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: root.footerText !== "" ? 0 : 16 + + opacity: root.textOpacity + } + + ButtonTextType { + text: root.footerText + visible: text !== "" + + color: AmneziaStyle.color.mutedGray + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.topMargin: 16 + Layout.bottomMargin: 16 + + opacity: root.textOpacity + } + } + + ImageButtonType { + id: rightImage + + implicitWidth: 40 + implicitHeight: 40 + + hoverEnabled: false + image: rightImageSource + imageColor: rightImageColor + visible: rightImageSource ? true : false + + Layout.alignment: Qt.AlignRight | Qt.AlignTop + Layout.topMargin: 16 + Layout.bottomMargin: 16 + Layout.rightMargin: 16 + + Rectangle { + id: rightImageBackground + anchors.fill: parent + radius: 12 + color: "transparent" + + Behavior on color { + PropertyAnimation { duration: 200 } + } + } + onClicked: { + if (clickedFunction && typeof clickedFunction === "function") { + clickedFunction() + } + } + } + } + } + + MouseArea { + anchors.fill: parent + + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + + onEntered: { + backgroundRect.color = root.hoveredColor + + if (rightImageSource) { + rightImageBackground.color = rightImage.hoveredColor + } + root.textOpacity = 0.8 + } + + onExited: { + backgroundRect.color = root.defaultColor + + if (rightImageSource) { + rightImageBackground.color = rightImage.defaultColor + } + root.textOpacity = 1 + } + + onPressedChanged: { + if (rightImageSource) { + rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor + } + root.textOpacity = 0.7 + } + + onClicked: { + root.clicked() + } + } +} diff --git a/client/ui/qml/Controls2/CheckBoxType.qml b/client/ui/qml/Controls2/CheckBoxType.qml index 1816c58c..a26a68f1 100644 --- a/client/ui/qml/Controls2/CheckBoxType.qml +++ b/client/ui/qml/Controls2/CheckBoxType.qml @@ -11,26 +11,26 @@ CheckBox { id: root property string descriptionText - property string descriptionTextColor: AmneziaStyle.color.grey - property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled + property string descriptionTextColor: AmneziaStyle.color.mutedGray + property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray - property string textColor: AmneziaStyle.color.white - property string textDisabledColor: AmneziaStyle.color.grey + property string textColor: AmneziaStyle.color.paleGray + property string textDisabledColor: AmneziaStyle.color.mutedGray - property string hoveredColor: AmneziaStyle.color.blackHovered + property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite property string defaultColor: AmneziaStyle.color.transparent - property string pressedColor: AmneziaStyle.color.blackPressed + property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite - property string defaultBorderColor: AmneziaStyle.color.white - property string checkedBorderColor: AmneziaStyle.color.orange - property string checkedBorderDisabledColor: AmneziaStyle.color.brownDark + property string defaultBorderColor: AmneziaStyle.color.paleGray + property string checkedBorderColor: AmneziaStyle.color.goldenApricot + property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray - property string checkedImageColor: AmneziaStyle.color.orange - property string pressedImageColor: AmneziaStyle.color.orangeDark + property string checkedImageColor: AmneziaStyle.color.goldenApricot + property string pressedImageColor: AmneziaStyle.color.burntOrange property string defaultImageColor: AmneziaStyle.color.transparent - property string checkedDisabledImageColor: AmneziaStyle.color.brownLight + property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown property string imageSource: "qrc:/images/controls/check.svg" diff --git a/client/ui/qml/Controls2/DividerType.qml b/client/ui/qml/Controls2/DividerType.qml index a6dec6fb..2eb535ec 100644 --- a/client/ui/qml/Controls2/DividerType.qml +++ b/client/ui/qml/Controls2/DividerType.qml @@ -10,5 +10,5 @@ Rectangle { Layout.rightMargin: 16 height: 1 - color: AmneziaStyle.color.greyDark + color: AmneziaStyle.color.slateGray } diff --git a/client/ui/qml/Controls2/DrawerType2.qml b/client/ui/qml/Controls2/DrawerType2.qml index 0611fb12..6647bc88 100644 --- a/client/ui/qml/Controls2/DrawerType2.qml +++ b/client/ui/qml/Controls2/DrawerType2.qml @@ -21,8 +21,8 @@ Item { property Component collapsedContent property Component expandedContent - property string defaultColor: AmneziaStyle.color.blackLight - property string borderColor: AmneziaStyle.color.greyDark + property string defaultColor: AmneziaStyle.color.onyxBlack + property string borderColor: AmneziaStyle.color.slateGray property real expandedHeight property real collapsedHeight: 0 diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml index 437c5146..906cfffe 100644 --- a/client/ui/qml/Controls2/DropDownType.qml +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -11,31 +11,31 @@ Item { id: root property string text - property string textColor: AmneziaStyle.color.white - property string textDisabledColor: AmneziaStyle.color.grey + property string textColor: AmneziaStyle.color.paleGray + property string textDisabledColor: AmneziaStyle.color.mutedGray property int textMaximumLineCount: 2 property int textElide: Qt.ElideRight property string descriptionText - property string descriptionTextColor: AmneziaStyle.color.grey - property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled + property string descriptionTextColor: AmneziaStyle.color.mutedGray + property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray property string headerText property string headerBackButtonImage property var rootButtonClickedFunction property string rootButtonImage: "qrc:/images/controls/chevron-down.svg" - property string rootButtonImageColor: AmneziaStyle.color.white - property string rootButtonBackgroundColor: AmneziaStyle.color.blackLight - property string rootButtonBackgroundHoveredColor: AmneziaStyle.color.blackLight - property string rootButtonBackgroundPressedColor: AmneziaStyle.color.blackLight + property string rootButtonImageColor: AmneziaStyle.color.paleGray + property string rootButtonBackgroundColor: AmneziaStyle.color.onyxBlack + property string rootButtonBackgroundHoveredColor: AmneziaStyle.color.onyxBlack + property string rootButtonBackgroundPressedColor: AmneziaStyle.color.onyxBlack - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 - property string rootButtonHoveredBorderColor: AmneziaStyle.color.greyDisabled - property string rootButtonDefaultBorderColor: AmneziaStyle.color.greyDark - property string rootButtonPressedBorderColor: AmneziaStyle.color.white + property string rootButtonHoveredBorderColor: AmneziaStyle.color.charcoalGray + property string rootButtonDefaultBorderColor: AmneziaStyle.color.slateGray + property string rootButtonPressedBorderColor: AmneziaStyle.color.paleGray property int rootButtonTextLeftMargins: 16 property int rootButtonTextTopMargin: 16 diff --git a/client/ui/qml/Controls2/Header2Type.qml b/client/ui/qml/Controls2/Header2Type.qml index 4c8cb79b..c435cbe2 100644 --- a/client/ui/qml/Controls2/Header2Type.qml +++ b/client/ui/qml/Controls2/Header2Type.qml @@ -39,7 +39,7 @@ Item { implicitHeight: 40 image: root.actionButtonImage - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray visible: image ? true : false @@ -59,7 +59,7 @@ Item { text: root.descriptionText - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray visible: root.descriptionText !== "" } diff --git a/client/ui/qml/Controls2/HeaderType.qml b/client/ui/qml/Controls2/HeaderType.qml index 60cb5b8a..f1cafbff 100644 --- a/client/ui/qml/Controls2/HeaderType.qml +++ b/client/ui/qml/Controls2/HeaderType.qml @@ -48,7 +48,7 @@ Item { Layout.alignment: Qt.AlignRight image: root.actionButtonImage - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray visible: image ? true : false @@ -68,7 +68,7 @@ Item { text: root.descriptionText - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray visible: root.descriptionText !== "" } diff --git a/client/ui/qml/Controls2/HorizontalRadioButton.qml b/client/ui/qml/Controls2/HorizontalRadioButton.qml index addda1ab..1ac1cd30 100644 --- a/client/ui/qml/Controls2/HorizontalRadioButton.qml +++ b/client/ui/qml/Controls2/HorizontalRadioButton.qml @@ -9,19 +9,19 @@ import "TextTypes" RadioButton { id: root - property string hoveredColor: AmneziaStyle.color.blackHovered + property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite property string defaultColor: AmneziaStyle.color.transparent property string checkedColor: AmneziaStyle.color.transparent property string disabledColor: AmneziaStyle.color.transparent - property string textColor: AmneziaStyle.color.white - property string textDisabledColor: AmneziaStyle.color.grey + property string textColor: AmneziaStyle.color.paleGray + property string textDisabledColor: AmneziaStyle.color.mutedGray - property string pressedBorderColor: AmneziaStyle.color.greyDisabled - property string checkedBorderColor: AmneziaStyle.color.orange + property string pressedBorderColor: AmneziaStyle.color.charcoalGray + property string checkedBorderColor: AmneziaStyle.color.goldenApricot property string defaultBodredColor: AmneziaStyle.color.transparent - property string checkedDisabledBorderColor: AmneziaStyle.color.brownLight - property string borderFocusedColor: AmneziaStyle.color.white + property string checkedDisabledBorderColor: AmneziaStyle.color.mutedBrown + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderWidth: 0 implicitWidth: content.implicitWidth diff --git a/client/ui/qml/Controls2/ImageButtonType.qml b/client/ui/qml/Controls2/ImageButtonType.qml index 7a47050c..fffb6d84 100644 --- a/client/ui/qml/Controls2/ImageButtonType.qml +++ b/client/ui/qml/Controls2/ImageButtonType.qml @@ -9,18 +9,18 @@ Button { property string image - property string hoveredColor: AmneziaStyle.color.blackHovered + property string hoveredColor: AmneziaStyle.color.translucentWhite property string defaultColor: AmneziaStyle.color.transparent - property string pressedColor: AmneziaStyle.color.blackPressed - property string disableColor: AmneziaStyle.color.greyDark + property string pressedColor: AmneziaStyle.color.sheerWhite + property string disableColor: AmneziaStyle.color.slateGray - property string imageColor: AmneziaStyle.color.grey - property string disableImageColor: AmneziaStyle.color.greyDark + property string imageColor: AmneziaStyle.color.mutedGray + property string disableImageColor: AmneziaStyle.color.slateGray property alias backgroundColor: background.color property alias backgroundRadius: background.radius - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 hoverEnabled: true diff --git a/client/ui/qml/Controls2/LabelWithButtonType.qml b/client/ui/qml/Controls2/LabelWithButtonType.qml index 9a55e521..3b1609f7 100644 --- a/client/ui/qml/Controls2/LabelWithButtonType.qml +++ b/client/ui/qml/Controls2/LabelWithButtonType.qml @@ -26,16 +26,16 @@ Item { property alias eyeButton: eyeImage property FlickableType parentFlickable - property string textColor: AmneziaStyle.color.white - property string textDisabledColor: AmneziaStyle.color.grey - property string descriptionColor: AmneziaStyle.color.grey - property string descriptionDisabledColor: AmneziaStyle.color.greyDisabled + property string textColor: AmneziaStyle.color.paleGray + property string textDisabledColor: AmneziaStyle.color.mutedGray + property string descriptionColor: AmneziaStyle.color.mutedGray + property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray property real textOpacity: 1.0 - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 - property string rightImageColor: AmneziaStyle.color.white + property string rightImageColor: AmneziaStyle.color.paleGray property bool descriptionOnTop: false property bool hideDescription: true diff --git a/client/ui/qml/Controls2/LabelWithImageType.qml b/client/ui/qml/Controls2/LabelWithImageType.qml new file mode 100644 index 00000000..57d60d8f --- /dev/null +++ b/client/ui/qml/Controls2/LabelWithImageType.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 + +import "TextTypes" + +RowLayout { + property string imageSource + property string leftText + property var rightText + property bool isRightTextUndefined: rightText === undefined + + visible: !isRightTextUndefined + + Image { + Layout.preferredHeight: 18 + Layout.preferredWidth: 18 + source: imageSource + } + + ListItemTitleType { + Layout.fillWidth: true + Layout.rightMargin: 10 + Layout.alignment: Qt.AlignRight + + text: leftText + } + + ParagraphTextType { + visible: rightText !== "" + + Layout.alignment: Qt.AlignLeft + + text: isRightTextUndefined ? "" : rightText + } +} diff --git a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml index 67118913..f7b777a7 100644 --- a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml +++ b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml @@ -105,8 +105,8 @@ ListView { indicator: Rectangle { width: parent.width - 1 height: parent.height - color: radioButton.hovered ? AmneziaStyle.color.greyDark : AmneziaStyle.color.blackLight - border.color: radioButton.focus ? AmneziaStyle.color.white : AmneziaStyle.color.transparent + color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack + border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent border.width: radioButton.focus ? 1 : 0 Behavior on color { diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index 0a6e6914..bd4aa4fb 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -84,11 +84,11 @@ Popup { implicitHeight: 32 defaultColor: "white" - hoveredColor: AmneziaStyle.color.whiteHovered - pressedColor: AmneziaStyle.color.whiteHovered - disabledColor: AmneziaStyle.color.greyDisabled + hoveredColor: AmneziaStyle.color.lightGray + pressedColor: AmneziaStyle.color.lightGray + disabledColor: AmneziaStyle.color.charcoalGray - textColor: AmneziaStyle.color.black + textColor: AmneziaStyle.color.midnightBlack borderWidth: 0 text: qsTr("Close") diff --git a/client/ui/qml/Controls2/ProgressBarType.qml b/client/ui/qml/Controls2/ProgressBarType.qml index 0ae63dc3..83e49771 100644 --- a/client/ui/qml/Controls2/ProgressBarType.qml +++ b/client/ui/qml/Controls2/ProgressBarType.qml @@ -10,14 +10,14 @@ ProgressBar { implicitHeight: 4 background: Rectangle { - color: AmneziaStyle.color.brown + color: AmneziaStyle.color.richBrown } contentItem: Item { Rectangle { width: root.visualPosition * parent.width height: parent.height - color: AmneziaStyle.color.orange + color: AmneziaStyle.color.goldenApricot } } } diff --git a/client/ui/qml/Controls2/SwitcherType.qml b/client/ui/qml/Controls2/SwitcherType.qml index 6bf430de..9b2885ea 100644 --- a/client/ui/qml/Controls2/SwitcherType.qml +++ b/client/ui/qml/Controls2/SwitcherType.qml @@ -10,29 +10,29 @@ Switch { id: root property alias descriptionText: description.text - property string descriptionTextColor: AmneziaStyle.color.grey - property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled + property string descriptionTextColor: AmneziaStyle.color.mutedGray + property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray - property string textColor: AmneziaStyle.color.white - property string textDisabledColor: AmneziaStyle.color.grey + property string textColor: AmneziaStyle.color.paleGray + property string textDisabledColor: AmneziaStyle.color.mutedGray - property string checkedIndicatorColor: AmneziaStyle.color.brown + property string checkedIndicatorColor: AmneziaStyle.color.richBrown property string defaultIndicatorColor: AmneziaStyle.color.transparent - property string checkedDisabledIndicatorColor: AmneziaStyle.color.brownDark + property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 - property string checkedIndicatorBorderColor: AmneziaStyle.color.brown - property string defaultIndicatorBorderColor: AmneziaStyle.color.greyDisabled - property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.brownDark + property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown + property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray + property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown - property string checkedInnerCircleColor: AmneziaStyle.color.orange - property string defaultInnerCircleColor: AmneziaStyle.color.white - property string checkedDisabledInnerCircleColor: AmneziaStyle.color.brownLight - property string defaultDisabledInnerCircleColor: AmneziaStyle.color.greyDisabled + property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot + property string defaultInnerCircleColor: AmneziaStyle.color.paleGray + property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown + property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray - property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.blackHovered + property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent hoverEnabled: enabled ? true : false diff --git a/client/ui/qml/Controls2/TabButtonType.qml b/client/ui/qml/Controls2/TabButtonType.qml index 5f34aaf4..d57ff3a0 100644 --- a/client/ui/qml/Controls2/TabButtonType.qml +++ b/client/ui/qml/Controls2/TabButtonType.qml @@ -6,13 +6,13 @@ import Style 1.0 TabButton { id: root - property string hoveredColor: AmneziaStyle.color.brown - property string defaultColor: AmneziaStyle.color.greyDark - property string selectedColor: AmneziaStyle.color.orange + property string hoveredColor: AmneziaStyle.color.richBrown + property string defaultColor: AmneziaStyle.color.slateGray + property string selectedColor: AmneziaStyle.color.goldenApricot - property string textColor: AmneziaStyle.color.white + property string textColor: AmneziaStyle.color.paleGray - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 property bool isSelected: false diff --git a/client/ui/qml/Controls2/TabImageButtonType.qml b/client/ui/qml/Controls2/TabImageButtonType.qml index 4484f8e4..abe544aa 100644 --- a/client/ui/qml/Controls2/TabImageButtonType.qml +++ b/client/ui/qml/Controls2/TabImageButtonType.qml @@ -6,15 +6,15 @@ import Style 1.0 TabButton { id: root - property string hoveredColor: AmneziaStyle.color.brown - property string defaultColor: AmneziaStyle.color.white - property string selectedColor: AmneziaStyle.color.orange + property string hoveredColor: AmneziaStyle.color.richBrown + property string defaultColor: AmneziaStyle.color.paleGray + property string selectedColor: AmneziaStyle.color.goldenApricot property string image property bool isSelected: false - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 property var clickedFunc diff --git a/client/ui/qml/Controls2/TextAreaType.qml b/client/ui/qml/Controls2/TextAreaType.qml index db84ba61..9359fa16 100644 --- a/client/ui/qml/Controls2/TextAreaType.qml +++ b/client/ui/qml/Controls2/TextAreaType.qml @@ -11,12 +11,12 @@ Rectangle { property alias textArea: textArea property alias textAreaText: textArea.text - property string borderHoveredColor: AmneziaStyle.color.greyDisabled - property string borderNormalColor: AmneziaStyle.color.greyDark - property string borderFocusedColor: AmneziaStyle.color.white + property string borderHoveredColor: AmneziaStyle.color.charcoalGray + property string borderNormalColor: AmneziaStyle.color.slateGray + property string borderFocusedColor: AmneziaStyle.color.paleGray height: 148 - color: AmneziaStyle.color.blackLight + color: AmneziaStyle.color.onyxBlack border.width: 1 border.color: getBorderColor(borderNormalColor) radius: 16 @@ -54,10 +54,10 @@ Rectangle { anchors.topMargin: 16 anchors.bottomMargin: 16 - color: AmneziaStyle.color.white - selectionColor: AmneziaStyle.color.brown - selectedTextColor: AmneziaStyle.color.white - placeholderTextColor: AmneziaStyle.color.grey + color: AmneziaStyle.color.paleGray + selectionColor: AmneziaStyle.color.richBrown + selectedTextColor: AmneziaStyle.color.paleGray + placeholderTextColor: AmneziaStyle.color.mutedGray font.pixelSize: 16 font.weight: Font.Medium diff --git a/client/ui/qml/Controls2/TextAreaWithFooterType.qml b/client/ui/qml/Controls2/TextAreaWithFooterType.qml new file mode 100644 index 00000000..102929e2 --- /dev/null +++ b/client/ui/qml/Controls2/TextAreaWithFooterType.qml @@ -0,0 +1,183 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 + +import "TextTypes" + +Rectangle { + id: root + + property string placeholderText + property string text + property string headerText + property alias textArea: textArea + property alias textAreaText: textArea.text + + property string borderHoveredColor: AmneziaStyle.color.charcoalGray + property string borderNormalColor: AmneziaStyle.color.slateGray + property string borderFocusedColor: AmneziaStyle.color.paleGray + + property string firstButtonImage + property string secondButtonImage + + property var firstButtonClickedFunc + property var secondButtonClickedFunc + + height: 148 + color: AmneziaStyle.color.onyxBlack + border.width: 1 + border.color: getBorderColor(borderNormalColor) + radius: 16 + + property FlickableType parentFlickable: null + onFocusChanged: { + if (root.activeFocus) { + if (root.parentFlickable) { + root.parentFlickable.ensureVisible(root) + } + } + } + + MouseArea { + id: parentMouse + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: textArea.forceActiveFocus() + hoverEnabled: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 0 + + LabelTextType { + Layout.fillWidth: true + text: root.headerText + } + + TextArea { + id: textArea + + Layout.fillWidth: true + Layout.fillHeight: true + + leftPadding: 0 + Layout.bottomMargin: 16 + + color: AmneziaStyle.color.paleGray + selectionColor: AmneziaStyle.color.richBrown + selectedTextColor: AmneziaStyle.color.paleGray + placeholderTextColor: AmneziaStyle.color.mutedGray + + font.pixelSize: 16 + font.weight: Font.Medium + font.family: "PT Root UI VF" + + placeholderText: root.placeholderText + text: root.text + + + KeyNavigation.tab: firstButton + + onCursorVisibleChanged: { + if (textArea.cursorVisible) { + fl.interactive = true + } else { + fl.interactive = false + } + } + + wrapMode: Text.Wrap + + MouseArea { + id: textAreaMouse + anchors.fill: parent + acceptedButtons: Qt.RightButton + hoverEnabled: true + onClicked: { + fl.interactive = true + contextMenu.open() + } + } + + onFocusChanged: { + root.border.color = getBorderColor(borderNormalColor) + } + + ContextMenuType { + id: contextMenu + textObj: textArea + } + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: -8 + spacing: 0 + ImageButtonType { + id: firstButton + visible: root.firstButtonImage !== "" + + imageColor: AmneziaStyle.color.paleGray + + image: root.firstButtonImage + onClicked: function() { + if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") { + root.firstButtonClickedFunc() + } + } + } + + ImageButtonType { + id: secondButton + visible: root.secondButtonImage !== "" + + imageColor: AmneziaStyle.color.paleGray + + image: root.secondButtonImage + onClicked: function() { + if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") { + root.secondButtonClickedFunc() + } + } + } + + Item { + Layout.fillWidth: true + } + + ImageButtonType { + id: resetButton + imageColor: AmneziaStyle.color.paleGray + + visible: root.textAreaText !== "" + image: "qrc:/images/controls/close.svg" + + onClicked: function() { + root.textAreaText = "" + textArea.focus = true + } + } + } + } + + onPressed: { + root.border.color = getBorderColor(borderFocusedColor) + } + + onExited: { + root.border.color = getBorderColor(borderNormalColor) + } + + onEntered: { + root.border.color = getBorderColor(borderHoveredColor) + } + } + + + function getBorderColor(noneFocusedColor) { + return textArea.focus ? root.borderFocusedColor : noneFocusedColor + } +} diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 7ab8f9e3..4ec0976b 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -10,8 +10,8 @@ Item { id: root property string headerText - property string headerTextDisabledColor: AmneziaStyle.color.greyDisabled - property string headerTextColor: AmneziaStyle.color.grey + property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray + property string headerTextColor: AmneziaStyle.color.mutedGray property alias errorText: errorField.text property bool checkEmptyText: false @@ -23,18 +23,18 @@ Item { property alias textField: textField property alias textFieldText: textField.text - property string textFieldTextColor: AmneziaStyle.color.white - property string textFieldTextDisabledColor: AmneziaStyle.color.grey + property string textFieldTextColor: AmneziaStyle.color.paleGray + property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray property string textFieldPlaceholderText property bool textFieldEditable: true - property string borderColor: AmneziaStyle.color.greyDark - property string borderFocusedColor: AmneziaStyle.color.white + property string borderColor: AmneziaStyle.color.slateGray + property string borderFocusedColor: AmneziaStyle.color.paleGray - property string backgroundColor: AmneziaStyle.color.blackLight + property string backgroundColor: AmneziaStyle.color.onyxBlack property string backgroundDisabledColor: AmneziaStyle.color.transparent - property string bgBorderHoveredColor: AmneziaStyle.color.greyDisabled + property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight @@ -92,10 +92,10 @@ Item { inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText placeholderText: root.textFieldPlaceholderText - placeholderTextColor: AmneziaStyle.color.greyDisabled + placeholderTextColor: AmneziaStyle.color.charcoalGray - selectionColor: AmneziaStyle.color.brown - selectedTextColor: AmneziaStyle.color.white + selectionColor: AmneziaStyle.color.richBrown + selectedTextColor: AmneziaStyle.color.paleGray font.pixelSize: 16 font.weight: 400 @@ -149,7 +149,9 @@ Item { text: root.errorText visible: root.errorText !== "" - color: AmneziaStyle.color.red + color: AmneziaStyle.color.vibrantRed + + Layout.fillWidth: true } } diff --git a/client/ui/qml/Controls2/TextTypes/ButtonTextType.qml b/client/ui/qml/Controls2/TextTypes/ButtonTextType.qml index 0bf5b203..28056758 100644 --- a/client/ui/qml/Controls2/TextTypes/ButtonTextType.qml +++ b/client/ui/qml/Controls2/TextTypes/ButtonTextType.qml @@ -6,7 +6,7 @@ Text { lineHeight: 24 lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 16 font.weight: 600 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml b/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml index 4b480154..ba511289 100644 --- a/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml +++ b/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml @@ -6,7 +6,7 @@ Text { lineHeight: 16 + LanguageModel.getLineHeightAppend() lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.black + color: AmneziaStyle.color.midnightBlack font.pixelSize: 13 font.weight: 400 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TextTypes/Header1TextType.qml b/client/ui/qml/Controls2/TextTypes/Header1TextType.qml index fa4aaa4e..40a0b35e 100644 --- a/client/ui/qml/Controls2/TextTypes/Header1TextType.qml +++ b/client/ui/qml/Controls2/TextTypes/Header1TextType.qml @@ -6,7 +6,7 @@ Text { lineHeight: 38 + LanguageModel.getLineHeightAppend() lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 32 font.weight: 700 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TextTypes/Header2TextType.qml b/client/ui/qml/Controls2/TextTypes/Header2TextType.qml index e456c117..74412cfd 100644 --- a/client/ui/qml/Controls2/TextTypes/Header2TextType.qml +++ b/client/ui/qml/Controls2/TextTypes/Header2TextType.qml @@ -6,7 +6,7 @@ Text { lineHeight: 30 + LanguageModel.getLineHeightAppend() lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 25 font.weight: 700 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TextTypes/LabelTextType.qml b/client/ui/qml/Controls2/TextTypes/LabelTextType.qml index edad6ef9..9a9a1963 100644 --- a/client/ui/qml/Controls2/TextTypes/LabelTextType.qml +++ b/client/ui/qml/Controls2/TextTypes/LabelTextType.qml @@ -6,7 +6,7 @@ Text { lineHeight: 16 + LanguageModel.getLineHeightAppend() lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray font.pixelSize: 13 font.weight: 400 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml b/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml index eef97c60..40cd7835 100644 --- a/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml +++ b/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml @@ -6,7 +6,7 @@ Text { lineHeight: 21.6 + LanguageModel.getLineHeightAppend() lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 18 font.weight: 400 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TextTypes/ParagraphTextType.qml b/client/ui/qml/Controls2/TextTypes/ParagraphTextType.qml index b2723007..109b2876 100644 --- a/client/ui/qml/Controls2/TextTypes/ParagraphTextType.qml +++ b/client/ui/qml/Controls2/TextTypes/ParagraphTextType.qml @@ -5,7 +5,7 @@ Text { lineHeight: 24 + LanguageModel.getLineHeightAppend() lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 16 font.weight: 400 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TextTypes/SmallTextType.qml b/client/ui/qml/Controls2/TextTypes/SmallTextType.qml index 06966c4f..6c28607a 100644 --- a/client/ui/qml/Controls2/TextTypes/SmallTextType.qml +++ b/client/ui/qml/Controls2/TextTypes/SmallTextType.qml @@ -6,7 +6,7 @@ Text { lineHeight: 20 + LanguageModel.getLineHeightAppend() lineHeightMode: Text.FixedHeight - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray font.pixelSize: 14 font.weight: 400 font.family: "PT Root UI VF" diff --git a/client/ui/qml/Controls2/TopCloseButtonType.qml b/client/ui/qml/Controls2/TopCloseButtonType.qml index af9f4576..1bd7fef6 100644 --- a/client/ui/qml/Controls2/TopCloseButtonType.qml +++ b/client/ui/qml/Controls2/TopCloseButtonType.qml @@ -25,7 +25,7 @@ Popup { id: button image: "qrc:/images/svg/close_black_24dp.svg" - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray implicitWidth: 40 implicitHeight: 40 diff --git a/client/ui/qml/Controls2/VerticalRadioButton.qml b/client/ui/qml/Controls2/VerticalRadioButton.qml index 04ae91a3..1a781f20 100644 --- a/client/ui/qml/Controls2/VerticalRadioButton.qml +++ b/client/ui/qml/Controls2/VerticalRadioButton.qml @@ -14,15 +14,15 @@ RadioButton { property int textElide: Qt.ElideRight property string descriptionText - property string hoveredColor: AmneziaStyle.color.blackHovered + property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite property string defaultColor: AmneziaStyle.color.transparent property string disabledColor: AmneziaStyle.color.transparent property string selectedColor: AmneziaStyle.color.transparent - property string textColor: AmneziaStyle.color.white - property string selectedTextColor: AmneziaStyle.color.orange + property string textColor: AmneziaStyle.color.paleGray + property string selectedTextColor: AmneziaStyle.color.goldenApricot - property string borderFocusedColor: AmneziaStyle.color.white + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 property string imageSource @@ -139,7 +139,7 @@ RadioButton { CaptionTextType { id: description - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray text: root.descriptionText visible: root.descriptionText !== "" diff --git a/client/ui/qml/Controls2/WarningType.qml b/client/ui/qml/Controls2/WarningType.qml index 6d30fcb3..24e5ecad 100644 --- a/client/ui/qml/Controls2/WarningType.qml +++ b/client/ui/qml/Controls2/WarningType.qml @@ -10,9 +10,9 @@ import "TextTypes" Rectangle { id: root - property string textColor: AmneziaStyle.color.white - property string backGroundColor: AmneziaStyle.color.blackLight - property string imageColor: AmneziaStyle.color.white + property string textColor: AmneziaStyle.color.paleGray + property string backGroundColor: AmneziaStyle.color.onyxBlack + property string imageColor: AmneziaStyle.color.paleGray property string textString property int textFormat: Text.PlainText diff --git a/client/ui/qml/Modules/Style/AmneziaStyle.qml b/client/ui/qml/Modules/Style/AmneziaStyle.qml index 7f1850aa..c0038246 100644 --- a/client/ui/qml/Modules/Style/AmneziaStyle.qml +++ b/client/ui/qml/Modules/Style/AmneziaStyle.qml @@ -5,22 +5,22 @@ import QtQuick QtObject { property QtObject color: QtObject { readonly property color transparent: 'transparent' - readonly property color white: '#D7D8DB' - readonly property color whiteHovered: '#C1C2C5' - readonly property color grey: '#878B91' - readonly property color greyDisabled: '#494B50' - readonly property color greyDark: '#2C2D30' - readonly property color blackLight: '#1C1D21' - readonly property color blackHovered: '#01010114' - readonly property color blackPressed: '#0101011f' - readonly property color black: '#0E0E11' - readonly property color orange: '#FBB26A' - readonly property color orangeDark: '#A85809' - readonly property color brownLight: '#84603D' - readonly property color brown: '#633303' - readonly property color brownDark: '#402102' - readonly property color red: '#EB5757' - - readonly property color connectionInProgress: '#261E1A' + readonly property color paleGray: '#D7D8DB' + readonly property color lightGray: '#C1C2C5' + readonly property color mutedGray: '#878B91' + readonly property color charcoalGray: '#494B50' + readonly property color slateGray: '#2C2D30' + readonly property color onyxBlack: '#1C1D21' + readonly property color midnightBlack: '#0E0E11' + readonly property color goldenApricot: '#FBB26A' + readonly property color burntOrange: '#A85809' + readonly property color mutedBrown: '#84603D' + readonly property color richBrown: '#633303' + readonly property color deepBrown: '#402102' + readonly property color vibrantRed: '#EB5757' + readonly property color darkCharcoal: '#261E1A' + readonly property color sheerWhite: Qt.rgba(1, 1, 1, 0.12) + readonly property color translucentWhite: Qt.rgba(1, 1, 1, 0.08) + readonly property color barelyTranslucentWhite: Qt.rgba(1, 1, 1, 0.05) } -} \ No newline at end of file +} diff --git a/client/ui/qml/Pages2/PageDeinstalling.qml b/client/ui/qml/Pages2/PageDeinstalling.qml index 15633fa0..f5fdb29a 100644 --- a/client/ui/qml/Pages2/PageDeinstalling.qml +++ b/client/ui/qml/Pages2/PageDeinstalling.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageDevMenu.qml b/client/ui/qml/Pages2/PageDevMenu.qml new file mode 100644 index 00000000..af6f773a --- /dev/null +++ b/client/ui/qml/Pages2/PageDevMenu.qml @@ -0,0 +1,94 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + defaultActiveFocusItem: focusItem + + Item { + id: focusItem + KeyNavigation.tab: backButton + } + + ColumnLayout { + id: backButtonLayout + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + id: backButton + // KeyNavigation.tab: removeButton + } + } + + FlickableType { + id: fl + anchors.top: backButtonLayout.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + HeaderType { + id: header + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: "Dev menu" + } + + + TextFieldWithHeaderType { + id: passwordTextField + + Layout.fillWidth: true + Layout.topMargin: 16 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + parentFlickable: fl + + headerText: qsTr("Gateway endpoint") + textFieldText: SettingsController.gatewayEndpoint + + buttonImageSource: textFieldText !== "" ? "qrc:/images/controls/refresh-cw.svg" : "" + + clickedFunc: function() { + SettingsController.resetGatewayEndpoint() + } + + textField.onEditingFinished: { + textFieldText = textField.text.replace(/^\s+|\s+$/g, '') + if (textFieldText !== SettingsController.gatewayEndpoint) { + SettingsController.gatewayEndpoint = textFieldText + } + } + + // KeyNavigation.tab: saveButton + } + } + } +} diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index da52d535..8074337a 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -57,10 +57,10 @@ PageType { implicitHeight: 36 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.grey + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.mutedGray borderWidth: 0 visible: isLoggingEnabled ? true : false @@ -94,10 +94,10 @@ PageType { implicitHeight: 36 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.grey + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.mutedGray leftImageColor: AmneziaStyle.color.transparent borderWidth: 0 @@ -106,7 +106,7 @@ PageType { buttonTextLabel.font.weight: 500 property bool isSplitTunnelingEnabled: SitesModel.isTunnelingEnabled || AppSplitTunnelingModel.isTunnelingEnabled || - (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi")) + ServersModel.isDefaultServerDefaultContainerHasSplitTunneling text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled") @@ -243,7 +243,7 @@ PageType { hoverEnabled: false image: "qrc:/images/controls/chevron-down.svg" - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray icon.width: 18 icon.height: 18 @@ -265,11 +265,21 @@ PageType { } } - LabelTextType { - id: collapsedServerMenuDescription - Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 89 : 44 + RowLayout { Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - text: drawer.isCollapsed ? ServersModel.defaultServerDescriptionCollapsed : ServersModel.defaultServerDescriptionExpanded + Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 89 : 44 + spacing: 0 + + Image { + Layout.rightMargin: 8 + visible: source !== "" + source: ServersModel.defaultServerImagePathCollapsed + } + + LabelTextType { + id: collapsedServerMenuDescription + text: drawer.isCollapsed ? ServersModel.defaultServerDescriptionCollapsed : ServersModel.defaultServerDescriptionExpanded + } } } @@ -304,8 +314,8 @@ PageType { DropDownType { id: containersDropDown - rootButtonImageColor: AmneziaStyle.color.black - rootButtonBackgroundColor: AmneziaStyle.color.white + rootButtonImageColor: AmneziaStyle.color.midnightBlack + rootButtonBackgroundColor: AmneziaStyle.color.paleGray rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8) rootButtonBackgroundPressedColor: Qt.rgba(215, 216, 219, 0.65) rootButtonHoveredBorderColor: AmneziaStyle.color.transparent @@ -314,7 +324,7 @@ PageType { rootButtonTextBottomMargin: 8 text: ServersModel.defaultServerDefaultContainerName - textColor: AmneziaStyle.color.black + textColor: AmneziaStyle.color.midnightBlack headerText: qsTr("VPN protocol") headerBackButtonImage: "qrc:/images/controls/arrow-left.svg" @@ -505,7 +515,7 @@ PageType { ImageButtonType { id: serverInfoButton image: "qrc:/images/controls/settings.svg" - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray implicitWidth: 56 implicitHeight: 56 diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index e8a8c149..8651fa27 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index 5ef5771e..5089a764 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index d2330c27..30540a93 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -295,7 +295,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 Layout.preferredHeight: checkboxLayout.implicitHeight - color: AmneziaStyle.color.blackLight + color: AmneziaStyle.color.onyxBlack radius: 16 Connections { diff --git a/client/ui/qml/Pages2/PageProtocolRaw.qml b/client/ui/qml/Pages2/PageProtocolRaw.qml index 1acc5b54..24853afd 100644 --- a/client/ui/qml/Pages2/PageProtocolRaw.qml +++ b/client/ui/qml/Pages2/PageProtocolRaw.qml @@ -192,9 +192,9 @@ PageType { leftPadding: 0 height: 24 - color: AmneziaStyle.color.white - selectionColor: AmneziaStyle.color.brown - selectedTextColor: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray + selectionColor: AmneziaStyle.color.richBrown + selectedTextColor: AmneziaStyle.color.paleGray font.pixelSize: 16 font.weight: Font.Medium @@ -224,7 +224,7 @@ PageType { visible: ServersModel.isProcessedServerHasWriteAccess() text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunction: function() { diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 2cf18544..4d3b2c4e 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml index 4e6a851e..758375b1 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index 6a8094d7..20ee1da6 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2 import PageEnum 1.0 import ContainerEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageServiceDnsSettings.qml b/client/ui/qml/Pages2/PageServiceDnsSettings.qml index 7d9f2df4..bb3cbf96 100644 --- a/client/ui/qml/Pages2/PageServiceDnsSettings.qml +++ b/client/ui/qml/Pages2/PageServiceDnsSettings.qml @@ -70,7 +70,7 @@ PageType { width: parent.width text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed Keys.onTabPressed: root.lastItemTabClicked() diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml index 9b5425db..9bdbf2db 100644 --- a/client/ui/qml/Pages2/PageServiceSftpSettings.qml +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -115,7 +115,7 @@ PageType { descriptionOnTop: true rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: AmneziaStyle.color.white + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { GC.copyToClipBoard(descriptionText) @@ -139,7 +139,7 @@ PageType { KeyNavigation.tab: usernameLabel.rightButton rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: AmneziaStyle.color.white + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { GC.copyToClipBoard(descriptionText) @@ -163,7 +163,7 @@ PageType { KeyNavigation.tab: passwordLabel.eyeButton rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: AmneziaStyle.color.white + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { GC.copyToClipBoard(descriptionText) @@ -194,7 +194,7 @@ PageType { } rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: AmneziaStyle.color.white + rightImageColor: AmneziaStyle.color.paleGray buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg" @@ -218,10 +218,10 @@ PageType { Layout.rightMargin: 16 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 parentFlickable: fl @@ -282,10 +282,10 @@ PageType { implicitHeight: 32 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.orange + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.goldenApricot text: qsTr("Detailed instructions") diff --git a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml index 95343f63..9d21963d 100644 --- a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml +++ b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml @@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2 import PageEnum 1.0 import ContainerProps 1.0 +import Style 1.0 import "./" import "../Controls2" @@ -106,7 +107,7 @@ PageType { descriptionOnTop: true rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: "#D7D8DB" + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { GC.copyToClipBoard(descriptionText) @@ -130,7 +131,7 @@ PageType { KeyNavigation.tab: usernameLabel.rightButton rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: "#D7D8DB" + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { GC.copyToClipBoard(descriptionText) @@ -154,7 +155,7 @@ PageType { KeyNavigation.tab: passwordLabel.eyeButton rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: "#D7D8DB" + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { GC.copyToClipBoard(descriptionText) @@ -179,7 +180,7 @@ PageType { rightButton.KeyNavigation.tab: changeSettingsButton rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: "#D7D8DB" + rightImageColor: AmneziaStyle.color.paleGray buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg" diff --git a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml index de1fefb8..946a77bb 100644 --- a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml +++ b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml @@ -83,10 +83,10 @@ PageType { } descriptionOnTop: true - textColor: AmneziaStyle.color.orange + textColor: AmneziaStyle.color.goldenApricot rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: AmneziaStyle.color.white + rightImageColor: AmneziaStyle.color.paleGray Keys.onTabPressed: lastItemTabClicked(focusItem) diff --git a/client/ui/qml/Pages2/PageSettings.qml b/client/ui/qml/Pages2/PageSettings.qml index f8056c63..bb5ca766 100644 --- a/client/ui/qml/Pages2/PageSettings.qml +++ b/client/ui/qml/Pages2/PageSettings.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts import QtQuick.Dialogs import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" @@ -128,6 +129,26 @@ PageType { DividerType {} + LabelWithButtonType { + id: devConsole + visible: SettingsController.isDevModeEnabled + Layout.fillWidth: true + + text: qsTr("Dev console") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + leftImageSource: "qrc:/images/controls/bug.svg" + + // Keys.onTabPressed: lastItemTabClicked(header) + + clickedFunction: function() { + PageController.goToPage(PageEnum.PageDevMenu) + } + } + + DividerType { + visible: SettingsController.isDevModeEnabled + } + LabelWithButtonType { id: close visible: GC.isDesktop() diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index f6fcea62..1e38a539 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -85,7 +85,7 @@ PageType { font.pixelSize: 14 text: qsTr("Amnezia is a free and open-source application. You can support the developers if you like it.") - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray } ParagraphTextType { @@ -163,7 +163,7 @@ PageType { parentFlickable: fl clickedFunction: function() { - Qt.openUrlExternally(qsTr("https://amnezia.org")) + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) } } @@ -177,7 +177,19 @@ PageType { horizontalAlignment: Text.AlignHCenter text: qsTr("Software version: %1").arg(SettingsController.getAppVersion()) - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray + + MouseArea { + property int clickCount: 0 + anchors.fill: parent + onClicked: { + if (clickCount > 10) { + SettingsController.enableDevMode() + } else { + clickCount++ + } + } + } } BasicButtonType { @@ -188,10 +200,10 @@ PageType { implicitHeight: 32 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.orange + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.goldenApricot text: qsTr("Check for updates") @@ -211,10 +223,10 @@ PageType { implicitHeight: 25 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.orange + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.goldenApricot text: qsTr("Privacy Policy") @@ -222,7 +234,7 @@ PageType { parentFlickable: fl clickedFunc: function() { - Qt.openUrlExternally("https://amnezia.org/en/policy") + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy") } } } diff --git a/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml b/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml new file mode 100644 index 00000000..234e5142 --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml @@ -0,0 +1,103 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + ListView { + id: menuContent + + property var selectedText + + width: parent.width + height: menuContent.contentItem.height + + clip: true + interactive: false + model: ApiCountryModel + + ButtonGroup { + id: containersRadioButtonGroup + } + + delegate: Item { + implicitWidth: parent.width + implicitHeight: content.implicitHeight + + ColumnLayout { + id: content + + anchors.fill: parent + + RowLayout { + VerticalRadioButton { + id: containerRadioButton + + Layout.fillWidth: true + Layout.leftMargin: 16 + + text: countryName + + ButtonGroup.group: containersRadioButtonGroup + + imageSource: "qrc:/images/controls/download.svg" + + checked: index === ApiCountryModel.currentIndex + + onClicked: { + if (index !== ApiCountryModel.currentIndex) { + PageController.showBusyIndicator(true) + var prevIndex = ApiCountryModel.currentIndex + ApiCountryModel.currentIndex = index + if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) { + ApiCountryModel.currentIndex = prevIndex + } + } + } + + MouseArea { + anchors.fill: containerRadioButton + cursorShape: Qt.PointingHandCursor + enabled: false + } + + Keys.onEnterPressed: { + if (checkable) { + checked = true + } + containerRadioButton.clicked() + } + Keys.onReturnPressed: { + if (checkable) { + checked = true + } + containerRadioButton.clicked() + } + } + + Image { + Layout.rightMargin: 32 + Layout.alignment: Qt.AlignRight + + source: "qrc:/countriesFlags/images/flagKit/" + countryCode + ".svg" + } + } + + DividerType { + Layout.fillWidth: true + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml new file mode 100644 index 00000000..f23e36d9 --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -0,0 +1,208 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + defaultActiveFocusItem: focusItem + + FlickableType { + id: fl + anchors.top: parent.top + anchors.bottom: parent.bottom + contentHeight: content.height + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + spacing: 0 + + Item { + id: focusItem +// KeyNavigation.tab: backButton + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/map-pin.svg" + leftText: qsTr("For the region") + rightText: ApiServicesModel.getSelectedServiceData("region") + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/tag.svg" + leftText: qsTr("Price") + rightText: ApiServicesModel.getSelectedServiceData("price") + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/history.svg" + leftText: qsTr("Work period") + rightText: ApiServicesModel.getSelectedServiceData("workPeriod") + + visible: rightText !== "" + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/gauge.svg" + leftText: qsTr("Speed") + rightText: ApiServicesModel.getSelectedServiceData("speed") + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + onLinkActivated: function(link) { + Qt.openUrlExternally(link) + } + textFormat: Text.RichText + text: { + var text = ApiServicesModel.getSelectedServiceData("features") + if (text === undefined) { + return "" + } + return text.replace("%1", LanguageModel.getCurrentSiteUrl()) + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + } + + LabelWithButtonType { + id: supportUuid + Layout.fillWidth: true + + text: qsTr("Support tag") + descriptionText: SettingsController.getInstallationUuid() + + descriptionOnTop: true + +// parentFlickable: fl +// KeyNavigation.tab: passwordLabel.eyeButton + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: AmneziaStyle.color.paleGray + + clickedFunction: function() { + GC.copyToClipBoard(descriptionText) + PageController.showNotificationMessage(qsTr("Copied")) + if (!GC.isMobile()) { + this.rightButton.forceActiveFocus() + } + } + } + + BasicButtonType { + id: resetButton + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 24 + Layout.bottomMargin: 16 + Layout.leftMargin: 8 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + textColor: AmneziaStyle.color.vibrantRed + + text: qsTr("Reload API config") + +// Keys.onTabPressed: lastItemTabClicked(focusItem) + + clickedFunc: function() { + var headerText = qsTr("Reload API config?") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { + PageController.showNotificationMessage(qsTr("Cannot reload API config during active connection")) + } else { + PageController.showBusyIndicator(true) + InstallController.updateServiceFromApi(ServersModel.processedIndex, "", "", true) + PageController.showBusyIndicator(false) + } + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + removeButton.forceActiveFocus() + } + } + + showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + } + + BasicButtonType { + id: removeButton + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: 16 + Layout.leftMargin: 8 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + textColor: AmneziaStyle.color.vibrantRed + + text: qsTr("Remove from application") + +// Keys.onTabPressed: lastItemTabClicked(focusItem) + + clickedFunc: function() { + var headerText = qsTr("Remove from application?") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { + PageController.showNotificationMessage(qsTr("Cannot remove server during active connection")) + } else { + PageController.showBusyIndicator(true) + InstallController.removeProcessedServer() + PageController.showBusyIndicator(false) + } + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + removeButton.forceActiveFocus() + } + } + + showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index 2a650d6b..2e8bda2f 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -215,7 +215,7 @@ PageType { text: appPath rightImageSource: "qrc:/images/controls/trash.svg" - rightImageColor: AmneziaStyle.color.white + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { var headerText = qsTr("Remove ") + appPath + "?" @@ -242,7 +242,7 @@ PageType { Rectangle { anchors.fill: addAppButton anchors.bottomMargin: -24 - color: AmneziaStyle.color.black + color: AmneziaStyle.color.midnightBlack opacity: 0.8 } diff --git a/client/ui/qml/Pages2/PageSettingsApplication.qml b/client/ui/qml/Pages2/PageSettingsApplication.qml index 1693018c..0f85ac30 100644 --- a/client/ui/qml/Pages2/PageSettingsApplication.qml +++ b/client/ui/qml/Pages2/PageSettingsApplication.qml @@ -224,7 +224,7 @@ PageType { text: qsTr("Reset settings and remove all data from the application") rightImageSource: "qrc:/images/controls/chevron-right.svg" - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed Keys.onTabPressed: lastItemTabClicked() parentFlickable: fl @@ -241,7 +241,7 @@ PageType { } else { SettingsController.clearSettings() - PageController.replaceStartPage() + PageController.goToPageHome() } if (!GC.isMobile()) { diff --git a/client/ui/qml/Pages2/PageSettingsBackup.qml b/client/ui/qml/Pages2/PageSettingsBackup.qml index 0a4f5994..abede9b3 100644 --- a/client/ui/qml/Pages2/PageSettingsBackup.qml +++ b/client/ui/qml/Pages2/PageSettingsBackup.qml @@ -28,7 +28,6 @@ PageType { function onRestoreBackupFinished() { PageController.showNotificationMessage(qsTr("Settings restored from backup file")) - //goToStartPage() PageController.goToPageHome() } @@ -122,10 +121,10 @@ PageType { Layout.topMargin: -8 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Restore from backup") diff --git a/client/ui/qml/Pages2/PageSettingsConnection.qml b/client/ui/qml/Pages2/PageSettingsConnection.qml index 218ffe28..31b1c764 100644 --- a/client/ui/qml/Pages2/PageSettingsConnection.qml +++ b/client/ui/qml/Pages2/PageSettingsConnection.qml @@ -3,6 +3,7 @@ import QtQuick.Controls import QtQuick.Layouts import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageSettingsDns.qml b/client/ui/qml/Pages2/PageSettingsDns.qml index a73de327..1d7517d9 100644 --- a/client/ui/qml/Pages2/PageSettingsDns.qml +++ b/client/ui/qml/Pages2/PageSettingsDns.qml @@ -38,7 +38,7 @@ PageType { anchors.bottom: parent.bottom contentHeight: content.height - property var isServerFromApi: ServersModel.getDefaultServerData("isServerFromApi") + property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex) enabled: !isServerFromApi @@ -103,10 +103,10 @@ PageType { Layout.fillWidth: true defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Restore default") diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index 6a7fa385..3ab0df8a 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -115,7 +115,7 @@ disabled after 14 days, and all log files will be deleted.") Layout.fillWidth: true text: qsTr("Open folder with logs") - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray } } @@ -160,7 +160,7 @@ disabled after 14 days, and all log files will be deleted.") Layout.fillWidth: true text: qsTr("Save logs to file") - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray } } @@ -209,7 +209,7 @@ disabled after 14 days, and all log files will be deleted.") Layout.fillWidth: true text: qsTr("Clear logs") - color: AmneziaStyle.color.white + color: AmneziaStyle.color.paleGray } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index ffee0289..e170a351 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -38,7 +38,7 @@ PageType { function onRemoveProcessedServerFinished(finishedMessage) { if (!ServersModel.getServersCount()) { - PageController.replaceStartPage() + PageController.goToPageHome() } else { PageController.goToStartPage() PageController.goToPage(PageEnum.PageSettingsServersList) @@ -119,7 +119,7 @@ PageType { Layout.fillWidth: true text: qsTr("Reboot server") - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed KeyNavigation.tab: labelWithButton3 @@ -160,7 +160,7 @@ PageType { Layout.fillWidth: true text: qsTr("Remove server from application") - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed Keys.onTabPressed: { if (content.isServerWithWriteAccess) { @@ -208,7 +208,7 @@ PageType { Layout.fillWidth: true text: qsTr("Clear server from Amnezia software") - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed Keys.onTabPressed: labelWithButton5.visible ? labelWithButton5.forceActiveFocus() : @@ -247,11 +247,11 @@ PageType { LabelWithButtonType { id: labelWithButton5 - visible: ServersModel.getProcessedServerData("isServerFromApi") + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") Layout.fillWidth: true text: qsTr("Reset API config") - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed Keys.onTabPressed: root.lastItemTabClickedSignal() @@ -285,7 +285,7 @@ PageType { } DividerType { - visible: ServersModel.getProcessedServerData("isServerFromApi") + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index cf649ae0..95ae5c8a 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -19,13 +19,19 @@ import "../Components" PageType { id: root + property int pageSettingsServerProtocols: 0 + property int pageSettingsServerServices: 1 + property int pageSettingsServerData: 2 + property int pageSettingsApiServerInfo: 3 + property int pageSettingsApiLanguageList: 4 + defaultActiveFocusItem: focusItem Connections { target: PageController function onGoToPageSettingsServerServices() { - tabBar.currentIndex = 1 + tabBar.currentIndex = root.pageSettingsServerServices } } @@ -70,6 +76,15 @@ PageType { BackButtonType { id: backButton KeyNavigation.tab: headerContent.actionButton + + backButtonFunction: function() { + if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo && + ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { + nestedStackView.currentIndex = root.pageSettingsApiLanguageList + } else { + PageController.closePage() + } + } } HeaderType { @@ -78,11 +93,15 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - actionButtonImage: "qrc:/images/controls/edit-3.svg" + actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg" headerText: name descriptionText: { - if (ServersModel.isProcessedServerHasWriteAccess()) { + if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { + return ApiServicesModel.getSelectedServiceData("serviceDescription") + } else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) { + return serverDescription + } else if (ServersModel.isProcessedServerHasWriteAccess()) { return credentialsLogin + " · " + hostName } else { return hostName @@ -92,7 +111,11 @@ PageType { KeyNavigation.tab: tabBar actionButtonFunction: function() { - serverNameEditDrawer.open() + if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) { + nestedStackView.currentIndex = root.pageSettingsApiServerInfo + } else { + serverNameEditDrawer.open() + } } } @@ -172,13 +195,16 @@ PageType { Layout.fillWidth: true - currentIndex: (ServersModel.getProcessedServerData("isServerFromApi") - && !ServersModel.getProcessedServerData("hasInstalledContainers")) ? 2 : 0 + currentIndex: (ServersModel.getProcessedServerData("isServerFromTelegramApi") + && !ServersModel.getProcessedServerData("hasInstalledContainers")) ? + root.pageSettingsServerData : root.pageSettingsServerProtocols background: Rectangle { color: AmneziaStyle.color.transparent } + visible: !ServersModel.getProcessedServerData("isServerFromGatewayApi") + activeFocusOnTab: true onFocusChanged: { if (activeFocus) { @@ -190,45 +216,53 @@ PageType { id: protocolsTab visible: protocolsPage.installedProtocolsCount width: protocolsPage.installedProtocolsCount ? undefined : 0 - isSelected: tabBar.currentIndex === 0 + isSelected: tabBar.currentIndex === root.pageSettingsServerProtocols text: qsTr("Protocols") KeyNavigation.tab: servicesTab - Keys.onReturnPressed: tabBar.currentIndex = 0 - Keys.onEnterPressed: tabBar.currentIndex = 0 + Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerProtocols + Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerProtocols } + TabButtonType { id: servicesTab visible: servicesPage.installedServicesCount width: servicesPage.installedServicesCount ? undefined : 0 - isSelected: tabBar.currentIndex === 1 + isSelected: tabBar.currentIndex === root.pageSettingsServerServices text: qsTr("Services") KeyNavigation.tab: dataTab - Keys.onReturnPressed: tabBar.currentIndex = 1 - Keys.onEnterPressed: tabBar.currentIndex = 1 + Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerServices + Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerServices } + TabButtonType { id: dataTab - isSelected: tabBar.currentIndex === 2 + isSelected: tabBar.currentIndex === root.pageSettingsServerData text: qsTr("Management") - Keys.onReturnPressed: tabBar.currentIndex = 2 - Keys.onEnterPressed: tabBar.currentIndex = 2 - KeyNavigation.tab: stackView.currentIndex === 0 ? - protocolsPage : - stackView.currentIndex === 1 ? - servicesPage : - dataPage + Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerData + Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerData + Keys.onTabPressed: function() { + if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) { + return protocolsPage + } else if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) { + return servicesPage + } else { + return dataPage + } + } } } StackLayout { - id: stackView + id: nestedStackView Layout.preferredWidth: root.width Layout.preferredHeight: root.height - tabBar.implicitHeight - header.implicitHeight - currentIndex: tabBar.currentIndex + currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ? + (ServersModel.getProcessedServerData("isCountrySelectionAvailable") ? + root.pageSettingsApiLanguageList : root.pageSettingsApiServerInfo) : tabBar.currentIndex PageSettingsServerProtocols { id: protocolsPage @@ -236,18 +270,34 @@ PageType { onLastItemTabClickedSignal: lastItemTabClicked(focusItem) } + PageSettingsServerServices { id: servicesPage stackView: root.stackView onLastItemTabClickedSignal: lastItemTabClicked(focusItem) } + PageSettingsServerData { id: dataPage stackView: root.stackView onLastItemTabClickedSignal: lastItemTabClicked(focusItem) } + + PageSettingsApiServerInfo { + id: apiInfoPage + stackView: root.stackView + +// onLastItemTabClickedSignal: lastItemTabClicked(focusItem) + } + + PageSettingsApiLanguageList { + id: apiLanguageListPage + stackView: root.stackView + +// onLastItemTabClickedSignal: lastItemTabClicked(focusItem) + } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index e06a637e..6410156d 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -184,7 +184,7 @@ PageType { Keys.onTabPressed: lastItemTabClicked(focusItem) text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() - textColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed clickedFunction: function() { var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName()) diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocols.qml b/client/ui/qml/Pages2/PageSettingsServerProtocols.qml index 33585d71..0bc1fc7d 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocols.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocols.qml @@ -8,6 +8,7 @@ import PageEnum 1.0 import ProtocolEnum 1.0 import ContainerProps 1.0 import ContainersModelFilters 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageSettingsServerServices.qml b/client/ui/qml/Pages2/PageSettingsServerServices.qml index 72a4d3f7..440fd8db 100644 --- a/client/ui/qml/Pages2/PageSettingsServerServices.qml +++ b/client/ui/qml/Pages2/PageSettingsServerServices.qml @@ -8,6 +8,7 @@ import PageEnum 1.0 import ProtocolEnum 1.0 import ContainerProps 1.0 import ContainersModelFilters 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index 596505a9..102dd46f 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -7,6 +7,7 @@ import SortFilterProxyModel 0.2 import PageEnum 1.0 import ProtocolEnum 1.0 import ContainerProps 1.0 +import Style 1.0 import "./" import "../Controls2" @@ -119,7 +120,11 @@ PageType { servicesNameString += servicesName[i] + " · " } - return servicesNameString + hostName + if (ServersModel.isServerFromApi(index)) { + return servicesNameString + serverDescription + } else { + return servicesNameString + hostName + } } rightImageSource: "qrc:/images/controls/chevron-right.svg" diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index 60821ebc..f5fe285a 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -21,7 +21,7 @@ import "../Components" PageType { id: root - property var isServerFromApi: ServersModel.getDefaultServerData("isServerFromApi") + property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi") defaultActiveFocusItem: searchField.textField @@ -36,7 +36,7 @@ PageType { if (ConnectionController.isConnected) { PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection")) root.pageEnabled = false - } else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && isServerFromApi) { + } else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling) { PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function")) root.pageEnabled = false } else { @@ -266,7 +266,7 @@ PageType { text: url descriptionText: ip rightImageSource: "qrc:/images/controls/trash.svg" - rightImageColor: AmneziaStyle.color.white + rightImageColor: AmneziaStyle.color.paleGray clickedFunction: function() { var headerText = qsTr("Remove ") + url + "?" @@ -300,7 +300,7 @@ PageType { Rectangle { anchors.fill: addSiteButton anchors.bottomMargin: -24 - color: AmneziaStyle.color.black + color: AmneziaStyle.color.midnightBlack opacity: 0.8 } @@ -341,7 +341,7 @@ PageType { implicitHeight: 56 image: "qrc:/images/controls/more-vertical.svg" - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray onClicked: function () { moreActionsDrawer.open() diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml new file mode 100644 index 00000000..75fd3d47 --- /dev/null +++ b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml @@ -0,0 +1,154 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + defaultActiveFocusItem: focusItem + + FlickableType { + id: fl + anchors.top: parent.top + anchors.bottom: parent.bottom + contentHeight: content.height + continueButton.implicitHeight + continueButton.anchors.bottomMargin + continueButton.anchors.topMargin + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + spacing: 0 + + Item { + id: focusItem + KeyNavigation.tab: backButton + } + + BackButtonType { + id: backButton + Layout.topMargin: 20 +// KeyNavigation.tab: fileButton.rightButton + } + + HeaderType { + Layout.fillWidth: true + Layout.topMargin: 8 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 32 + + headerText: ApiServicesModel.getSelectedServiceData("name") + descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription") + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/map-pin.svg" + leftText: qsTr("For the region") + rightText: ApiServicesModel.getSelectedServiceData("region") + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/tag.svg" + leftText: qsTr("Price") + rightText: ApiServicesModel.getSelectedServiceData("price") + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/history.svg" + leftText: qsTr("Work period") + rightText: ApiServicesModel.getSelectedServiceData("workPeriod") + + visible: rightText !== "" + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/gauge.svg" + leftText: qsTr("Speed") + rightText: ApiServicesModel.getSelectedServiceData("speed") + } + + LabelWithImageType { + Layout.fillWidth: true + Layout.margins: 16 + + imageSource: "qrc:/images/controls/info.svg" + leftText: qsTr("Features") + rightText: "" + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + onLinkActivated: function(link) { + Qt.openUrlExternally(link) + } + textFormat: Text.RichText + text: { + var text = ApiServicesModel.getSelectedServiceData("features") + return text.replace("%1", LanguageModel.getCurrentSiteUrl()) + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + } + } + } + + BasicButtonType { + id: continueButton + + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + + anchors.topMargin: 32 + anchors.rightMargin: 16 + anchors.leftMargin: 16 + anchors.bottomMargin: 32 + + text: qsTr("Connect") + + clickedFunc: function() { + var endpoint = ApiServicesModel.getStoreEndpoint() + if (endpoint !== undefined && endpoint !== "") { + Qt.openUrlExternally(endpoint) + PageController.closePage() + PageController.closePage() + } else { + PageController.showBusyIndicator(true) + InstallController.installServiceFromApi() + PageController.showBusyIndicator(false) + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml new file mode 100644 index 00000000..cb79f19e --- /dev/null +++ b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml @@ -0,0 +1,100 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" + +PageType { + id: root + + defaultActiveFocusItem: focusItem + + FlickableType { + id: fl + anchors.top: parent.top + anchors.bottom: parent.bottom + contentHeight: content.height + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + spacing: 0 + + Item { + id: focusItem + KeyNavigation.tab: backButton + } + + BackButtonType { + id: backButton + Layout.topMargin: 20 +// KeyNavigation.tab: fileButton.rightButton + } + + HeaderType { + Layout.fillWidth: true + Layout.topMargin: 8 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 32 + + headerText: qsTr("VPN by Amnezia") + descriptionText: qsTr("Choose a VPN service that suits your needs.") + } + + ListView { + id: containers + width: parent.width + height: containers.contentItem.height + spacing: 16 + + currentIndex: 1 + interactive: false + model: ApiServicesModel + + delegate: Item { + implicitWidth: containers.width + implicitHeight: delegateContent.implicitHeight + + ColumnLayout { + id: delegateContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + CardWithIconsType { + id: card + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: name + bodyText: cardDescription + footerText: price + + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + onClicked: { + ApiServicesModel.setServiceIndex(index) + PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo) + } + } + } + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index f7b8949b..3febca4c 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts import QtQuick.Dialogs import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" @@ -17,7 +18,9 @@ PageType { target: ImportController function onQrDecodingFinished() { - PageController.closePage() + if (Qt.platform.os === "ios") { + PageController.closePage() + } PageController.goToPage(PageEnum.PageSetupWizardViewConfig) } } @@ -41,46 +44,163 @@ PageType { Item { id: focusItem - KeyNavigation.tab: backButton + KeyNavigation.tab: textKey.textField } - BackButtonType { - id: backButton - Layout.topMargin: 20 - KeyNavigation.tab: fileButton.rightButton - } HeaderType { Layout.fillWidth: true - Layout.topMargin: 8 + Layout.topMargin: 24 Layout.rightMargin: 16 Layout.leftMargin: 16 - headerText: qsTr("Server connection") - descriptionText: qsTr("Do not use connection codes from untrusted sources, as they may be created to intercept your data.") + headerText: qsTr("Connection") } - Header2TextType { + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 32 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 24 + + text: qsTr("Insert the key, add a configuration file or scan the QR-code") + } + + TextFieldWithHeaderType { + id: textKey + Layout.fillWidth: true - Layout.topMargin: 48 Layout.rightMargin: 16 Layout.leftMargin: 16 - text: qsTr("What do you have?") + headerText: qsTr("Insert key") + buttonText: qsTr("Insert") + + clickedFunc: function() { + textField.text = "" + textField.paste() + } + + KeyNavigation.tab: continueButton } - LabelWithButtonType { - id: fileButton + BasicButtonType { + id: continueButton + Layout.fillWidth: true Layout.topMargin: 16 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + visible: textKey.textFieldText !== "" + + text: qsTr("Continue") + Keys.onTabPressed: lastItemTabClicked(focusItem) + + clickedFunc: function() { + if (ImportController.extractConfigFromData(textKey.textFieldText)) { + PageController.goToPage(PageEnum.PageSetupWizardViewConfig) + } + } + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 32 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 24 + + color: AmneziaStyle.color.charcoalGray + text: qsTr("Other connection options") + } + + CardWithIconsType { + id: apiInstalling + + visible: false + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 + + headerText: qsTr("VPN by Amnezia") + bodyText: qsTr("Connect to classic paid and free VPN services from Amnezia") - text: !ServersModel.getServersCount() ? qsTr("File with connection settings or backup") : qsTr("File with connection settings") rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/folder-open.svg" + leftImageSource: "qrc:/images/controls/amnezia.svg" - KeyNavigation.tab: qrButton.visible ? qrButton.rightButton : textButton.rightButton + onClicked: function() { + PageController.showBusyIndicator(true) + var result = InstallController.fillAvailableServices() + PageController.showBusyIndicator(false) + if (result) { + PageController.goToPage(PageEnum.PageSetupWizardApiServicesList) + } + } + } - clickedFunction: function() { + CardWithIconsType { + id: manualInstalling + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 + + headerText: qsTr("Self-hosted VPN") + bodyText: qsTr("Configure Amnezia VPN on your own server") + + rightImageSource: "qrc:/images/controls/chevron-right.svg" + leftImageSource: "qrc:/images/controls/server.svg" + + onClicked: { + PageController.goToPage(PageEnum.PageSetupWizardCredentials) + } + } + + CardWithIconsType { + id: backupRestore + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 + + visible: PageController.isStartPageVisible() + + headerText: qsTr("Restore from backup") + + rightImageSource: "qrc:/images/controls/chevron-right.svg" + leftImageSource: "qrc:/images/controls/archive-restore.svg" + + onClicked: { + var filePath = SystemController.getFileName(qsTr("Open backup file"), + qsTr("Backup files (*.backup)")) + if (filePath !== "") { + PageController.showBusyIndicator(true) + SettingsController.restoreAppConfig(filePath) + PageController.showBusyIndicator(false) + } + } + } + + CardWithIconsType { + id: openFile + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 + + headerText: qsTr("File with connection settings") + + rightImageSource: "qrc:/images/controls/chevron-right.svg" + leftImageSource: "qrc:/images/controls/folder-search-2.svg" + + onClicked: { var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" : "Config files (*.vpn *.ovpn *.conf *.json)" var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter) @@ -92,20 +212,22 @@ PageType { } } - DividerType {} + CardWithIconsType { + id: scanQr - LabelWithButtonType { - id: qrButton Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 + visible: SettingsController.isCameraPresent() - text: qsTr("QR code") + headerText: qsTr("QR code") + rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/qr-code.svg" + leftImageSource: "qrc:/images/controls/scan-line.svg" - KeyNavigation.tab: textButton.rightButton - - clickedFunction: function() { + onClicked: { ImportController.startDecodingQr() if (Qt.platform.os === "ios") { PageController.goToPage(PageEnum.PageSetupWizardQrReader) @@ -113,26 +235,25 @@ PageType { } } - DividerType { - visible: SettingsController.isCameraPresent() - } + CardWithIconsType { + id: siteLink - LabelWithButtonType { - id: textButton Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 + + visible: PageController.isStartPageVisible() + + headerText: qsTr("I have nothing") - text: qsTr("Key as text") rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/text-cursor.svg" + leftImageSource: "qrc:/images/controls/help-circle.svg" - Keys.onTabPressed: lastItemTabClicked(focusItem) - - clickedFunction: function() { - PageController.goToPage(PageEnum.PageSetupWizardTextKey) + onClicked: { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) } } - - DividerType {} } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index ea522363..7f1c3eed 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -3,6 +3,7 @@ import QtQuick.Controls import QtQuick.Layouts import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" @@ -142,6 +143,23 @@ PageType { text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties") } + + CardWithIconsType { + id: siteLink + + Layout.fillWidth: true + Layout.bottomMargin: 16 + + headerText: qsTr("How to run your VPN server") + bodyText: qsTr("Where to get connection data, step-by-step instructions for buying a VPS") + + rightImageSource: "qrc:/images/controls/chevron-right.svg" + leftImageSource: "qrc:/images/controls/help-circle.svg" + + onClicked: { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide") + } + } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index 0cf4c88c..02a7c928 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -187,10 +187,10 @@ PageType { anchors.bottomMargin: 24 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 Keys.onTabPressed: lastItemTabClicked(focusItem) diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index b94e40f1..1128761d 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" @@ -46,14 +47,7 @@ PageType { ServersModel.processedIndex = ServersModel.defaultIndex } - PageController.goToStartPage() - if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSetupWizardStart)) { - PageController.replaceStartPage() - } - if (stackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageHome)) { - PageController.goToPageHome() - } - + PageController.goToPageHome() PageController.showNotificationMessage(finishedMessage) } diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index d3e4aa4a..48741feb 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -95,10 +95,10 @@ PageType { implicitHeight: 32 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.orange + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.goldenApricot text: qsTr("More detailed") KeyNavigation.tab: transportProtoSelector diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml index 59da549c..48265f66 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml @@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2 import PageEnum 1.0 import ProtocolEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageSetupWizardStart.qml b/client/ui/qml/Pages2/PageSetupWizardStart.qml index ee7e2a24..b12c7830 100644 --- a/client/ui/qml/Pages2/PageSetupWizardStart.qml +++ b/client/ui/qml/Pages2/PageSetupWizardStart.qml @@ -14,183 +14,44 @@ import "../Components" PageType { id: root - property bool isControlsDisabled: false - defaultActiveFocusItem: focusItem - Connections { - target: PageController + ColumnLayout { + id: content - function onGoToPageViewConfig() { - PageController.goToPage(PageEnum.PageSetupWizardViewConfig) + anchors.fill: parent + spacing: 0 + + Image { + id: image + source: "qrc:/images/amneziaBigLogo.png" + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + Layout.topMargin: 32 + Layout.preferredWidth: 360 + Layout.preferredHeight: 287 } - function onClosePage() { - if (stackView.depth <= 1) { - PageController.hideWindow() - return - } - stackView.pop() + Item { + id: focusItem + KeyNavigation.tab: startButton } - function onGoToPage(page, slide) { - var pagePath = PageController.getPagePath(page) - if (slide) { - stackView.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition) - } else { - stackView.push(pagePath, { "objectName" : pagePath }, StackView.Immediate) - } - } + BasicButtonType { + id: startButton + Layout.fillWidth: true + Layout.bottomMargin: 48 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + Layout.alignment: Qt.AlignBottom - function onGoToStartPage() { - while (stackView.depth > 1) { - stackView.pop() - } - } + text: qsTr("Let's get started") - function onDisableControls(disabled) { - isControlsDisabled = disabled - } - - function onDisableTabBar(disabled) { - isControlsDisabled = disabled - } - - function onEscapePressed() { - if (isControlsDisabled) { - return + clickedFunc: function() { + PageController.goToPage(PageEnum.PageSetupWizardConfigSource) } - PageController.closePage() - } - } - - Connections { - target: SettingsController - - function onRestoreBackupFinished() { - PageController.showNotificationMessage(qsTr("Settings restored from backup file")) - PageController.replaceStartPage() - } - } - - Connections { - target: InstallController - - function onInstallationErrorOccurred(error) { - PageController.showBusyIndicator(false) - PageController.showErrorMessage(error) - - var currentPageName = stackView.currentItem.objectName - - if (currentPageName === PageController.getPagePath(PageEnum.PageSetupWizardInstalling)) { - PageController.closePage() - } - } - } - - Connections { - target: ImportController - - function onRestoreAppConfig(data) { - PageController.showBusyIndicator(true) - SettingsController.restoreAppConfigFromData(data) - PageController.showBusyIndicator(false) - } - - function onImportErrorOccurred(error, goToPageHome) { - PageController.showErrorMessage(error) - } - } - - FlickableType { - id: fl - anchors.top: parent.top - anchors.bottom: parent.bottom - contentHeight: content.height - - ColumnLayout { - id: content - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - spacing: 0 - - Image { - id: image - source: "qrc:/images/amneziaBigLogo.png" - - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 32 - Layout.leftMargin: 8 - Layout.rightMargin: 8 - Layout.preferredWidth: 344 - Layout.preferredHeight: 279 - } - - ParagraphTextType { - Layout.fillWidth: true - Layout.topMargin: 50 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - text: qsTr("Free service for creating a personal VPN on your server.") + - qsTr(" Helps you access blocked content without revealing your privacy, even to VPN providers.") - } - - Item { - id: focusItem - KeyNavigation.tab: startButton - } - - BasicButtonType { - id: startButton - Layout.fillWidth: true - Layout.topMargin: 32 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - text: qsTr("I have the data to connect") - - clickedFunc: function() { - connectionTypeSelection.open() - } - - KeyNavigation.tab: startButton2 - } - - BasicButtonType { - id: startButton2 - Layout.fillWidth: true - Layout.topMargin: 8 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white - borderWidth: 1 - - text: qsTr("I have nothing") - - clickedFunc: function() { - Qt.openUrlExternally(qsTr("https://amnezia.org/instructions/0_starter-guide")) - } - - Keys.onTabPressed: lastItemTabClicked(focusItem) - } - } - } - - ConnectionTypeSelectionDrawer { - id: connectionTypeSelection - - onClosed: { - PageController.forceTabBarActiveFocus() - root.defaultActiveFocusItem.forceActiveFocus() + Keys.onTabPressed: lastItemTabClicked(focusItem) } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index 5c9da47a..c4227df1 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -3,6 +3,7 @@ import QtQuick.Controls import QtQuick.Layouts import PageEnum 1.0 +import Style 1.0 import "./" import "../Controls2" diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index 54f64423..3aac1555 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -51,13 +51,7 @@ PageType { ServersModel.processedIndex = ServersModel.defaultIndex } - PageController.goToStartPage() - if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSetupWizardStart)) { - PageController.replaceStartPage() - } - if (stackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageHome)) { - PageController.goToPageHome() - } + PageController.goToPageHome() } } @@ -107,10 +101,10 @@ PageType { implicitHeight: 32 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.orange + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.goldenApricot text: showContent ? qsTr("Collapse content") : qsTr("Show content") KeyNavigation.tab: connectButton @@ -139,8 +133,8 @@ PageType { iconPath: "qrc:/images/controls/alert-circle.svg" - textColor: AmneziaStyle.color.red - imageColor: AmneziaStyle.color.red + textColor: AmneziaStyle.color.vibrantRed + imageColor: AmneziaStyle.color.vibrantRed } WarningType { @@ -159,7 +153,7 @@ PageType { implicitHeight: configContent.implicitHeight radius: 10 - color: AmneziaStyle.color.blackLight + color: AmneziaStyle.color.onyxBlack visible: showContent @@ -180,7 +174,7 @@ PageType { Rectangle { anchors.fill: columnContent anchors.bottomMargin: -24 - color: AmneziaStyle.color.black + color: AmneziaStyle.color.midnightBlack opacity: 0.8 } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 459b985f..33577d74 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -273,7 +273,7 @@ PageType { implicitWidth: accessTypeSelectorContent.implicitWidth implicitHeight: accessTypeSelectorContent.implicitHeight - color: AmneziaStyle.color.blackLight + color: AmneziaStyle.color.onyxBlack radius: 16 RowLayout { @@ -327,7 +327,7 @@ PageType { visible: accessTypeSelector.currentIndex === 0 text: qsTr("Share VPN access without the ability to manage the server") - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray } TextFieldWithHeaderType { @@ -658,7 +658,7 @@ PageType { ImageButtonType { id: closeSearchButton image: "qrc:/images/controls/close.svg" - imageColor: AmneziaStyle.color.white + imageColor: AmneziaStyle.color.paleGray Keys.onTabPressed: { if (!GC.isMobile()) { @@ -806,7 +806,7 @@ PageType { ColumnLayout { id: textColumn - property string textColor: AmneziaStyle.color.grey + property string textColor: AmneziaStyle.color.mutedGray Layout.bottomMargin: 24 ParagraphTextType { @@ -853,10 +853,10 @@ PageType { Layout.topMargin: 24 defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Rename") @@ -946,10 +946,10 @@ PageType { Layout.fillWidth: true defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Revoke") diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index f22132c1..4807c030 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -67,7 +67,7 @@ PageType { text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") + qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ") - color: AmneziaStyle.color.grey + color: AmneziaStyle.color.mutedGray } DropDownType { diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 1e9ba16f..770347ca 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -24,8 +24,14 @@ PageType { target: PageController function onGoToPageHome() { - tabBar.setCurrentIndex(0) - tabBarStackView.goToTabBarPage(PageEnum.PageHome) + if (PageController.isStartPageVisible()) { + tabBar.visible = false + tabBarStackView.goToTabBarPage(PageEnum.PageSetupWizardStart) + } else { + tabBar.visible = true + tabBar.setCurrentIndex(0) + tabBarStackView.goToTabBarPage(PageEnum.PageHome) + } } function onGoToPageSettings() { @@ -65,7 +71,6 @@ PageType { } function onGoToStartPage() { - connectionTypeSelection.close() while (tabBarStackView.depth > 1) { tabBarStackView.pop() } @@ -78,9 +83,9 @@ PageType { var pageName = tabBarStackView.currentItem.objectName if ((pageName === PageController.getPagePath(PageEnum.PageShare)) || - (pageName === PageController.getPagePath(PageEnum.PageSettings))) { + (pageName === PageController.getPagePath(PageEnum.PageSettings)) || + (pageName === PageController.getPagePath(PageEnum.PageSetupWizardConfigSource))) { PageController.goToPageHome() - tabBar.previousIndex = 0 } else { PageController.closePage() } @@ -126,6 +131,33 @@ PageType { function onCachedProfileCleared(message) { PageController.showNotificationMessage(message) } + + function onApiConfigRemoved(message) { + PageController.showNotificationMessage(message) + } + + function onInstallServerFromApiFinished(message) { + PageController.showBusyIndicator(false) + if (!ConnectionController.isConnected) { + ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1); + ServersModel.processedIndex = ServersModel.defaultIndex + } + + PageController.goToPageHome() + PageController.showNotificationMessage(message) + } + + function onChangeApiCountryFinished(message) { + PageController.showBusyIndicator(false) + + PageController.goToPageHome() + PageController.showNotificationMessage(message) + } + + function onReloadServerFromApiFinished(message) { + PageController.goToPageHome() + PageController.showNotificationMessage(message) + } } Connections { @@ -151,6 +183,12 @@ PageType { function onImportErrorOccurred(error, goToPageHome) { PageController.showErrorMessage(error) } + + function onRestoreAppConfig(data) { + PageController.showBusyIndicator(true) + SettingsController.restoreAppConfigFromData(data) + PageController.showBusyIndicator(false) + } } Connections { @@ -159,6 +197,11 @@ PageType { function onLoggingDisableByWatcher() { PageController.showNotificationMessage(qsTr("Logging was disabled after 14 days, log files were deleted")) } + + function onRestoreBackupFinished() { + PageController.showNotificationMessage(qsTr("Settings restored from backup file")) + PageController.goToPageHome() + } } StackViewType { @@ -169,31 +212,37 @@ PageType { anchors.left: parent.left anchors.bottom: tabBar.top - width: parent.width - height: root.height - tabBar.implicitHeight - enabled: !root.isControlsDisabled function goToTabBarPage(page) { - connectionTypeSelection.close() - var pagePath = PageController.getPagePath(page) tabBarStackView.clear(StackView.Immediate) tabBarStackView.replace(pagePath, { "objectName" : pagePath }, StackView.Immediate) } Component.onCompleted: { - var pagePath = PageController.getPagePath(PageEnum.PageHome) - ServersModel.processedIndex = ServersModel.defaultIndex + var pagePath + if (PageController.isStartPageVisible()) { + tabBar.visible = false + pagePath = PageController.getPagePath(PageEnum.PageSetupWizardStart) + } else { + tabBar.visible = true + pagePath = PageController.getPagePath(PageEnum.PageHome) + ServersModel.processedIndex = ServersModel.defaultIndex + } + tabBarStackView.push(pagePath, { "objectName" : pagePath }) } + + Keys.onPressed: function(event) { + PageController.keyPressEvent(event.key) + event.accepted = true + } } TabBar { id: tabBar - property int previousIndex: 0 - anchors.right: parent.right anchors.left: parent.left anchors.bottom: parent.bottom @@ -203,6 +252,8 @@ PageType { leftPadding: 96 rightPadding: 96 + height: visible ? homeTabButton.implicitHeight + tabBar.topPadding + tabBar.bottomPadding : 0 + enabled: !root.isControlsDisabled && !root.isTabBarDisabled background: Shape { @@ -219,8 +270,8 @@ PageType { PathLine { x: 0; y: 0 } strokeWidth: 1 - strokeColor: AmneziaStyle.color.greyDark - fillColor: AmneziaStyle.color.blackLight + strokeColor: AmneziaStyle.color.slateGray + fillColor: AmneziaStyle.color.onyxBlack } } @@ -232,7 +283,6 @@ PageType { tabBarStackView.goToTabBarPage(PageEnum.PageHome) ServersModel.processedIndex = ServersModel.defaultIndex tabBar.currentIndex = 0 - tabBar.previousIndex = 0 } KeyNavigation.tab: shareTabButton @@ -261,7 +311,6 @@ PageType { clickedFunc: function () { tabBarStackView.goToTabBarPage(PageEnum.PageShare) tabBar.currentIndex = 1 - tabBar.previousIndex = 1 } KeyNavigation.tab: settingsTabButton @@ -274,7 +323,6 @@ PageType { clickedFunc: function () { tabBarStackView.goToTabBarPage(PageEnum.PageSettings) tabBar.currentIndex = 2 - tabBar.previousIndex = 2 } KeyNavigation.tab: plusTabButton @@ -285,19 +333,11 @@ PageType { isSelected: tabBar.currentIndex === 3 image: "qrc:/images/controls/plus.svg" clickedFunc: function () { - connectionTypeSelection.open() + tabBarStackView.goToTabBarPage(PageEnum.PageSetupWizardConfigSource) + tabBar.currentIndex = 3 } Keys.onTabPressed: PageController.forceStackActiveFocus() } } - - ConnectionTypeSelectionDrawer { - id: connectionTypeSelection - - onAboutToHide: { - PageController.forceTabBarActiveFocus() - tabBar.setCurrentIndex(tabBar.previousIndex) - } - } } diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index ea775d26..a5a47e2c 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -10,6 +10,7 @@ import Style 1.0 import "Config" import "Controls2" import "Components" +import "Pages2" Window { id: root @@ -22,7 +23,7 @@ Window { maximumWidth: 600 maximumHeight: 800 - color: AmneziaStyle.color.black + color: AmneziaStyle.color.midnightBlack onClosing: function() { console.debug("QML onClosing signal") @@ -31,34 +32,9 @@ Window { title: "AmneziaVPN" - StackViewType { - id: rootStackView - - width: root.width - height: root.height - focus: true - - Component.onCompleted: { - var pagePath = PageController.getInitialPage() - rootStackView.push(pagePath, { "objectName" : pagePath }) - } - - Keys.onPressed: function(event) { - PageController.keyPressEvent(event.key) - event.accepted = true - } - } - Connections { target: PageController - function onReplaceStartPage() { - var pagePath = PageController.getInitialPage() - rootStackView.clear() - PageController.updateNavigationBarColor(PageController.getInitialPageNavigationBarColor()) - rootStackView.replace(pagePath, { "objectName" : pagePath }) - } - function onRaiseMainWindow() { root.show() root.raise() @@ -103,6 +79,10 @@ Window { } } + PageStart { + anchors.fill: parent + } + Item { anchors.right: parent.right anchors.left: parent.left @@ -196,10 +176,10 @@ Window { Layout.fillWidth: true defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.blackHovered - pressedColor: AmneziaStyle.color.blackPressed - disabledColor: AmneziaStyle.color.grey - textColor: AmneziaStyle.color.white + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray borderWidth: 1 text: qsTr("Save")