diff --git a/.gitignore b/.gitignore index 26ce16e4..1b4e16b2 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,12 @@ client/3rd/ShadowSocks/build/ # QtCtreator CMake CMakeLists.txt.user* +# Linux files +*.7z +deploy/AppDir +deploy/Tools +deploy/AmneziaVPN*Installer* + # MACOS files .DS_Store client/.DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 6c8fa28b..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,27 +0,0 @@ -variables: - GIT_STRATEGY: clone - -stages: - - build - -build-windows: - stage: build - tags: - - windows - script: - - cmd.exe /k "deploy\windows-env.bat && cd deploy && windows.bat" - artifacts: - name: artifacts-windows - paths: - - AmneziaVPN.exe - -build-macos: - stage: build - tags: - - macos - script: - - cd deploy && ./macos.sh - artifacts: - name: artifacts-macos - paths: - - AmneziaVPN.dmg diff --git a/.gitmodules b/.gitmodules index 008ebf89..a8295ac7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,12 +16,12 @@ [submodule "client/3rd/outline-go-tun2socks"] path = client/3rd/outline-go-tun2socks url = https://github.com/Jigsaw-Code/outline-go-tun2socks.git -[submodule "client/3rd/qzxing"] - path = client/3rd/qzxing - url = https://github.com/ftylitak/qzxing.git [submodule "client/3rd/CocoaAsyncSocket"] path = client/3rd/CocoaAsyncSocket url = https://github.com/robbiehanson/CocoaAsyncSocket.git [submodule "client/3rd/CocoaLumberjack"] path = client/3rd/CocoaLumberjack url = https://github.com/CocoaLumberjack/CocoaLumberjack.git +[submodule "client/3rd/qtkeychain"] + path = client/3rd/qtkeychain + url = https://github.com/frankosterfeld/qtkeychain.git diff --git a/.travis.yml b/.travis.yml index c5022b12..9e16ef5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,10 +23,10 @@ jobs: if [ ! -f $QT_BIN_DIR/qmake ]; then \ brew install p7zip ccache && \ python3 -m pip install --upgrade pip && \ - pip install -U aqtinstall requests py7zr && \ - pip show aqtinstall && \ - aqt install-qt mac desktop $QT_VERSION clang_64 -m all -O $HOME/Qt && \ - aqt install-tool mac desktop tools_ifw -O $HOME/Qt ; \ + python3 -m pip install -U aqtinstall requests py7zr && \ + python3 -m pip show aqtinstall && \ + python3 -m aqt install-qt mac desktop $QT_VERSION clang_64 -m all -O $HOME/Qt && \ + python3 -m aqt install-tool mac desktop tools_ifw -O $HOME/Qt ; \ fi script: @@ -77,6 +77,7 @@ jobs: aqt install-qt windows desktop $QT_VERSION win64_msvc2019_64 -m all -O /C/Qt && \ aqt install-tool windows desktop tools_ifw -O /C/Qt ; \ fi + - choco install ccache script: - echo set BUILD_ARCH=$BUILD_ARCH > winbuild.bat @@ -86,7 +87,10 @@ jobs: - echo call \""%MSVC_PATH_WIN%\\Common7\\Tools\\VsDevCmd.bat\" -arch=amd64" >> winbuild.bat - echo call deploy\\build_windows.bat >> winbuild.bat - cmd //c winbuild.bat - + + after_script: + - ccache --show-stats + deploy: provider: releases token: $GH_TOKEN @@ -128,6 +132,7 @@ jobs: aqt install-qt windows desktop $QT_VERSION win32_msvc2019 -m all -O /C/Qt && \ aqt install-tool windows desktop tools_ifw -O /C/Qt ; \ fi + - choco install ccache script: - echo set BUILD_ARCH=$BUILD_ARCH > winbuild.bat @@ -138,6 +143,9 @@ jobs: - echo call deploy\\build_windows.bat >> winbuild.bat - cmd //c winbuild.bat + after_script: + - ccache --show-stats + deploy: provider: releases token: $GH_TOKEN @@ -156,17 +164,21 @@ jobs: # ------------------------------------------------------ - name: Linux os: linux + language: cpp dist: focal addons: apt: packages: - p7zip + - p7zip-full - python3 - python3-pip - libgl-dev - mesa-common-dev - libpulse-dev + - libxcb-* + - libxkbcommon-x11-0 env: - QT_VERSION=5.15.2 @@ -205,12 +217,30 @@ jobs: - ccache - directories: - $HOME/Qt + - $HOME/.ccache # ------------------------------------------------------ - name: Android os: linux - language: android - dist: xenial + language: cpp + dist: focal + env: + - QT_VERSION=5.15.2 + - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin + - ANDROID_API_VERSION=android-21 + - ANDROID_HOME=$HOME/sdk + - ANDROID_SDK_ROOT=$ANDROID_HOME + - LOCAL_ANDROID_HOME=$ANDROID_HOME + - LOCAL_ANDROID_SDK_ROOT=$ANDROID_HOME + - NDK_VERSION=21d + - ANDROID_NDK_PLATFORM=android-21 + - ANDROID_NDK_HOME=$HOME/android-ndk-r${NDK_VERSION} + - ANDROID_NDK_ROOT=$ANDROID_NDK_HOME + - ANDROID_NDK_HOST=linux-x86_64 + - LOCAL_ANDROID_NDK_HOME=$ANDROID_NDK_HOME + - LOCAL_ANDROID_NDK_HOST_PLATFORM=$ANDROID_NDK_HOST + - JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 + - TERM=dumb addons: apt: @@ -218,53 +248,30 @@ jobs: - p7zip - python3 - python3-pip - - android: - components: - # Uncomment the lines below if you want to - # use the latest revision of Android SDK Tools - # - tools - # - platform-tools - - # The BuildTools version used by your project - - build-tools-30.0.2 - - # The SDK version used to compile your project - - android-30 - - # Additional components - - extra - - extra-google-google_play_services - - extra-google-m2repository - - extra-android-m2repository - - env: - - QT_VERSION=5.15.2 - - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin - - USE_ANDROID_NDK_VERSION=21d - - ANDROID_NDK_HOME=$HOME/NDK + - openjdk-8-jdk install: - | if [ ! -f $QT_BIN_DIR/qmake ]; then \ - export PATH=$HOME/.local/bin:$PATH && \ python3 -m pip install -U aqtinstall requests py7zr && \ python3 -m pip show aqtinstall && \ - python3 -m aqt install-qt linux android $QT_VERSION android_armv7 -m all -O $HOME/Qt && \ - python3 -m aqt install-qt linux android $QT_VERSION android_arm64_v8a -m all -O $HOME/Qt && \ - python3 -m aqt install-qt linux android $QT_VERSION android_x86_64 -m all -O $HOME/Qt && \ - python3 -m aqt install-qt linux android $QT_VERSION android_x86 -m all -O $HOME/Qt ; \ + python3 -m aqt install-qt linux android $QT_VERSION -m all -O $HOME/Qt ; \ + fi + + - | + if [ ! -f $ANDROID_SDK_ROOT/tools/bin/sdkmanager ]; then \ + echo "Download Android SDK" && \ + wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -qO $HOME/sdk.zip > /dev/null && \ + unzip -q -d $ANDROID_SDK_ROOT $HOME/sdk.zip && \ + echo "Download tools" && \ + yes | $ANDROID_SDK_ROOT/tools/bin/sdkmanager --licenses > /dev/null 2>&1 && \ + $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" > /dev/null 2>&1 || exit 1 ; \ fi - | - export TERM=dumb && - curl -L https://dl.google.com/android/repository/android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip -O && - unzip ./android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip > /dev/null && - rm android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip && - export ANDROID_NDK_HOME=`pwd`/android-ndk-r${USE_ANDROID_NDK_VERSION} && - export LOCAL_ANDROID_NDK_HOME="$ANDROID_NDK_HOME" && - export LOCAL_ANDROID_NDK_HOST_PLATFORM="linux-x86_64" && - export PATH=$PATH:${ANDROID_NDK_HOME} && - env + if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then \ + wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux-x86_64.zip -qO $HOME/ndk.zip && + unzip -q -d $HOME $HOME/ndk.zip ; \ + fi script: - bash deploy/build_android.sh @@ -290,6 +297,7 @@ jobs: - $HOME/.gradle/wrapper/ - $HOME/.android/build-cache - $ANDROID_NDK_HOME + - $ANDROID_SDK_ROOT # ------------------------------------------------------ - name: iOS @@ -308,9 +316,9 @@ jobs: if [ ! -f $QT_BIN_DIR/qmake ]; then \ brew install p7zip ccache && \ python3 -m pip install --upgrade pip && \ - pip install -U aqtinstall requests py7zr && \ - pip show aqtinstall && \ - aqt install-qt mac ios $QT_VERSION -m all -O $HOME/Qt ; \ + python3 -m pip install -U aqtinstall requests py7zr && \ + python3 -m pip show aqtinstall && \ + python3 -m aqt install-qt mac ios $QT_VERSION -m all -O $HOME/Qt ; \ fi - brew install golang - go install golang.org/x/mobile/cmd/gomobile@latest diff --git a/client/3rd/QSimpleCrypto/QSimpleCrypto.pri b/client/3rd/QSimpleCrypto/QSimpleCrypto.pri new file mode 100644 index 00000000..99a1c129 --- /dev/null +++ b/client/3rd/QSimpleCrypto/QSimpleCrypto.pri @@ -0,0 +1,18 @@ +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 new file mode 100644 index 00000000..11f60b31 --- /dev/null +++ b/client/3rd/QSimpleCrypto/include/QAead.h @@ -0,0 +1,87 @@ +/** + * 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 new file mode 100644 index 00000000..e7b83a88 --- /dev/null +++ b/client/3rd/QSimpleCrypto/include/QBlockCipher.h @@ -0,0 +1,84 @@ +/** + * 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 new file mode 100644 index 00000000..fc059654 --- /dev/null +++ b/client/3rd/QSimpleCrypto/include/QCryptoError.h @@ -0,0 +1,45 @@ +#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 new file mode 100644 index 00000000..45eb3169 --- /dev/null +++ b/client/3rd/QSimpleCrypto/include/QRsa.h @@ -0,0 +1,104 @@ +/** + * 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 new file mode 100644 index 00000000..fdd6c020 --- /dev/null +++ b/client/3rd/QSimpleCrypto/include/QSimpleCrypto_global.h @@ -0,0 +1,9 @@ +#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 new file mode 100644 index 00000000..96e0c76c --- /dev/null +++ b/client/3rd/QSimpleCrypto/include/QX509.h @@ -0,0 +1,87 @@ +/** + * 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(const 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 new file mode 100644 index 00000000..8cd8ca82 --- /dev/null +++ b/client/3rd/QSimpleCrypto/include/QX509Store.h @@ -0,0 +1,120 @@ +/** + * 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 new file mode 100644 index 00000000..968c8841 --- /dev/null +++ b/client/3rd/QSimpleCrypto/sources/QAead.cpp @@ -0,0 +1,364 @@ +/** + * 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 new file mode 100644 index 00000000..8b86ab98 --- /dev/null +++ b/client/3rd/QSimpleCrypto/sources/QBlockCipher.cpp @@ -0,0 +1,193 @@ +/** + * 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 new file mode 100644 index 00000000..234f55d7 --- /dev/null +++ b/client/3rd/QSimpleCrypto/sources/QCryptoError.cpp @@ -0,0 +1,6 @@ +#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 new file mode 100644 index 00000000..544d6746 --- /dev/null +++ b/client/3rd/QSimpleCrypto/sources/QRsa.cpp @@ -0,0 +1,274 @@ +/** + * 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 new file mode 100644 index 00000000..8666347d --- /dev/null +++ b/client/3rd/QSimpleCrypto/sources/QX509.cpp @@ -0,0 +1,234 @@ +/** + * 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(const 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 new file mode 100644 index 00000000..bbbec1a8 --- /dev/null +++ b/client/3rd/QSimpleCrypto/sources/QX509Store.cpp @@ -0,0 +1,176 @@ +/** + * 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/3rd/qrcodegen/qrcodegen.cpp b/client/3rd/qrcodegen/qrcodegen.cpp new file mode 100644 index 00000000..21fffba9 --- /dev/null +++ b/client/3rd/qrcodegen/qrcodegen.cpp @@ -0,0 +1,856 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "qrcodegen.hpp" + +using std::int8_t; +using std::uint8_t; +using std::size_t; +using std::vector; + + +namespace qrcodegen { + +/*---- Class QrSegment ----*/ + +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + return numBitsCharCount[(ver + 7) / 17]; +} + + +const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); + + +QrSegment QrSegment::makeBytes(const vector &data) { + if (data.size() > static_cast(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) : + mode(&md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) : + mode(&md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +int QrSegment::getTotalBits(const vector &segs, int version) { + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode->numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast(seg.data.size()); + } + return result; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +const QrSegment::Mode &QrSegment::getMode() const { + return *mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + +/*---- Class QrCode ----*/ + +int QrCode::getFormatBits(Ecc ecl) { + switch (ecl) { + case Ecc::LOW : return 1; + case Ecc::MEDIUM : return 0; + case Ecc::QUARTILE: return 3; + case Ecc::HIGH : return 2; + default: throw std::logic_error("Unreachable"); + } +} + + +QrCode QrCode::encodeText(const char *text, Ecc ecl) { + vector segs = QrSegment::makeSegments(text); + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) { + vector segs{QrSegment::makeBytes(data)}; + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl) { + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw std::invalid_argument("Invalid value"); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + std::ostringstream sb; + if (dataUsedBits == -1) + sb << "Segment too long"; + else { + sb << "Data length = " << dataUsedBits << " bits, "; + sb << "Max capacity = " << dataCapacityBits << " bits"; + } + throw data_too_long(sb.str()); + } + } + assert(dataUsedBits != -1); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Concatenate all segments to create the data bit string + BitBuffer bb; + for (const QrSegment &seg : segs) { + bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); + bb.appendBits(static_cast(seg.getNumChars()), seg.getMode().numCharCountBits(version)); + bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); + } + assert(bb.size() == static_cast(dataUsedBits)); + + // Add terminator and pad up to a byte if applicable + size_t dataCapacityBits = static_cast(getNumDataCodewords(version, ecl)) * 8; + assert(bb.size() <= dataCapacityBits); + bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); + bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); + assert(bb.size() % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + + // Pack bits into bytes in big endian + vector dataCodewords(bb.size() / 8); + for (size_t i = 0; i < bb.size(); i++) + dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + + // Create the QR Code object + return QrCode(version, ecl, dataCodewords, mask); +} + + +QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) : + // Initialize fields and check arguments + version(ver), + errorCorrectionLevel(ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version value out of range"); + if (msk < -1 || msk > 7) + throw std::domain_error("Mask value out of range"); + size = ver * 4 + 17; + size_t sz = static_cast(size); + modules = vector >(sz, vector(sz)); // Initially all light + isFunction = vector >(sz, vector(sz)); + + // Compute ECC, draw modules + drawFunctionPatterns(); + const vector allCodewords = addEccAndInterleave(dataCodewords); + drawCodewords(allCodewords); + + // Do masking + if (msk == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + assert(0 <= msk && msk <= 7); + mask = msk; + applyMask(msk); // Apply the final choice of mask + drawFormatBits(msk); // Overwrite old format bits + + isFunction.clear(); + isFunction.shrink_to_fit(); +} + + +int QrCode::getVersion() const { + return version; +} + + +int QrCode::getSize() const { + return size; +} + + +QrCode::Ecc QrCode::getErrorCorrectionLevel() const { + return errorCorrectionLevel; +} + + +int QrCode::getMask() const { + return mask; +} + + +bool QrCode::getModule(int x, int y) const { + return 0 <= x && x < size && 0 <= y && y < size && module(x, y); +} + + +void QrCode::drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + const vector alignPatPos = getAlignmentPatternPositions(); + size_t numAlign = alignPatPos.size(); + for (size_t i = 0; i < numAlign; i++) { + for (size_t j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + + +void QrCode::drawFormatBits(int msk) { + // Calculate error correction code and pack bits + int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, getBit(bits, i)); + + // Draw second copy + for (int i = 0; i < 8; i++) + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, getBit(bits, i)); + setFunctionModule(8, size - 8, true); // Always dark +} + + +void QrCode::drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = static_cast(version) << 12 | rem; // uint18 + assert(bits >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = getBit(bits, i); + int a = size - 11 + i % 3; + int b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + + +void QrCode::drawFinderPattern(int x, int y) { + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + + +void QrCode::drawAlignmentPattern(int x, int y) { + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); + } +} + + +void QrCode::setFunctionModule(int x, int y, bool isDark) { + size_t ux = static_cast(x); + size_t uy = static_cast(y); + modules .at(uy).at(ux) = isDark; + isFunction.at(uy).at(ux) = true; +} + + +bool QrCode::module(int x, int y) const { + return modules.at(static_cast(y)).at(static_cast(x)); +} + + +vector QrCode::addEccAndInterleave(const vector &data) const { + if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) + throw std::invalid_argument("Invalid argument"); + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast(errorCorrectionLevel)][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + vector > blocks; + const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + vector dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); + k += static_cast(dat.size()); + const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); + blocks.push_back(std::move(dat)); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + vector result; + for (size_t i = 0; i < blocks.at(0).size(); i++) { + for (size_t j = 0; j < blocks.size(); j++) { + // Skip the padding byte in short blocks + if (i != static_cast(shortBlockLen - blockEccLen) || j >= static_cast(numShortBlocks)) + result.push_back(blocks.at(j).at(i)); + } + } + assert(result.size() == static_cast(rawCodewords)); + return result; +} + + +void QrCode::drawCodewords(const vector &data) { + if (data.size() != static_cast(getNumRawDataModules(version) / 8)) + throw std::invalid_argument("Invalid argument"); + + size_t i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + size_t x = static_cast(right - j); // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + size_t y = static_cast(upward ? size - 1 - vert : vert); // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/light by the constructor and are left unchanged by this method + } + } + } + assert(i == data.size() * 8); +} + + +void QrCode::applyMask(int msk) { + if (msk < 0 || msk > 7) + throw std::domain_error("Mask value out of range"); + size_t sz = static_cast(size); + for (size_t y = 0; y < sz; y++) { + for (size_t x = 0; x < sz; x++) { + bool invert; + switch (msk) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw std::logic_error("Unreachable"); + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + + +long QrCode::getPenaltyScore() const { + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < size; y++) { + bool runColor = false; + int runX = 0; + std::array runHistory = {}; + for (int x = 0; x < size; x++) { + if (module(x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < size; x++) { + bool runColor = false; + int runY = 0; + std::array runHistory = {}; + for (int y = 0; y < size; y++) { + if (module(x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = module(x, y); + if ( color == module(x + 1, y) && + color == module(x, y + 1) && + color == module(x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of dark and light modules + int dark = 0; + for (const vector &row : modules) { + for (bool color : row) { + if (color) + dark++; + } + } + int total = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = static_cast((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1; + assert(0 <= k && k <= 9); + result += k * PENALTY_N4; + assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 + return result; +} + + +vector QrCode::getAlignmentPatternPositions() const { + if (version == 1) + return vector(); + else { + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; + vector result; + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); + return result; + } +} + + +int QrCode::getNumRawDataModules(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version number out of range"); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + assert(208 <= result && result <= 29648); + return result; +} + + +int QrCode::getNumDataCodewords(int ver, Ecc ecl) { + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK [static_cast(ecl)][ver] + * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; +} + + +vector QrCode::reedSolomonComputeDivisor(int degree) { + if (degree < 1 || degree > 255) + throw std::domain_error("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + vector result(static_cast(degree)); + result.at(result.size() - 1) = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // and drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < result.size(); j++) { + result.at(j) = reedSolomonMultiply(result.at(j), root); + if (j + 1 < result.size()) + result.at(j) ^= result.at(j + 1); + } + root = reedSolomonMultiply(root, 0x02); + } + return result; +} + + +vector QrCode::reedSolomonComputeRemainder(const vector &data, const vector &divisor) { + vector result(divisor.size()); + for (uint8_t b : data) { // Polynomial division + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t i = 0; i < result.size(); i++) + result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); + } + return result; +} + + +uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + assert(z >> 8 == 0); + return static_cast(z); +} + + +int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const { + int n = runHistory.at(1); + assert(n <= size * 3); + bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; + return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) + + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); +} + + +int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { + if (currentRunColor) { // Terminate dark run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add light border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); +} + + +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { + if (runHistory.at(0) == 0) + currentRunLength += size; // Add light border to initial run + std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); + runHistory.at(0) = currentRunLength; +} + + +bool QrCode::getBit(long x, int i) { + return ((x >> i) & 1) != 0; +} + + +/*---- Tables of constants ----*/ + +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N3 = 40; +const int QrCode::PENALTY_N4 = 10; + + +const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + + +data_too_long::data_too_long(const std::string &msg) : + std::length_error(msg) {} + + + +/*---- Class BitBuffer ----*/ + +BitBuffer::BitBuffer() + : std::vector() {} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + +std::string toSvgString(const QrCode &qr, int border) { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - qr.getSize()) + throw std::overflow_error("Border too large"); + + std::ostringstream sb; + sb << "\n"; + sb << "\n"; + sb << "\n"; + sb << "\t\n"; + sb << "\t\n"; + sb << "\n"; + return sb.str(); +} +} diff --git a/client/3rd/qrcodegen/qrcodegen.hpp b/client/3rd/qrcodegen/qrcodegen.hpp new file mode 100644 index 00000000..f355edc7 --- /dev/null +++ b/client/3rd/qrcodegen/qrcodegen.hpp @@ -0,0 +1,551 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + + +namespace qrcodegen { + +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: int modeBits; + + // Number of character count bits for three different version ranges. + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the character count field for a segment in + * this mode in a QR Code at the given version number. The result is in the range [0, 16]. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ + public: static QrSegment makeBytes(const std::vector &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static QrSegment makeAlphanumeric(const char *text); + + + /* + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. + */ + public: static std::vector makeSegments(const char *text); + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ + public: static bool isNumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static bool isAlphanumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ + private: const Mode *mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ + private: int numChars; + + /* The data bits of this segment. Accessed through getData(). */ + private: std::vector data; + + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ + public: QrSegment(const Mode &md, int numCh, const std::vector &dt); + + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ + public: QrSegment(const Mode &md, int numCh, std::vector &&dt); + + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ + public: const Mode &getMode() const; + + + /* + * Returns the character count field of this segment. + */ + public: int getNumChars() const; + + + /* + * Returns the data bits of this segment. + */ + public: const std::vector &getData() const; + + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. + public: static int getTotalBits(const std::vector &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + + + +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of dark and light cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ +class QrCode final { + + /*---- Public helper enumeration ----*/ + + /* + * The error correction level in a QR Code symbol. + */ + public: enum class Ecc { + LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + HIGH , // The QR Code can tolerate about 30% erroneous codewords + }; + + + // Returns a value in the range 0 to 3 (unsigned 2-bit integer). + private: static int getFormatBits(Ecc ecl); + + + + /*---- Static factory functions (high level) ----*/ + + /* + * Returns a QR Code representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer + * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + * the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeText(const char *text, Ecc ecl); + + + /* + * Returns a QR Code representing the given binary data at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeBinary(const std::vector &data, Ecc ecl); + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a QR Code representing the given segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + */ + public: static QrCode encodeSegments(const std::vector &segs, Ecc ecl, + int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters + + + + /*---- Instance fields ----*/ + + // Immutable scalar parameters: + + /* The version number of this QR Code, which is between 1 and 40 (inclusive). + * This determines the size of this barcode. */ + private: int version; + + /* The width and height of this QR Code, measured in modules, between + * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ + private: int size; + + /* The error correction level used in this QR Code. */ + private: Ecc errorCorrectionLevel; + + /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). + * Even if a QR Code is created with automatic masking requested (mask = -1), + * the resulting object still has a mask value between 0 and 7. */ + private: int mask; + + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code (false = light, true = dark). + // Immutable after constructor finishes. Accessed through getModule(). + private: std::vector > modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private: std::vector > isFunction; + + + + /*---- Constructor (low level) ----*/ + + /* + * Creates a new QR Code with the given version number, + * error correction level, data codeword bytes, and mask number. + * This is a low-level API that most users should not use directly. + * A mid-level API is the encodeSegments() function. + */ + public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int msk); + + + + /*---- Public instance methods ----*/ + + /* + * Returns this QR Code's version, in the range [1, 40]. + */ + public: int getVersion() const; + + + /* + * Returns this QR Code's size, in the range [21, 177]. + */ + public: int getSize() const; + + + /* + * Returns this QR Code's error correction level. + */ + public: Ecc getErrorCorrectionLevel() const; + + + /* + * Returns this QR Code's mask, in the range [0, 7]. + */ + public: int getMask() const; + + + /* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. + */ + public: bool getModule(int x, int y) const; + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + // Reads this object's version field, and draws and marks all function modules. + private: void drawFunctionPatterns(); + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + private: void drawFormatBits(int msk); + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field, iff 7 <= version <= 40. + private: void drawVersion(); + + + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. + private: void drawFinderPattern(int x, int y); + + + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. + private: void drawAlignmentPattern(int x, int y); + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in bounds. + private: void setFunctionModule(int x, int y, bool isDark); + + + // Returns the color of the module at the given coordinates, which must be in range. + private: bool module(int x, int y) const; + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + private: std::vector addEccAndInterleave(const std::vector &data) const; + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code. Function modules need to be marked off before this is called. + private: void drawCodewords(const std::vector &data); + + + // XORs the codeword modules in this QR Code with the given mask pattern. + // The function modules must be marked and the codeword bits must be drawn + // before masking. Due to the arithmetic of XOR, calling applyMask() with + // the same mask value a second time will undo the mask. A final well-formed + // QR Code needs exactly one (not zero, two, etc.) mask applied. + private: void applyMask(int msk); + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + private: long getPenaltyScore() const; + + + + /*---- Private helper functions ----*/ + + // Returns an ascending list of positions of alignment patterns for this version number. + // Each position is in the range [0,177), and are used on both the x and y axes. + // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. + private: std::vector getAlignmentPatternPositions() const; + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + private: static int getNumRawDataModules(int ver); + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + private: static int getNumDataCodewords(int ver, Ecc ecl); + + + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be + // implemented as a lookup table over all possible parameter values, instead of as an algorithm. + private: static std::vector reedSolomonComputeDivisor(int degree); + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + private: static std::vector reedSolomonComputeRemainder(const std::vector &data, const std::vector &divisor); + + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. + private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); + + + // Can only be called immediately after a light run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + private: int finderPenaltyCountPatterns(const std::array &runHistory) const; + + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const; + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private: void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const; + + + // Returns true iff the i'th bit of x is set to 1. + private: static bool getBit(long x, int i); + + + /*---- Constants and tables ----*/ + + // The minimum version number supported in the QR Code Model 2 standard. + public: static constexpr int MIN_VERSION = 1; + + // The maximum version number supported in the QR Code Model 2 standard. + public: static constexpr int MAX_VERSION = 40; + + + // For use in getPenaltyScore(), when evaluating which mask is best. + private: static const int PENALTY_N1; + private: static const int PENALTY_N2; + private: static const int PENALTY_N3; + private: static const int PENALTY_N4; + + + private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; + private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; + +}; + + + +/*---- Public exception class ----*/ + +/* + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + * - Decrease the error correction level if it was greater than Ecc::LOW. + * - If the encodeSegments() function was called with a maxVersion argument, then increase + * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other + * factory functions because they search all versions up to QrCode::MAX_VERSION.) + * - Split the text data into better or optimal segments in order to reduce the number of bits required. + * - Change the text or binary data to be shorter. + * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + * - Propagate the error upward to the caller/user. + */ +class data_too_long : public std::length_error { + + public: explicit data_too_long(const std::string &msg); + +}; + + + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + +std::string toSvgString(const QrCode &qr, int border); + +} diff --git a/client/3rd/qrcodegen/qrcodegen.pri b/client/3rd/qrcodegen/qrcodegen.pri new file mode 100644 index 00000000..da50cb98 --- /dev/null +++ b/client/3rd/qrcodegen/qrcodegen.pri @@ -0,0 +1,5 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +HEADERS += $$PWD/qrcodegen.hpp +SOURCES += $$PWD/qrcodegen.cpp diff --git a/client/3rd/qtkeychain b/client/3rd/qtkeychain new file mode 160000 index 00000000..f197cdb9 --- /dev/null +++ b/client/3rd/qtkeychain @@ -0,0 +1 @@ +Subproject commit f197cdb935b0cfd9881fdc6860874cb8379d1238 diff --git a/client/3rd/qzxing b/client/3rd/qzxing deleted file mode 160000 index 2fd4dd60..00000000 --- a/client/3rd/qzxing +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2fd4dd60c04a29c6d1271fdd9ae25378b8f61ec8 diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp new file mode 100644 index 00000000..334582cb --- /dev/null +++ b/client/amnezia_application.cpp @@ -0,0 +1,220 @@ +#include "amnezia_application.h" + +#include +#include +#include +#include + + +#include "core/servercontroller.h" +#include "debug.h" +#include "defines.h" + +#include "platforms/ios/QRCodeReaderBase.h" + +#include "ui/pages.h" + +#include "ui/pages_logic/AppSettingsLogic.h" +#include "ui/pages_logic/GeneralSettingsLogic.h" +#include "ui/pages_logic/NetworkSettingsLogic.h" +#include "ui/pages_logic/NewServerProtocolsLogic.h" +#include "ui/pages_logic/QrDecoderLogic.h" +#include "ui/pages_logic/ServerConfiguringProgressLogic.h" +#include "ui/pages_logic/ServerContainersLogic.h" +#include "ui/pages_logic/ServerListLogic.h" +#include "ui/pages_logic/ServerSettingsLogic.h" +#include "ui/pages_logic/ServerContainersLogic.h" +#include "ui/pages_logic/ShareConnectionLogic.h" +#include "ui/pages_logic/SitesLogic.h" +#include "ui/pages_logic/StartPageLogic.h" +#include "ui/pages_logic/VpnLogic.h" +#include "ui/pages_logic/WizardLogic.h" + +#include "ui/pages_logic/protocols/CloakLogic.h" +#include "ui/pages_logic/protocols/OpenVpnLogic.h" +#include "ui/pages_logic/protocols/ShadowSocksLogic.h" + +#if defined(Q_OS_IOS) +#include "platforms/ios/QtAppDelegate-C-Interface.h" +#endif + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + AmneziaApplication::AmneziaApplication(int &argc, char *argv[]): + AMNEZIA_BASE_CLASS(argc, argv) +#else + AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, + SingleApplication::Options options, int timeout, const QString &userData): + SingleApplication(argc, argv, allowSecondary, options, timeout, userData) +#endif +{ + setQuitOnLastWindowClosed(false); + + // Fix config file permissions +#ifdef Q_OS_LINUX + { + QSettings s(ORGANIZATION_NAME, APPLICATION_NAME); + s.setValue("permFixed", true); + } + + QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + + ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf"; + QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner); + + QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + + ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf"; + QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner); + +#endif + + m_settings = std::shared_ptr(new Settings); + m_serverController = std::shared_ptr(new ServerController(m_settings, this)); + m_configurator = std::shared_ptr(new VpnConfigurator(m_settings, m_serverController, this)); +} + +AmneziaApplication::~AmneziaApplication() +{ + if (m_engine) { + QObject::disconnect(m_engine, 0,0,0); + delete m_engine; + } + if (m_uiLogic) { + QObject::disconnect(m_uiLogic, 0,0,0); + delete m_uiLogic; + } + + if (m_protocolProps) delete m_protocolProps; + if (m_containerProps) delete m_containerProps; +} + +void AmneziaApplication::init() +{ + m_engine = new QQmlApplicationEngine; + m_uiLogic = new UiLogic(m_settings, m_configurator, m_serverController); + + const QUrl url(QStringLiteral("qrc:/ui/qml/main.qml")); + QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated, + this, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + + m_engine->rootContext()->setContextProperty("Debug", &Debug::Instance()); + m_uiLogic->registerPagesLogic(); + +#if defined(Q_OS_IOS) + setStartPageLogic(m_uiLogic->pageLogic()); +#endif + + m_engine->load(url); + + if (m_engine->rootObjects().size() > 0) { + m_uiLogic->setQmlRoot(m_engine->rootObjects().at(0)); + } + + if (m_settings->isSaveLogs()) { + if (!Debug::init()) { + qWarning() << "Initialization of debug subsystem failed"; + } + } + +#ifdef Q_OS_WIN + if (m_parser.isSet("a")) m_uiLogic->showOnStartup(); + else emit m_uiLogic->show(); +#else + m_uiLogic->showOnStartup(); +#endif + + // TODO - fix +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (isPrimary()) { + QObject::connect(this, &SingleApplication::instanceStarted, m_uiLogic, [this](){ + qDebug() << "Secondary instance started, showing this window instead"; + emit m_uiLogic->show(); + emit m_uiLogic->raise(); + }); + } +#endif + +} + +void AmneziaApplication::registerTypes() +{ + qRegisterMetaType("VpnProtocol::VpnConnectionState"); + qRegisterMetaType("ServerCredentials"); + + qRegisterMetaType("DockerContainer"); + qRegisterMetaType("TransportProto"); + qRegisterMetaType("Proto"); + qRegisterMetaType("ServiceType"); + qRegisterMetaType("Page"); + qRegisterMetaType("ConnectionState"); + + qRegisterMetaType("PageProtocolLogicBase *"); + + + declareQmlPageEnum(); + declareQmlProtocolEnum(); + declareQmlContainerEnum(); + + qmlRegisterType("PageType", 1, 0, "PageType"); + qmlRegisterType("QRCodeReader", 1, 0, "QRCodeReader"); + + m_containerProps = new ContainerProps; + qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps); + + m_protocolProps = new ProtocolProps; + qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps); +} + +void AmneziaApplication::loadFonts() +{ + QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-BlackItalic.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-Bold.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-BoldItalic.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-Italic.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-Light.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-LightItalic.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-Regular.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-Thin.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Lato-ThinItalic.ttf"); +} + +void AmneziaApplication::loadTranslator() +{ + m_translator = new QTranslator; + if (m_translator->load(QLocale(), QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/translations"))) { + installTranslator(m_translator); + } +} + +bool AmneziaApplication::parseCommands() +{ + m_parser.setApplicationDescription(APPLICATION_NAME); + m_parser.addHelpOption(); + m_parser.addVersionOption(); + + QCommandLineOption c_autostart {{"a", "autostart"}, "System autostart"}; + m_parser.addOption(c_autostart); + + QCommandLineOption c_cleanup {{"c", "cleanup"}, "Cleanup logs"}; + m_parser.addOption(c_cleanup); + + m_parser.process(*this); + + if (m_parser.isSet(c_cleanup)) { + Debug::cleanUp(); + QTimer::singleShot(100, this, [this]{ + quit(); + }); + exec(); + return false; + } + return true; +} + +QQmlApplicationEngine *AmneziaApplication::qmlEngine() const +{ + return m_engine; +} + diff --git a/client/amnezia_application.h b/client/amnezia_application.h new file mode 100644 index 00000000..f0f8f529 --- /dev/null +++ b/client/amnezia_application.h @@ -0,0 +1,62 @@ +#ifndef AMNEZIA_APPLICATION_H +#define AMNEZIA_APPLICATION_H + +#include +#include + +#include +#include +#include + +#include "settings.h" + +#include "ui/uilogic.h" +#include "configurators/vpn_configurator.h" + +#define amnApp (static_cast(QCoreApplication::instance())) + + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + #define AMNEZIA_BASE_CLASS QApplication +#else + #define AMNEZIA_BASE_CLASS SingleApplication + #define QAPPLICATION_CLASS QApplication + #include "singleapplication.h" +#endif + +class AmneziaApplication : public AMNEZIA_BASE_CLASS +{ + Q_OBJECT +public: +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + AmneziaApplication(int &argc, char *argv[]); +#else + AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false, + SingleApplication::Options options = SingleApplication::User, int timeout = 1000, const QString &userData = {} ); +#endif + virtual ~AmneziaApplication(); + + void init(); + void registerTypes(); + void loadFonts(); + void loadTranslator(); + bool parseCommands(); + + QQmlApplicationEngine *qmlEngine() const; + +private: + QQmlApplicationEngine *m_engine {}; + UiLogic *m_uiLogic {}; + std::shared_ptr m_settings; + std::shared_ptr m_configurator; + std::shared_ptr m_serverController; + + ContainerProps* m_containerProps {}; + ProtocolProps* m_protocolProps {}; + + QTranslator* m_translator; + QCommandLineParser m_parser; + +}; + +#endif // AMNEZIA_APPLICATION_H diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 1c112d2f..cb70759d 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -19,7 +19,7 @@ - + diff --git a/client/android/src/org/amnezia/vpn/AuthHelper.java b/client/android/src/org/amnezia/vpn/AuthHelper.java new file mode 100644 index 00000000..b30aa8f5 --- /dev/null +++ b/client/android/src/org/amnezia/vpn/AuthHelper.java @@ -0,0 +1,24 @@ +package org.amnezia.vpn; + +import android.content.Context; +import android.app.KeyguardManager; +import android.content.Intent; +import org.qtproject.qt5.android.bindings.QtActivity; + + +import static android.content.Context.KEYGUARD_SERVICE; + +public class AuthHelper extends QtActivity { + + static final String TAG = "AuthHelper"; + + public static Intent getAuthIntent(Context context) { + KeyguardManager mKeyguardManager = (KeyguardManager)context.getSystemService(KEYGUARD_SERVICE); + if (mKeyguardManager.isDeviceSecure()) { + return mKeyguardManager.createConfirmDeviceCredentialIntent(null, null); + } else { + return null; + } + } + +} diff --git a/client/client.pro b/client/client.pro index e0e59a0f..1ca71152 100644 --- a/client/client.pro +++ b/client/client.pro @@ -3,47 +3,53 @@ equals(QT_MAJOR_VERSION, 6): QT += core5compat TARGET = AmneziaVPN TEMPLATE = app -#CONFIG += console + +# silent builds on CI env +IS_CI=$$(CI) +!isEmpty(IS_CI){ + message("Detected CI env") + CONFIG += silent ccache +} CONFIG += qtquickcompiler -CONFIG += qzxing_multimedia \ - enable_decoder_qr_code \ - enable_encoder_qr_code - -DEFINES += QT_DEPRECATED_WARNINGS include("3rd/QtSsh/src/ssh/qssh.pri") include("3rd/QtSsh/src/botan/botan.pri") !android:!ios:include("3rd/SingleApplication/singleapplication.pri") include ("3rd/SortFilterProxyModel/SortFilterProxyModel.pri") -win32-msvc*: DEFINES += __STDC_CONSTANT_MACROS -include("3rd/qzxing/src/QZXing-components.pri") +include("3rd/qrcodegen/qrcodegen.pri") +include("3rd/QSimpleCrypto/QSimpleCrypto.pri") +include("3rd/qtkeychain/qtkeychain.pri") +INCLUDEPATH += $$PWD/3rd/QSimpleCrypto/include INCLUDEPATH += $$PWD/3rd/OpenSSL/include DEPENDPATH += $$PWD/3rd/OpenSSL/include HEADERS += \ ../ipc/ipc.h \ - configurators/cloak_configurator.h \ - configurators/ikev2_configurator.h \ - configurators/shadowsocks_configurator.h \ - configurators/ssh_configurator.h \ - configurators/vpn_configurator.h \ - configurators/wireguard_configurator.h \ + amnezia_application.h \ + configurators/cloak_configurator.h \ + configurators/configurator_base.h \ + configurators/ikev2_configurator.h \ + configurators/shadowsocks_configurator.h \ + configurators/ssh_configurator.h \ + configurators/vpn_configurator.h \ + configurators/wireguard_configurator.h \ containers/containers_defs.h \ core/defs.h \ core/errorstrings.h \ configurators/openvpn_configurator.h \ - core/scripts_registry.h \ - core/server_defs.h \ + core/scripts_registry.h \ + core/server_defs.h \ core/servercontroller.h \ debug.h \ defines.h \ managementserver.h \ platforms/ios/MobileUtils.h \ platforms/linux/leakdetector.h \ - protocols/protocols_defs.h \ + protocols/protocols_defs.h \ + secure_qsettings.h \ settings.h \ ui/notificationhandler.h \ ui/models/containers_model.h \ @@ -52,28 +58,29 @@ HEADERS += \ ui/pages_logic/AppSettingsLogic.h \ ui/pages_logic/GeneralSettingsLogic.h \ ui/pages_logic/NetworkSettingsLogic.h \ - ui/pages_logic/NewServerProtocolsLogic.h \ + ui/pages_logic/NewServerProtocolsLogic.h \ ui/pages_logic/PageLogicBase.h \ - ui/pages_logic/QrDecoderLogic.h \ - ui/pages_logic/ServerConfiguringProgressLogic.h \ + ui/pages_logic/QrDecoderLogic.h \ + ui/pages_logic/ServerConfiguringProgressLogic.h \ ui/pages_logic/ServerContainersLogic.h \ ui/pages_logic/ServerListLogic.h \ ui/pages_logic/ServerSettingsLogic.h \ ui/pages_logic/ShareConnectionLogic.h \ ui/pages_logic/SitesLogic.h \ ui/pages_logic/StartPageLogic.h \ + ui/pages_logic/ViewConfigLogic.h \ ui/pages_logic/VpnLogic.h \ ui/pages_logic/WizardLogic.h \ - ui/pages_logic/protocols/CloakLogic.h \ - ui/pages_logic/protocols/OpenVpnLogic.h \ - ui/pages_logic/protocols/OtherProtocolsLogic.h \ - ui/pages_logic/protocols/PageProtocolLogicBase.h \ - ui/pages_logic/protocols/ShadowSocksLogic.h \ + ui/pages_logic/protocols/CloakLogic.h \ + ui/pages_logic/protocols/OpenVpnLogic.h \ + ui/pages_logic/protocols/OtherProtocolsLogic.h \ + ui/pages_logic/protocols/PageProtocolLogicBase.h \ + ui/pages_logic/protocols/ShadowSocksLogic.h \ ui/property_helper.h \ ui/models/servers_model.h \ ui/uilogic.h \ - ui/qautostart.h \ - ui/models/sites_model.h \ + ui/qautostart.h \ + ui/models/sites_model.h \ utilities.h \ vpnconnection.h \ protocols/vpnprotocol.h \ @@ -84,24 +91,27 @@ HEADERS += \ platforms/ios/QRCodeReaderBase.h SOURCES += \ - configurators/cloak_configurator.cpp \ - configurators/ikev2_configurator.cpp \ - configurators/shadowsocks_configurator.cpp \ - configurators/ssh_configurator.cpp \ - configurators/vpn_configurator.cpp \ - configurators/wireguard_configurator.cpp \ + amnezia_application.cpp \ + configurators/cloak_configurator.cpp \ + configurators/configurator_base.cpp \ + configurators/ikev2_configurator.cpp \ + configurators/shadowsocks_configurator.cpp \ + configurators/ssh_configurator.cpp \ + configurators/vpn_configurator.cpp \ + configurators/wireguard_configurator.cpp \ containers/containers_defs.cpp \ - core/errorstrings.cpp \ + core/errorstrings.cpp \ configurators/openvpn_configurator.cpp \ - core/scripts_registry.cpp \ - core/server_defs.cpp \ + core/scripts_registry.cpp \ + core/server_defs.cpp \ core/servercontroller.cpp \ debug.cpp \ main.cpp \ managementserver.cpp \ platforms/ios/MobileUtils.cpp \ platforms/linux/leakdetector.cpp \ - protocols/protocols_defs.cpp \ + protocols/protocols_defs.cpp \ + secure_qsettings.cpp \ settings.cpp \ ui/notificationhandler.cpp \ ui/models/containers_model.cpp \ @@ -109,27 +119,28 @@ SOURCES += \ ui/pages_logic/AppSettingsLogic.cpp \ ui/pages_logic/GeneralSettingsLogic.cpp \ ui/pages_logic/NetworkSettingsLogic.cpp \ - ui/pages_logic/NewServerProtocolsLogic.cpp \ + ui/pages_logic/NewServerProtocolsLogic.cpp \ ui/pages_logic/PageLogicBase.cpp \ - ui/pages_logic/QrDecoderLogic.cpp \ - ui/pages_logic/ServerConfiguringProgressLogic.cpp \ + ui/pages_logic/QrDecoderLogic.cpp \ + ui/pages_logic/ServerConfiguringProgressLogic.cpp \ ui/pages_logic/ServerContainersLogic.cpp \ ui/pages_logic/ServerListLogic.cpp \ ui/pages_logic/ServerSettingsLogic.cpp \ ui/pages_logic/ShareConnectionLogic.cpp \ ui/pages_logic/SitesLogic.cpp \ ui/pages_logic/StartPageLogic.cpp \ + ui/pages_logic/ViewConfigLogic.cpp \ ui/pages_logic/VpnLogic.cpp \ ui/pages_logic/WizardLogic.cpp \ - ui/pages_logic/protocols/CloakLogic.cpp \ - ui/pages_logic/protocols/OpenVpnLogic.cpp \ - ui/pages_logic/protocols/OtherProtocolsLogic.cpp \ - ui/pages_logic/protocols/PageProtocolLogicBase.cpp \ - ui/pages_logic/protocols/ShadowSocksLogic.cpp \ + ui/pages_logic/protocols/CloakLogic.cpp \ + ui/pages_logic/protocols/OpenVpnLogic.cpp \ + ui/pages_logic/protocols/OtherProtocolsLogic.cpp \ + ui/pages_logic/protocols/PageProtocolLogicBase.cpp \ + ui/pages_logic/protocols/ShadowSocksLogic.cpp \ ui/models/servers_model.cpp \ ui/uilogic.cpp \ - ui/qautostart.cpp \ - ui/models/sites_model.cpp \ + ui/qautostart.cpp \ + ui/models/sites_model.cpp \ utilities.cpp \ vpnconnection.cpp \ protocols/vpnprotocol.cpp \ @@ -168,7 +179,6 @@ win32 { -lshlwapi \ -liphlpapi \ -lws2_32 \ - -liphlpapi \ -lgdi32 QMAKE_LFLAGS_WINDOWS += /entry:mainCRTStartup @@ -248,33 +258,31 @@ android { INCLUDEPATH += platforms/android HEADERS += \ - platforms/android/native.h \ platforms/android/android_controller.h \ platforms/android/android_notificationhandler.h \ protocols/android_vpnprotocol.h SOURCES += \ - platforms/android/native.cpp \ platforms/android/android_controller.cpp \ platforms/android/android_notificationhandler.cpp \ protocols/android_vpnprotocol.cpp DISTFILES += \ - android/AndroidManifest.xml \ - android/build.gradle \ - android/gradle/wrapper/gradle-wrapper.jar \ - android/gradle/wrapper/gradle-wrapper.properties \ - android/gradlew \ - android/gradlew.bat \ - android/gradle.properties \ - android/res/values/libs.xml \ - android/src/org/amnezia/vpn/OpenVPNThreadv3.kt \ - android/src/org/amnezia/vpn/VpnService.kt \ - android/src/org/amnezia/vpn/VpnServiceBinder.kt \ - android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt + android/AndroidManifest.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew \ + android/gradlew.bat \ + android/gradle.properties \ + android/res/values/libs.xml \ + android/src/org/amnezia/vpn/OpenVPNThreadv3.kt \ + android/src/org/amnezia/vpn/VpnService.kt \ + android/src/org/amnezia/vpn/VpnServiceBinder.kt \ + android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt - ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android for (abi, ANDROID_ABIS): { equals(ANDROID_TARGET_ARCH,$$abi) { @@ -295,7 +303,7 @@ android { } ios { - message("Client ios build") + message("Client iOS build") CONFIG += static CONFIG += file_copies @@ -322,7 +330,7 @@ ios { platforms/ios/QtAppDelegate-C-Interface.h SOURCES -= \ - platforms/ios/QRCodeReader.cpp \ + platforms/ios/QRCodeReaderBase.cpp \ platforms/ios/MobileUtils.cpp SOURCES += \ diff --git a/client/configurators/cloak_configurator.cpp b/client/configurators/cloak_configurator.cpp index 211ddc79..50f554dc 100644 --- a/client/configurators/cloak_configurator.cpp +++ b/client/configurators/cloak_configurator.cpp @@ -4,18 +4,25 @@ #include #include +#include "core/servercontroller.h" #include "containers/containers_defs.h" +CloakConfigurator::CloakConfigurator(std::shared_ptr settings, std::shared_ptr serverController, QObject *parent): + ConfiguratorBase(settings, serverController, parent) +{ + +} + QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) { ErrorCode e = ErrorCode::NoError; - QString cloakPublicKey = ServerController::getTextFileFromContainer(container, credentials, + QString cloakPublicKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckPublicKeyPath, &e); cloakPublicKey.replace("\n", ""); - QString cloakBypassUid = ServerController::getTextFileFromContainer(container, credentials, + QString cloakBypassUid = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckBypassUidKeyPath, &e); cloakBypassUid.replace("\n", ""); @@ -40,8 +47,8 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials, config.insert(config_key::remote, credentials.hostName); config.insert(config_key::port, "$CLOAK_SERVER_PORT"); - QString textCfg = ServerController::replaceVars(QJsonDocument(config).toJson(), - ServerController::genVarsForScript(credentials, container, containerConfig)); + QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(), + m_serverController->genVarsForScript(credentials, container, containerConfig)); // qDebug().noquote() << textCfg; return textCfg; diff --git a/client/configurators/cloak_configurator.h b/client/configurators/cloak_configurator.h index 562b9917..c6184805 100644 --- a/client/configurators/cloak_configurator.h +++ b/client/configurators/cloak_configurator.h @@ -3,15 +3,18 @@ #include -#include "core/defs.h" -#include "settings.h" -#include "core/servercontroller.h" +#include "configurator_base.h" -class CloakConfigurator +using namespace amnezia; + +class CloakConfigurator : ConfiguratorBase { + Q_OBJECT public: + CloakConfigurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); - static QString genCloakConfig(const ServerCredentials &credentials, DockerContainer container, + QString genCloakConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); }; diff --git a/client/configurators/configurator_base.cpp b/client/configurators/configurator_base.cpp new file mode 100644 index 00000000..44377401 --- /dev/null +++ b/client/configurators/configurator_base.cpp @@ -0,0 +1,10 @@ +#include "configurator_base.h" + +ConfiguratorBase::ConfiguratorBase(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent) + : QObject{parent}, + m_settings(settings), + m_serverController(serverController) +{ + +} diff --git a/client/configurators/configurator_base.h b/client/configurators/configurator_base.h new file mode 100644 index 00000000..8c0614d9 --- /dev/null +++ b/client/configurators/configurator_base.h @@ -0,0 +1,25 @@ +#ifndef CONFIGURATORBASE_H +#define CONFIGURATORBASE_H + +#include + +class Settings; +class ServerController; + +#include "containers/containers_defs.h" +#include "core/defs.h" + +class ConfiguratorBase : public QObject +{ + Q_OBJECT +public: + explicit ConfiguratorBase(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); + +protected: + std::shared_ptr m_settings; + std::shared_ptr m_serverController; + +}; + +#endif // CONFIGURATORBASE_H diff --git a/client/configurators/ikev2_configurator.cpp b/client/configurators/ikev2_configurator.cpp index c3ee19a1..9b702824 100644 --- a/client/configurators/ikev2_configurator.cpp +++ b/client/configurators/ikev2_configurator.cpp @@ -8,12 +8,18 @@ #include #include -#include "sftpdefs.h" - -#include "core/server_defs.h" #include "containers/containers_defs.h" +#include "core/server_defs.h" #include "core/scripts_registry.h" #include "utilities.h" +#include "core/servercontroller.h" + + +Ikev2Configurator::Ikev2Configurator(std::shared_ptr settings, std::shared_ptr serverController, QObject *parent): + ConfiguratorBase(settings, serverController, parent) +{ + +} Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container, ErrorCode *errorCode) @@ -35,16 +41,16 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se "--extKeyUsage serverAuth,clientAuth -8 \"%1\"") .arg(connData.clientId); - ErrorCode e = ServerController::runContainerScript(credentials, container, scriptCreateCert); + ErrorCode e = m_serverController->runContainerScript(credentials, container, scriptCreateCert); QString scriptExportCert = QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"") .arg(connData.password) .arg(connData.clientId) .arg(certFileName); - e = ServerController::runContainerScript(credentials, container, scriptExportCert); + e = m_serverController->runContainerScript(credentials, container, scriptExportCert); - connData.clientCert = ServerController::getTextFileFromContainer(container, credentials, certFileName, &e); - connData.caCert = ServerController::getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", &e); + connData.clientCert = m_serverController->getTextFileFromContainer(container, credentials, certFileName, &e); + connData.caCert = m_serverController->getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", &e); qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size(); qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size(); diff --git a/client/configurators/ikev2_configurator.h b/client/configurators/ikev2_configurator.h index 9061a47c..35c03b19 100644 --- a/client/configurators/ikev2_configurator.h +++ b/client/configurators/ikev2_configurator.h @@ -4,12 +4,15 @@ #include #include +#include "configurator_base.h" #include "core/defs.h" -#include "core/servercontroller.h" -class Ikev2Configurator +class Ikev2Configurator : ConfiguratorBase { + Q_OBJECT public: + Ikev2Configurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); struct ConnectionData { QByteArray clientCert; // p12 client cert @@ -19,14 +22,14 @@ public: QString host; // host ip }; - static QString genIkev2Config(const ServerCredentials &credentials, DockerContainer container, + QString genIkev2Config(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); - static QString genIkev2Config(const ConnectionData &connData); - static QString genMobileConfig(const ConnectionData &connData); - static QString genStrongSwanConfig(const ConnectionData &connData); + QString genIkev2Config(const ConnectionData &connData); + QString genMobileConfig(const ConnectionData &connData); + QString genStrongSwanConfig(const ConnectionData &connData); - static ConnectionData prepareIkev2Config(const ServerCredentials &credentials, + ConnectionData prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container, ErrorCode *errorCode = nullptr); }; diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index 19d352fc..441c15f6 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -6,16 +6,25 @@ #include #include #include +#include -#include "core/server_defs.h" #include "containers/containers_defs.h" +#include "core/server_defs.h" +#include "core/servercontroller.h" #include "core/scripts_registry.h" #include "utilities.h" +#include "settings.h" #include #include #include +OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr settings, std::shared_ptr serverController, QObject *parent): + ConfiguratorBase(settings, serverController, parent) +{ + +} + OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, ErrorCode *errorCode) { @@ -31,7 +40,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co arg(amnezia::protocols::openvpn::clientsDirPath). arg(connData.clientId); - ErrorCode e = ServerController::uploadTextFileToContainer(container, credentials, connData.request, reqFileName); + ErrorCode e = m_serverController->uploadTextFileToContainer(container, credentials, connData.request, reqFileName); if (e) { if (errorCode) *errorCode = e; return connData; @@ -43,8 +52,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } - connData.caCert = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e); - connData.clientCert = ServerController::getTextFileFromContainer(container, credentials, + connData.caCert = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e); + connData.clientCert = m_serverController->getTextFileFromContainer(container, credentials, QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e); if (e) { @@ -52,7 +61,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } - connData.taKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, &e); + connData.taKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, &e); if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) { if (errorCode) *errorCode = ErrorCode::RemoteProcessCrashError; @@ -61,17 +70,11 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } -Settings &OpenVpnConfigurator::m_settings() -{ - static Settings s; - return s; -} - QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) { - QString config = ServerController::replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container), - ServerController::genVarsForScript(credentials, container, containerConfig)); + QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container), + m_serverController->genVarsForScript(credentials, container, containerConfig)); ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode); if (errorCode && *errorCode) { @@ -105,7 +108,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig) QJsonObject json = QJsonDocument::fromJson(jsonConfig.toUtf8()).object(); QString config = json[config_key::config].toString(); - if (m_settings().routeMode() != Settings::VpnAllSites) { + if (m_settings->routeMode() != Settings::VpnAllSites) { config.replace("redirect-gateway def1 bypass-dhcp", ""); } else { @@ -161,9 +164,9 @@ ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, .arg(clientId); QStringList scriptList {script_import, script_sign}; - QString script = ServerController::replaceVars(scriptList.join("\n"), ServerController::genVarsForScript(credentials, container)); + QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container)); - return ServerController::runScript(credentials, script); + return m_serverController->runScript(credentials, script); } OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() diff --git a/client/configurators/openvpn_configurator.h b/client/configurators/openvpn_configurator.h index 99c4efa6..25230499 100644 --- a/client/configurators/openvpn_configurator.h +++ b/client/configurators/openvpn_configurator.h @@ -4,13 +4,15 @@ #include #include +#include "configurator_base.h" #include "core/defs.h" -#include "settings.h" -#include "core/servercontroller.h" -class OpenVpnConfigurator +class OpenVpnConfigurator : ConfiguratorBase { + Q_OBJECT public: + OpenVpnConfigurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); struct ConnectionData { QString clientId; @@ -22,22 +24,21 @@ public: QString host; // host ip }; - static QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, + QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); - static QString processConfigWithLocalSettings(QString jsonConfig); - static QString processConfigWithExportSettings(QString jsonConfig); + QString processConfigWithLocalSettings(QString jsonConfig); + QString processConfigWithExportSettings(QString jsonConfig); - static ErrorCode signCert(DockerContainer container, + ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId); private: - static ConnectionData createCertRequest(); + ConnectionData createCertRequest(); - static ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, + ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, ErrorCode *errorCode = nullptr); - static Settings &m_settings(); }; #endif // OPENVPN_CONFIGURATOR_H diff --git a/client/configurators/shadowsocks_configurator.cpp b/client/configurators/shadowsocks_configurator.cpp index 6f63ba79..97503ac4 100644 --- a/client/configurators/shadowsocks_configurator.cpp +++ b/client/configurators/shadowsocks_configurator.cpp @@ -5,13 +5,20 @@ #include #include "containers/containers_defs.h" +#include "core/servercontroller.h" + +ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr settings, std::shared_ptr serverController, QObject *parent): + ConfiguratorBase(settings, serverController, parent) +{ + +} QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) { ErrorCode e = ErrorCode::NoError; - QString ssKey = ServerController::getTextFileFromContainer(container, credentials, + QString ssKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::shadowsocks::ssKeyPath, &e); ssKey.replace("\n", ""); @@ -29,8 +36,8 @@ QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &c config.insert("method", "$SHADOWSOCKS_CIPHER"); - QString textCfg = ServerController::replaceVars(QJsonDocument(config).toJson(), - ServerController::genVarsForScript(credentials, container, containerConfig)); + QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(), + m_serverController->genVarsForScript(credentials, container, containerConfig)); //qDebug().noquote() << textCfg; return textCfg; diff --git a/client/configurators/shadowsocks_configurator.h b/client/configurators/shadowsocks_configurator.h index 4445b074..be80f169 100644 --- a/client/configurators/shadowsocks_configurator.h +++ b/client/configurators/shadowsocks_configurator.h @@ -3,15 +3,17 @@ #include +#include "configurator_base.h" #include "core/defs.h" -#include "settings.h" -#include "core/servercontroller.h" -class ShadowSocksConfigurator +class ShadowSocksConfigurator : ConfiguratorBase { + Q_OBJECT public: + ShadowSocksConfigurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); - static QString genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container, + QString genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); }; diff --git a/client/configurators/ssh_configurator.cpp b/client/configurators/ssh_configurator.cpp index 4a206779..f8dc9869 100644 --- a/client/configurators/ssh_configurator.cpp +++ b/client/configurators/ssh_configurator.cpp @@ -14,8 +14,15 @@ #include "core/server_defs.h" #include "utilities.h" +#include "sftpdefs.h" + using namespace QSsh; +SshConfigurator::SshConfigurator(std::shared_ptr settings, std::shared_ptr serverController, QObject *parent): + ConfiguratorBase(settings, serverController, parent) +{ + +} QString SshConfigurator::convertOpenSShKey(const QString &key) { diff --git a/client/configurators/ssh_configurator.h b/client/configurators/ssh_configurator.h index 71146a1c..d7a177c3 100644 --- a/client/configurators/ssh_configurator.h +++ b/client/configurators/ssh_configurator.h @@ -4,16 +4,19 @@ #include #include +#include "configurator_base.h" #include "core/defs.h" -#include "settings.h" -#include "core/servercontroller.h" -class SshConfigurator +class SshConfigurator : ConfiguratorBase { + Q_OBJECT public: - static QProcessEnvironment prepareEnv(); - static QString convertOpenSShKey(const QString &key); - static void openSshTerminal(const ServerCredentials &credentials); + SshConfigurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); + + QProcessEnvironment prepareEnv(); + QString convertOpenSShKey(const QString &key); + void openSshTerminal(const ServerCredentials &credentials); }; diff --git a/client/configurators/vpn_configurator.cpp b/client/configurators/vpn_configurator.cpp index edc13c90..1433eeaa 100644 --- a/client/configurators/vpn_configurator.cpp +++ b/client/configurators/vpn_configurator.cpp @@ -4,6 +4,7 @@ #include "shadowsocks_configurator.h" #include "wireguard_configurator.h" #include "ikev2_configurator.h" +#include "ssh_configurator.h" #include #include @@ -11,11 +12,18 @@ #include "containers/containers_defs.h" #include "utilities.h" +#include "settings.h" -Settings &VpnConfigurator::m_settings() +VpnConfigurator::VpnConfigurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent): + ConfiguratorBase(settings, serverController, parent) { - static Settings s; - return s; + openVpnConfigurator = std::shared_ptr(new OpenVpnConfigurator(settings, serverController, this)); + shadowSocksConfigurator = std::shared_ptr(new ShadowSocksConfigurator(settings, serverController, this)); + cloakConfigurator = std::shared_ptr(new CloakConfigurator(settings, serverController, this)); + wireguardConfigurator = std::shared_ptr(new WireguardConfigurator(settings, serverController, this)); + ikev2Configurator = std::shared_ptr(new Ikev2Configurator(settings, serverController, this)); + sshConfigurator = std::shared_ptr(new SshConfigurator(settings, serverController, this)); } QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, @@ -23,19 +31,19 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia { switch (proto) { case Proto::OpenVpn: - return OpenVpnConfigurator::genOpenVpnConfig(credentials, container, containerConfig, errorCode); + return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, errorCode); case Proto::ShadowSocks: - return ShadowSocksConfigurator::genShadowSocksConfig(credentials, container, containerConfig, errorCode); + return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode); case Proto::Cloak: - return CloakConfigurator::genCloakConfig(credentials, container, containerConfig, errorCode); + return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode); case Proto::WireGuard: - return WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, errorCode); + return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode); case Proto::Ikev2: - return Ikev2Configurator::genIkev2Config(credentials, container, containerConfig, errorCode); + return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode); default: return ""; @@ -46,20 +54,20 @@ QPair VpnConfigurator::getDnsForConfig(int serverIndex) { QPair dns; - bool useAmneziaDns = m_settings().useAmneziaDns(); - const QJsonObject &server = m_settings().server(serverIndex); + bool useAmneziaDns = m_settings->useAmneziaDns(); + const QJsonObject &server = m_settings->server(serverIndex); dns.first = server.value(config_key::dns1).toString(); dns.second = server.value(config_key::dns2).toString(); if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) { - if (useAmneziaDns && m_settings().containers(serverIndex).contains(DockerContainer::Dns)) { + if (useAmneziaDns && m_settings->containers(serverIndex).contains(DockerContainer::Dns)) { dns.first = protocols::dns::amneziaDnsIp; } - else dns.first = m_settings().primaryDns(); + else dns.first = m_settings->primaryDns(); } if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) { - dns.second = m_settings().secondaryDns(); + dns.second = m_settings->secondaryDns(); } qDebug() << "VpnConfigurator::getDnsForConfig" << dns.first << dns.second; @@ -83,7 +91,7 @@ QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, Docker processConfigWithDnsSettings(serverIndex, container, proto, config); if (proto == Proto::OpenVpn) { - config = OpenVpnConfigurator::processConfigWithLocalSettings(config); + config = openVpnConfigurator->processConfigWithLocalSettings(config); } return config; } @@ -94,7 +102,7 @@ QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, Docke processConfigWithDnsSettings(serverIndex, container, proto, config); if (proto == Proto::OpenVpn) { - config = OpenVpnConfigurator::processConfigWithExportSettings(config); + config = openVpnConfigurator->processConfigWithExportSettings(config); } return config; } diff --git a/client/configurators/vpn_configurator.h b/client/configurators/vpn_configurator.h index 702fc9d4..47bdc1fd 100644 --- a/client/configurators/vpn_configurator.h +++ b/client/configurators/vpn_configurator.h @@ -3,29 +3,46 @@ #include +#include "configurator_base.h" #include "core/defs.h" -#include "settings.h" -#include "core/servercontroller.h" + + +class OpenVpnConfigurator; +class ShadowSocksConfigurator; +class CloakConfigurator; +class WireguardConfigurator; +class Ikev2Configurator; +class SshConfigurator; // Retrieve connection settings from server -class VpnConfigurator +class VpnConfigurator : ConfiguratorBase { + Q_OBJECT public: + VpnConfigurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); - static QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container, + QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr); - static QPair getDnsForConfig(int serverIndex); - static QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); + QPair getDnsForConfig(int serverIndex); + QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); - static QString &processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); - static QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); + QString &processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); + QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); // workaround for containers which is not support normal configaration - static void updateContainerConfigAfterInstallation(DockerContainer container, + void updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig, const QString &stdOut); - static Settings &m_settings(); + std::shared_ptr m_serverController; + + std::shared_ptr openVpnConfigurator; + std::shared_ptr shadowSocksConfigurator; + std::shared_ptr cloakConfigurator; + std::shared_ptr wireguardConfigurator; + std::shared_ptr ikev2Configurator; + std::shared_ptr sshConfigurator; }; #endif // VPN_CONFIGURATOR_H diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 5096f67d..532d89f6 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -5,18 +5,27 @@ #include #include #include +#include + #include #include #include #include -#include "sftpdefs.h" -#include "core/server_defs.h" #include "containers/containers_defs.h" +#include "core/server_defs.h" #include "core/scripts_registry.h" #include "utilities.h" +#include "core/servercontroller.h" +#include "settings.h" + +WireguardConfigurator::WireguardConfigurator(std::shared_ptr settings, std::shared_ptr serverController, QObject *parent): + ConfiguratorBase(settings, serverController, parent) +{ + +} WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys() { @@ -71,7 +80,12 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon stdOut += data + "\n"; }; - ServerController::runContainerScript(credentials, container, script, cbReadStdOut); + e = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut); + if (errorCode && e) { + *errorCode = e; + return connData; + } + stdOut.replace("AllowedIPs = ", ""); stdOut.replace("/32", ""); QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts); @@ -104,14 +118,14 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon } // Get keys - connData.serverPubKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e); + connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e); connData.serverPubKey.replace("\n", ""); if (e) { if (errorCode) *errorCode = e; return connData; } - connData.pskKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e); + connData.pskKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e); connData.pskKey.replace("\n", ""); if (e) { @@ -129,7 +143,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon arg(connData.pskKey). arg(connData.clientIP); - e = ServerController::uploadTextFileToContainer(container, credentials, configPart, + e = m_serverController->uploadTextFileToContainer(container, credentials, configPart, protocols::wireguard::serverConfigPath, QSsh::SftpOverwriteMode::SftpAppendToExisting); if (e) { @@ -137,24 +151,18 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon return connData; } - e = ServerController::runScript(credentials, - ServerController::replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'", - ServerController::genVarsForScript(credentials, container))); + e = m_serverController->runScript(credentials, + m_serverController->replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'", + m_serverController->genVarsForScript(credentials, container))); return connData; } -Settings &WireguardConfigurator::m_settings() -{ - static Settings s; - return s; -} - QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) { - QString config = ServerController::replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container), - ServerController::genVarsForScript(credentials, container, containerConfig)); + QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container), + m_serverController->genVarsForScript(credentials, container, containerConfig)); ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode); if (errorCode && *errorCode) { @@ -182,8 +190,8 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede QString WireguardConfigurator::processConfigWithLocalSettings(QString config) { // TODO replace DNS if it already set - config.replace("$PRIMARY_DNS", m_settings().primaryDns()); - config.replace("$SECONDARY_DNS", m_settings().secondaryDns()); + config.replace("$PRIMARY_DNS", m_settings->primaryDns()); + config.replace("$SECONDARY_DNS", m_settings->secondaryDns()); QJsonObject jConfig; jConfig[config_key::config] = config; @@ -193,8 +201,8 @@ QString WireguardConfigurator::processConfigWithLocalSettings(QString config) QString WireguardConfigurator::processConfigWithExportSettings(QString config) { - config.replace("$PRIMARY_DNS", m_settings().primaryDns()); - config.replace("$SECONDARY_DNS", m_settings().secondaryDns()); + config.replace("$PRIMARY_DNS", m_settings->primaryDns()); + config.replace("$SECONDARY_DNS", m_settings->secondaryDns()); return config; } diff --git a/client/configurators/wireguard_configurator.h b/client/configurators/wireguard_configurator.h index 635c8dce..76790d74 100644 --- a/client/configurators/wireguard_configurator.h +++ b/client/configurators/wireguard_configurator.h @@ -4,13 +4,15 @@ #include #include +#include "configurator_base.h" #include "core/defs.h" -#include "settings.h" -#include "core/servercontroller.h" -class WireguardConfigurator +class WireguardConfigurator : ConfiguratorBase { + Q_OBJECT public: + WireguardConfigurator(std::shared_ptr settings, + std::shared_ptr serverController, QObject *parent = nullptr); struct ConnectionData { QString clientPrivKey; // client private key @@ -21,20 +23,18 @@ public: QString host; // host ip }; - static QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, + QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); - static QString processConfigWithLocalSettings(QString config); - static QString processConfigWithExportSettings(QString config); + QString processConfigWithLocalSettings(QString config); + QString processConfigWithExportSettings(QString config); private: - static ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, + ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); - static ConnectionData genClientKeys(); - - static Settings &m_settings(); + ConnectionData genClientKeys(); }; #endif // WIREGUARD_CONFIGURATOR_H diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 0d2e13f7..c86ba303 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -18,18 +18,18 @@ #include "containers/containers_defs.h" #include "server_defs.h" +#include "settings.h" #include "scripts_registry.h" #include "utilities.h" #include - using namespace QSsh; -Settings &ServerController::m_settings() +ServerController::ServerController(std::shared_ptr settings, QObject *parent) : + m_settings(settings) { - static Settings s; - return s; + } ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script, @@ -605,7 +605,7 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr cbReadStdOut, cbReadStdErr); - VpnConfigurator::updateContainerConfigAfterInstallation(container, config, stdOut); + m_configurator->updateContainerConfigAfterInstallation(container, config, stdOut); return e; } @@ -698,8 +698,8 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential vars.append({{"$IPSEC_VPN_C2C_TRAFFIC", "no"}}); - vars.append({{"$PRIMARY_SERVER_DNS", m_settings().primaryDns()}}); - vars.append({{"$SECONDARY_SERVER_DNS", m_settings().secondaryDns()}}); + vars.append({{"$PRIMARY_SERVER_DNS", m_settings->primaryDns()}}); + vars.append({{"$SECONDARY_SERVER_DNS", m_settings->secondaryDns()}}); // Sftp vars diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index 0f1e6cbf..091eaa52 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -7,78 +7,83 @@ #include "sshremoteprocess.h" #include "debug.h" #include "defs.h" -#include "settings.h" #include "containers/containers_defs.h" #include "sftpdefs.h" +class Settings; +class VpnConfigurator; + using namespace amnezia; class ServerController : public QObject { Q_OBJECT public: + ServerController(std::shared_ptr settings, QObject *parent = nullptr); + typedef QList> Vars; - static ErrorCode fromSshConnectionErrorCode(QSsh::SshError error); + ErrorCode fromSshConnectionErrorCode(QSsh::SshError error); // QSsh exitCode and exitStatus are different things - static ErrorCode fromSshProcessExitStatus(int exitStatus); + ErrorCode fromSshProcessExitStatus(int exitStatus); - static QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials); - static void disconnectFromHost(const ServerCredentials &credentials); + QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials); + void disconnectFromHost(const ServerCredentials &credentials); - static ErrorCode removeAllContainers(const ServerCredentials &credentials); - static ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container); - static ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); - static ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, + ErrorCode removeAllContainers(const ServerCredentials &credentials); + ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container); + ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); + ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig, QJsonObject &newConfig); // create initial config - generate passwords, etc - static QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp); + QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp); - static bool isReinstallContainerRequred(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig); + bool isReinstallContainerRequred(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig); - static ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials); + ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials); - static ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, + ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, QSsh::SftpOverwriteMode overwriteMode = QSsh::SftpOverwriteMode::SftpOverwriteExisting); - static ErrorCode uploadTextFileToContainer(DockerContainer container, + ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path, QSsh::SftpOverwriteMode overwriteMode = QSsh::SftpOverwriteMode::SftpOverwriteExisting); - static QByteArray getTextFileFromContainer(DockerContainer container, + QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr); - static ErrorCode setupServerFirewall(const ServerCredentials &credentials); + ErrorCode setupServerFirewall(const ServerCredentials &credentials); - static QString replaceVars(const QString &script, const Vars &vars); + QString replaceVars(const QString &script, const Vars &vars); - static ErrorCode runScript(const ServerCredentials &credentials, QString script, + ErrorCode runScript(const ServerCredentials &credentials, QString script, const std::function)> &cbReadStdOut = nullptr, const std::function)> &cbReadStdErr = nullptr); - static ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script, + ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script, const std::function)> &cbReadStdOut = nullptr, const std::function)> &cbReadStdErr = nullptr); - static Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject()); + Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject()); - static QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); - static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); + QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); + QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); private: - static ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container); - static ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); - static ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); - static ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); - static ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); - static ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); + ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container); + ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); + ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); + ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); + ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); + ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); - static Settings &m_settings(); + std::shared_ptr m_settings; + std::shared_ptr m_configurator; }; #endif // SERVERCONTROLLER_H diff --git a/client/defines.h b/client/defines.h index 415dc21a..c64eb458 100644 --- a/client/defines.h +++ b/client/defines.h @@ -4,7 +4,7 @@ #define APPLICATION_NAME "AmneziaVPN" #define SERVICE_NAME "AmneziaVPN-service" #define ORGANIZATION_NAME "AmneziaVPN.ORG" -#define APP_MAJOR_VERSION "2.0.10" -#define APP_VERSION "2.0.10.0" +#define APP_MAJOR_VERSION "2.1.2" +#define APP_VERSION "2.1.2.0" #endif // DEFINES_H diff --git a/client/main.cpp b/client/main.cpp index 1283e839..62685953 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,86 +1,33 @@ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include "ui/uilogic.h" #include +#include -#include "ui/pages.h" - -#include "ui/pages_logic/AppSettingsLogic.h" -#include "ui/pages_logic/GeneralSettingsLogic.h" -#include "ui/pages_logic/NetworkSettingsLogic.h" -#include "ui/pages_logic/NewServerProtocolsLogic.h" -#include "ui/pages_logic/QrDecoderLogic.h" -#include "ui/pages_logic/ServerConfiguringProgressLogic.h" -#include "ui/pages_logic/ServerContainersLogic.h" -#include "ui/pages_logic/ServerListLogic.h" -#include "ui/pages_logic/ServerSettingsLogic.h" -#include "ui/pages_logic/ServerContainersLogic.h" -#include "ui/pages_logic/ShareConnectionLogic.h" -#include "ui/pages_logic/SitesLogic.h" -#include "ui/pages_logic/StartPageLogic.h" -#include "ui/pages_logic/VpnLogic.h" -#include "ui/pages_logic/WizardLogic.h" - -#include "ui/pages_logic/protocols/CloakLogic.h" -#include "ui/pages_logic/protocols/OpenVpnLogic.h" -#include "ui/pages_logic/protocols/ShadowSocksLogic.h" - -#include "ui/uilogic.h" - -#include "QZXing.h" - -#include "platforms/ios/QRCodeReaderBase.h" - -#include "debug.h" +#include "amnezia_application.h" #include "defines.h" -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) -#define QAPPLICATION_CLASS QGuiApplication -#include "singleapplication.h" -#undef QAPPLICATION_CLASS -#endif - - #ifdef Q_OS_WIN #include "Windows.h" #endif -#if defined(Q_OS_ANDROID) -#include "native.h" -#endif - #if defined(Q_OS_IOS) -#include "QtAppDelegate-C-Interface.h" +#include "platforms/ios/QtAppDelegate-C-Interface.h" #endif -static void loadTranslator() -{ - QTranslator* translator = new QTranslator; - if (translator->load(QLocale(), QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/translations"))) { - qApp->installTranslator(translator); - } -} int main(int argc, char *argv[]) { QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false")); - QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); #ifdef Q_OS_WIN AllowSetForegroundWindow(ASFW_ANY); #endif -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - SingleApplication app(argc, argv, true, SingleApplication::Mode::User | SingleApplication::Mode::SecondaryNotification); + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + AmneziaApplication app(argc, argv); +#else + AmneziaApplication app(argc, argv, true, SingleApplication::Mode::User | SingleApplication::Mode::SecondaryNotification); if (!app.isPrimary()) { QTimer::singleShot(1000, &app, [&](){ @@ -88,163 +35,31 @@ int main(int argc, char *argv[]) }); return app.exec(); } -#else - QApplication app(argc, argv); #endif +// Allow to raise app window if secondary instance launched #ifdef Q_OS_WIN AllowSetForegroundWindow(0); #endif -#if defined(Q_OS_ANDROID) - NativeHelpers::registerApplicationInstance(&app); -#endif - #if defined(Q_OS_IOS) QtAppDelegateInitialize(); #endif - loadTranslator(); - - QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-BlackItalic.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-Bold.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-BoldItalic.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-Italic.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-Light.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-LightItalic.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-Regular.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-Thin.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Lato-ThinItalic.ttf"); + app.registerTypes(); app.setApplicationName(APPLICATION_NAME); app.setOrganizationName(ORGANIZATION_NAME); app.setApplicationDisplayName(APPLICATION_NAME); - QCommandLineParser parser; - parser.setApplicationDescription(APPLICATION_NAME); - parser.addHelpOption(); - parser.addVersionOption(); + app.loadTranslator(); + app.loadFonts(); - QCommandLineOption c_autostart {{"a", "autostart"}, "System autostart"}; - parser.addOption(c_autostart); + bool doExec = app.parseCommands(); - QCommandLineOption c_cleanup {{"c", "cleanup"}, "Cleanup logs"}; - parser.addOption(c_cleanup); - - parser.process(app); - - if (parser.isSet(c_cleanup)) { - Debug::cleanUp(); - QTimer::singleShot(100,[&app]{ - app.quit(); - }); - app.exec(); - return 0; + if (doExec) { + app.init(); + return app.exec(); } - - Settings settings; - - if (settings.isSaveLogs()) { - if (!Debug::init()) { - qWarning() << "Initialization of debug subsystem failed"; - } - } - - app.setQuitOnLastWindowClosed(false); - - QZXing::registerQMLTypes(); - - qRegisterMetaType("VpnProtocol::VpnConnectionState"); - qRegisterMetaType("ServerCredentials"); - - qRegisterMetaType("DockerContainer"); - qRegisterMetaType("TransportProto"); - qRegisterMetaType("Proto"); - qRegisterMetaType("ServiceType"); - qRegisterMetaType("Page"); - qRegisterMetaType("ConnectionState"); - - qRegisterMetaType("PageProtocolLogicBase *"); - - UiLogic *uiLogic = new UiLogic; - - QQmlApplicationEngine *engine = new QQmlApplicationEngine; - - declareQmlPageEnum(); - declareQmlProtocolEnum(); - declareQmlContainerEnum(); - - qmlRegisterType("PageType", 1, 0, "PageType"); - qmlRegisterType("QRCodeReader", 1, 0, "QRCodeReader"); - - QScopedPointer containerProps(new ContainerProps); - qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", containerProps.get()); - - QScopedPointer protocolProps(new ProtocolProps); - qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", protocolProps.get()); - - const QUrl url(QStringLiteral("qrc:/ui/qml/main.qml")); - QObject::connect(engine, &QQmlApplicationEngine::objectCreated, - &app, [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, Qt::QueuedConnection); - - engine->rootContext()->setContextProperty("Debug", &Debug::Instance()); - - engine->rootContext()->setContextProperty("UiLogic", uiLogic); - - engine->rootContext()->setContextProperty("AppSettingsLogic", uiLogic->appSettingsLogic()); - engine->rootContext()->setContextProperty("GeneralSettingsLogic", uiLogic->generalSettingsLogic()); - engine->rootContext()->setContextProperty("NetworkSettingsLogic", uiLogic->networkSettingsLogic()); - engine->rootContext()->setContextProperty("NewServerProtocolsLogic", uiLogic->newServerProtocolsLogic()); - engine->rootContext()->setContextProperty("QrDecoderLogic", uiLogic->qrDecoderLogic()); - engine->rootContext()->setContextProperty("ServerConfiguringProgressLogic", uiLogic->serverConfiguringProgressLogic()); - engine->rootContext()->setContextProperty("ServerListLogic", uiLogic->serverListLogic()); - engine->rootContext()->setContextProperty("ServerSettingsLogic", uiLogic->serverSettingsLogic()); - engine->rootContext()->setContextProperty("ServerContainersLogic", uiLogic->serverprotocolsLogic()); - engine->rootContext()->setContextProperty("ShareConnectionLogic", uiLogic->shareConnectionLogic()); - engine->rootContext()->setContextProperty("SitesLogic", uiLogic->sitesLogic()); - engine->rootContext()->setContextProperty("StartPageLogic", uiLogic->startPageLogic()); - engine->rootContext()->setContextProperty("VpnLogic", uiLogic->vpnLogic()); - engine->rootContext()->setContextProperty("WizardLogic", uiLogic->wizardLogic()); - -#if defined(Q_OS_IOS) - setStartPageLogic(uiLogic->startPageLogic()); -#endif - - engine->load(url); - - QObject::connect(&app, &QCoreApplication::aboutToQuit, uiLogic, [&engine, uiLogic](){ - QObject::disconnect(engine, 0,0,0); - delete engine; - - QObject::disconnect(uiLogic, 0,0,0); - delete uiLogic; - }); - - if (engine->rootObjects().size() > 0) { - uiLogic->setQmlRoot(engine->rootObjects().at(0)); - } - -#ifdef Q_OS_WIN - if (parser.isSet("a")) uiLogic->showOnStartup(); - else emit uiLogic->show(); -#else - uiLogic->showOnStartup(); -#endif - - // TODO - fix -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - if (app.isPrimary()) { - QObject::connect(&app, &SingleApplication::instanceStarted, uiLogic, [&](){ - qDebug() << "Secondary instance started, showing this window instead"; - emit uiLogic->show(); - emit uiLogic->raise(); - }); - } -#endif - - return app.exec(); + return 0; } diff --git a/client/platforms/android/native.cpp b/client/platforms/android/native.cpp deleted file mode 100644 index 60ff2d8b..00000000 --- a/client/platforms/android/native.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "native.h" -#include -#if defined(Q_OS_ANDROID) - #include -#endif // Q_OS_ANDROID - - -QObject *NativeHelpers::application_p_ = 0; - -#if defined(Q_OS_ANDROID) - -// define our native static functions -// these are the functions that Java part will call directly from Android UI thread -static void onPermissionsGranted(JNIEnv * /*env*/, jobject /*obj*/) -{ - QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsGranted" - , Qt::QueuedConnection); -} - -static void onPermissionsDenied(JNIEnv * /*env*/, jobject /*obj*/) -{ - QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsDenied" - , Qt::QueuedConnection); -} - -//create a vector with all our JNINativeMethod(s) -static JNINativeMethod methods[] = { - {"onPermissionsGranted", "()V", (void *)onPermissionsGranted}, - {"onPermissionsDenied", "()V", (void *)onPermissionsDenied}, -}; - -// this method is called automatically by Java after the .so file is loaded -JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) -{ - JNIEnv* env; - // get the JNIEnv pointer. - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) - return JNI_ERR; - - // search for Java class which declares the native methods - jclass javaClass = env->FindClass("org/ftylitak/qzxing/NativeFunctions"); - if (!javaClass) - return JNI_ERR; - - // register our native methods - if (env->RegisterNatives(javaClass, methods, - sizeof(methods) / sizeof(methods[0])) < 0) { - return JNI_ERR; - } - - return JNI_VERSION_1_6; -} - -#endif // Q_OS_ANDROID diff --git a/client/platforms/android/native.h b/client/platforms/android/native.h deleted file mode 100644 index db187a73..00000000 --- a/client/platforms/android/native.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NATIVE_H -#define NATIVE_H - -#include - -class NativeHelpers { -public: - static void registerApplicationInstance(QObject *app_p) { - application_p_ = app_p; - } - - static QObject* getApplicationInstance() { - return application_p_; - } - -private: - static QObject *application_p_; -}; - -#endif // NATIVE_H diff --git a/client/platforms/ios/MobileUtils.cpp b/client/platforms/ios/MobileUtils.cpp index eea504ce..3923d291 100644 --- a/client/platforms/ios/MobileUtils.cpp +++ b/client/platforms/ios/MobileUtils.cpp @@ -1,5 +1,5 @@ #include "MobileUtils.h" -void MobileUtils::shareText(const QStringList& filesToSend) { +void MobileUtils::shareText(const QStringList&) {} + -} diff --git a/client/platforms/ios/MobileUtils.h b/client/platforms/ios/MobileUtils.h index 42aa4031..a7967fdf 100644 --- a/client/platforms/ios/MobileUtils.h +++ b/client/platforms/ios/MobileUtils.h @@ -12,6 +12,7 @@ public: public slots: static void shareText(const QStringList& filesToSend); + }; #endif // MOBILEUTILS_H diff --git a/client/platforms/ios/MobileUtils.mm b/client/platforms/ios/MobileUtils.mm index 94c7c775..a9ad52b5 100644 --- a/client/platforms/ios/MobileUtils.mm +++ b/client/platforms/ios/MobileUtils.mm @@ -1,6 +1,9 @@ #include "MobileUtils.h" #include +#include + +#include static UIViewController* getViewController() { NSArray *windows = [[UIApplication sharedApplication]windows]; @@ -31,3 +34,4 @@ void MobileUtils::shareText(const QStringList& filesToSend) { popController.sourceView = qtController.view; } } + diff --git a/client/protocols/ikev2_vpn_protocol_windows.cpp b/client/protocols/ikev2_vpn_protocol_windows.cpp index 5697776f..44950f91 100644 --- a/client/protocols/ikev2_vpn_protocol_windows.cpp +++ b/client/protocols/ikev2_vpn_protocol_windows.cpp @@ -199,7 +199,7 @@ ErrorCode Ikev2Protocol::start() setLastError(ErrorCode::AmneziaServiceConnectionFailed); return ErrorCode::AmneziaServiceConnectionFailed; } - certInstallProcess->setProgram("certutil"); + certInstallProcess->setProgram(PermittedProcess::CertUtil); QStringList arguments({"-f" , "-importpfx", "-p", m_config[config_key::password].toString(), certFile.fileName(), "NoExport" diff --git a/client/protocols/openvpnovercloakprotocol.cpp b/client/protocols/openvpnovercloakprotocol.cpp index 59fc359a..5ba2c587 100644 --- a/client/protocols/openvpnovercloakprotocol.cpp +++ b/client/protocols/openvpnovercloakprotocol.cpp @@ -1,5 +1,4 @@ #include "openvpnovercloakprotocol.h" -#include "core/servercontroller.h" #include "utilities.h" #include "containers/containers_defs.h" diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 6fe20f28..fcce4ee6 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -1,8 +1,9 @@ #include #include #include -#include #include +#include +#include #include "debug.h" #include "defines.h" @@ -122,6 +123,21 @@ void OpenVpnProtocol::sendManagementCommand(const QString& command) } } +uint OpenVpnProtocol::selectMgmtPort() +{ + + for (int i = 0; i < 100; ++i) { + quint32 port = QRandomGenerator::global()->generate(); + port = (double)(65000-15001) * port / UINT32_MAX + 15001; + + QTcpServer s; + bool ok = s.listen(QHostAddress::LocalHost, port); + if (ok) return port; + } + + return m_managementPort; +} + void OpenVpnProtocol::updateRouteGateway(QString line) { // TODO: fix for macos @@ -132,24 +148,13 @@ void OpenVpnProtocol::updateRouteGateway(QString line) qDebug() << "Set VPN route gateway" << m_routeGateway; } -QString OpenVpnProtocol::openVpnExecPath() const -{ -#ifdef Q_OS_WIN - return Utils::executable("openvpn/openvpn", true); -#elif defined Q_OS_LINUX - return Utils::usrExecutable("openvpn"); -#else - return Utils::executable("/openvpn", true); -#endif -} - ErrorCode OpenVpnProtocol::start() { #ifndef Q_OS_IOS //qDebug() << "Start OpenVPN connection"; OpenVpnProtocol::stop(); - if (!QFileInfo::exists(openVpnExecPath())) { + if (!QFileInfo::exists(Utils::openVpnExecPath())) { setLastError(ErrorCode::OpenVpnExecutableMissing); return lastError(); } @@ -162,7 +167,10 @@ ErrorCode OpenVpnProtocol::start() // QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log"; // Utils::createEmptyFile(vpnLogFileNamePath); - if (!m_managementServer.start(m_managementHost, m_managementPort)) { + uint mgmtPort = selectMgmtPort(); + qDebug() << "OpenVpnProtocol::start mgmt port selected:" << mgmtPort; + + if (!m_managementServer.start(m_managementHost, mgmtPort)) { setLastError(ErrorCode::OpenVpnManagementServerError); return lastError(); } @@ -183,9 +191,9 @@ ErrorCode OpenVpnProtocol::start() setLastError(ErrorCode::AmneziaServiceConnectionFailed); return ErrorCode::AmneziaServiceConnectionFailed; } - m_openVpnProcess->setProgram(openVpnExecPath()); + m_openVpnProcess->setProgram(PermittedProcess::OpenVPN); QStringList arguments({"--config" , configPath(), - "--management", m_managementHost, QString::number(m_managementPort), + "--management", m_managementHost, QString::number(mgmtPort), "--management-client"/*, "--log", vpnLogFileNamePath */ }); m_openVpnProcess->setArguments(arguments); diff --git a/client/protocols/openvpnprotocol.h b/client/protocols/openvpnprotocol.h index 34cff8f4..ad80fe50 100644 --- a/client/protocols/openvpnprotocol.h +++ b/client/protocols/openvpnprotocol.h @@ -30,7 +30,6 @@ protected slots: private: QString configPath() const; - QString openVpnExecPath() const; bool openVpnProcessIsRunning() const; bool sendTermSignal(); void readOpenVpnConfiguration(const QJsonObject &configuration); @@ -47,6 +46,8 @@ private: QString m_configFileName; QTemporaryFile m_configFile; + uint selectMgmtPort(); + private: void updateRouteGateway(QString line); void updateVpnGateway(const QString &line); diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp index 5db84145..6957bc3d 100644 --- a/client/protocols/shadowsocksvpnprotocol.cpp +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -1,5 +1,4 @@ #include "shadowsocksvpnprotocol.h" -#include "core/servercontroller.h" #include "debug.h" #include "utilities.h" diff --git a/client/protocols/wireguardprotocol.cpp b/client/protocols/wireguardprotocol.cpp index 215f81ae..7f577ac5 100644 --- a/client/protocols/wireguardprotocol.cpp +++ b/client/protocols/wireguardprotocol.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -12,25 +11,20 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject* parent) : VpnProtocol(configuration, parent) { - //m_configFile.setFileTemplate(QDir::tempPath() + QDir::separator() + serviceName() + ".conf"); m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf"); readWireguardConfiguration(configuration); } WireguardProtocol::~WireguardProtocol() { - //qDebug() << "WireguardProtocol::~WireguardProtocol() 1"; WireguardProtocol::stop(); QThread::msleep(200); - //qDebug() << "WireguardProtocol::~WireguardProtocol() 2"; } void WireguardProtocol::stop() { - //qDebug() << "WireguardProtocol::stop() 1"; - #ifndef Q_OS_IOS - if (!QFileInfo::exists(wireguardExecPath())) { + if (!QFileInfo::exists(Utils::wireguardExecPath())) { qCritical() << "Wireguard executable missing!"; setLastError(ErrorCode::ExecutableMissing); return; @@ -51,7 +45,7 @@ void WireguardProtocol::stop() return; } - m_wireguardStopProcess->setProgram(wireguardExecPath()); + m_wireguardStopProcess->setProgram(PermittedProcess::Wireguard); QStringList arguments({"--remove", configPath()}); @@ -74,7 +68,6 @@ void WireguardProtocol::stop() setConnectionState(VpnProtocol::Disconnected); #endif - //qDebug() << "WireguardProtocol::stop() 2"; } void WireguardProtocol::readWireguardConfiguration(const QJsonObject &configuration) @@ -97,11 +90,6 @@ void WireguardProtocol::readWireguardConfiguration(const QJsonObject &configurat } -//bool WireguardProtocol::openVpnProcessIsRunning() const -//{ -// return Utils::processIsRunning("openvpn"); -//} - QString WireguardProtocol::configPath() const { return m_configFileName; @@ -117,31 +105,17 @@ void WireguardProtocol::updateRouteGateway(QString line) qDebug() << "Set VPN route gateway" << m_routeGateway; } -QString WireguardProtocol::wireguardExecPath() const -{ -#ifdef Q_OS_WIN - return Utils::executable("wireguard/wireguard-service", true); -#elif defined Q_OS_LINUX - return Utils::usrExecutable("wg"); -#else - return Utils::executable("/wireguard", true); -#endif -} - ErrorCode WireguardProtocol::start() { - //qDebug() << "WireguardProtocol::start() 1"; - #ifndef Q_OS_IOS if (!m_isConfigLoaded) { setLastError(ErrorCode::ConfigMissing); return lastError(); } - //qDebug() << "Start Wireguard connection"; WireguardProtocol::stop(); - if (!QFileInfo::exists(wireguardExecPath())) { + if (!QFileInfo::exists(Utils::wireguardExecPath())) { setLastError(ErrorCode::ExecutableMissing); return lastError(); } @@ -156,7 +130,6 @@ ErrorCode WireguardProtocol::start() m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess(); if (!m_wireguardStartProcess) { - //qWarning() << "IpcProcess replica is not created!"; setLastError(ErrorCode::AmneziaServiceConnectionFailed); return ErrorCode::AmneziaServiceConnectionFailed; } @@ -168,7 +141,7 @@ ErrorCode WireguardProtocol::start() return ErrorCode::AmneziaServiceConnectionFailed; } - m_wireguardStartProcess->setProgram(wireguardExecPath()); + m_wireguardStartProcess->setProgram(PermittedProcess::Wireguard); QStringList arguments({"--add", configPath()}); @@ -210,8 +183,6 @@ ErrorCode WireguardProtocol::start() m_wireguardStartProcess->start(); m_wireguardStartProcess->waitForFinished(10000); - //qDebug() << "WireguardProtocol::start() 2"; - return ErrorCode::NoError; #else return ErrorCode::NotImplementedError; diff --git a/client/protocols/wireguardprotocol.h b/client/protocols/wireguardprotocol.h index 8ed26c7d..3a091cac 100644 --- a/client/protocols/wireguardprotocol.h +++ b/client/protocols/wireguardprotocol.h @@ -23,8 +23,6 @@ public: private: QString configPath() const; - QString wireguardExecPath() const; - //bool openVpnProcessIsRunning() const; void readWireguardConfiguration(const QJsonObject &configuration); void updateRouteGateway(QString line); diff --git a/client/resources.qrc b/client/resources.qrc index a48a98f7..a7e67e39 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -159,5 +159,6 @@ ui/qml/Controls/SvgButtonType.qml ui/qml/Pages/PageQrDecoderIos.qml server_scripts/website_tor/Dockerfile + ui/qml/Pages/PageViewConfig.qml diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp new file mode 100644 index 00000000..e71eff48 --- /dev/null +++ b/client/secure_qsettings.cpp @@ -0,0 +1,263 @@ +#include "secure_qsettings.h" +#include "platforms/ios/MobileUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "utilities.h" +#include +#include "QAead.h" +#include "QBlockCipher.h" + +using namespace QKeychain; + +SecureQSettings::SecureQSettings(const QString &organization, const QString &application, QObject *parent) + : QObject{parent}, + m_settings(organization, application, parent), + encryptedKeys({"Servers/serversList"}) +{ + bool encrypted = m_settings.value("Conf/encrypted").toBool(); + + // convert settings to encrypted for if updated to >= 2.1.0 + if (encryptionRequired() && ! encrypted) { + for (const QString &key : m_settings.allKeys()) { + if (encryptedKeys.contains(key)) { + const QVariant &val = value(key); + setValue(key, val); + } + } + m_settings.setValue("Conf/encrypted", true); + m_settings.sync(); + } +} + +QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue) const +{ + QMutexLocker locker(&mutex); + + if (m_cache.contains(key)) { + return m_cache.value(key); + } + + if (!m_settings.contains(key)) return defaultValue; + + QVariant retVal; + + // check if value is not encrypted, v. < 2.0.x + retVal = m_settings.value(key); + if (retVal.isValid()) { + if (retVal.userType() == QVariant::ByteArray && + retVal.toByteArray().mid(0, magicString.size()) == magicString) { + + if (getEncKey().isEmpty() || getEncIv().isEmpty()) { + qCritical() << "SecureQSettings::setValue Decryption requested, but key is empty"; + return {}; + } + + QByteArray encryptedValue = retVal.toByteArray().mid(magicString.size()); + + QByteArray decryptedValue = decryptText(encryptedValue); + QDataStream ds(&decryptedValue, QIODevice::ReadOnly); + + ds >> retVal; + + if (!retVal.isValid()) { + qWarning() << "SecureQSettings::value settings decryption failed"; + retVal = QVariant(); + } + } + } + else { + qWarning() << "SecureQSettings::value invalid QVariant value"; + retVal = QVariant(); + } + + m_cache.insert(key, retVal); + return retVal; +} + +void SecureQSettings::setValue(const QString &key, const QVariant &value) +{ + QMutexLocker locker(&mutex); + + if (encryptionRequired() && encryptedKeys.contains(key)) { + if (!getEncKey().isEmpty() && !getEncIv().isEmpty()) { + QByteArray decryptedValue; + { + QDataStream ds(&decryptedValue, QIODevice::WriteOnly); + ds << value; + } + + QByteArray encryptedValue = encryptText(decryptedValue); + m_settings.setValue(key, magicString + encryptedValue); + } + else { + qCritical() << "SecureQSettings::setValue Encryption required, but key is empty"; + return; + } + + } + else { + m_settings.setValue(key, value); + } + + m_cache.insert(key, value); + sync(); +} + +void SecureQSettings::remove(const QString &key) +{ + QMutexLocker locker(&mutex); + + m_settings.remove(key); + m_cache.remove(key); + + sync(); +} + +void SecureQSettings::sync() +{ + m_settings.sync(); +} + +QByteArray SecureQSettings::backupAppConfig() const +{ + QJsonObject cfg; + + for (const QString &key : m_settings.allKeys()) { + cfg.insert(key, QJsonValue::fromVariant(value(key))); + } + + return QJsonDocument(cfg).toJson(); +} + +bool SecureQSettings::restoreAppConfig(const QByteArray &json) +{ + QJsonObject cfg = QJsonDocument::fromJson(json).object(); + if (cfg.isEmpty()) return false; + + for (const QString &key : cfg.keys()) { + setValue(key, cfg.value(key).toVariant()); + } + + sync(); + return true; +} + + +QByteArray SecureQSettings::encryptText(const QByteArray& value) const +{ + QSimpleCrypto::QBlockCipher cipher; + return cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv()); +} + +QByteArray SecureQSettings::decryptText(const QByteArray& ba) const +{ + QSimpleCrypto::QBlockCipher cipher; + return cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv()); +} + +bool SecureQSettings::encryptionRequired() const +{ +#ifdef Q_OS_LINUX + // QtKeyChain failing on Linux + return false; +#endif + return true; +} + +QByteArray SecureQSettings::getEncKey() const +{ + // load keys from system key storage + m_key = getSecTag(settingsKeyTag); + + if (m_key.isEmpty()) { + // Create new key + QSimpleCrypto::QBlockCipher cipher; + QByteArray key = cipher.generateSecureRandomBytes(32); + if (key.isEmpty()) { + qCritical() << "SecureQSettings::getEncKey Unable to generate new enc key"; + } + + setSecTag(settingsKeyTag, key); + + // check + m_key = getSecTag(settingsKeyTag); + if (key != m_key) { + qCritical() << "SecureQSettings::getEncKey Unable to store key in keychain" << key.size() << m_key.size(); + return {}; + } + } + + return m_key; +} + +QByteArray SecureQSettings::getEncIv() const +{ + // load keys from system key storage + m_iv = getSecTag(settingsIvTag); + + if (m_iv.isEmpty()) { + // Create new IV + QSimpleCrypto::QBlockCipher cipher; + QByteArray iv = cipher.generateSecureRandomBytes(32); + if (iv.isEmpty()) { + qCritical() << "SecureQSettings::getEncIv Unable to generate new enc IV"; + } + setSecTag(settingsIvTag, iv); + + // check + m_iv = getSecTag(settingsIvTag); + if (iv != m_iv) { + qCritical() << "SecureQSettings::getEncIv Unable to store IV in keychain" << iv.size() << m_iv.size(); + return {}; + } + } + + return m_iv; +} + +QByteArray SecureQSettings::getSecTag(const QString &tag) +{ + auto job = QSharedPointer(new ReadPasswordJob(keyChainName), &QObject::deleteLater); + job->setAutoDelete(false); + job->setKey(tag); + QEventLoop loop; + job->connect(job.data(), &ReadPasswordJob::finished, job.data(), [&loop](){ + loop.quit(); + }); + job->start(); + loop.exec(); + + if ( job->error() ) { + qCritical() << "SecureQSettings::getSecTag Error:" << job->errorString(); + } + + return job->binaryData(); +} + +void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data) +{ + auto job = QSharedPointer(new WritePasswordJob(keyChainName), &QObject::deleteLater); + job->setAutoDelete(false); + job->setKey(tag); + job->setBinaryData(data); + QEventLoop loop; + QTimer::singleShot(1000, &loop, SLOT(quit())); + job->connect(job.data(), &WritePasswordJob::finished, job.data(), [&loop](){ + loop.quit(); + }); + job->start(); + loop.exec(); + + if (job->error()) { + qCritical() << "SecureQSettings::setSecTag Error:" << job->errorString(); + } +} + + diff --git a/client/secure_qsettings.h b/client/secure_qsettings.h new file mode 100644 index 00000000..9b1f6167 --- /dev/null +++ b/client/secure_qsettings.h @@ -0,0 +1,56 @@ +#ifndef SECUREQSETTINGS_H +#define SECUREQSETTINGS_H + +#include +#include +#include +#include + +#include "keychain.h" + + +constexpr const char* settingsKeyTag = "settingsKeyTag"; +constexpr const char* settingsIvTag = "settingsIvTag"; +constexpr const char* keyChainName = "AmneziaVPN-Keychain"; + + +class SecureQSettings : public QObject +{ +public: + explicit SecureQSettings(const QString &organization, const QString &application = QString(), QObject *parent = nullptr); + + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + void setValue(const QString &key, const QVariant &value); + void remove(const QString &key); + void sync(); + + QByteArray backupAppConfig() const; + bool restoreAppConfig(const QByteArray &json); + + QByteArray encryptText(const QByteArray &value) const; + QByteArray decryptText(const QByteArray& ba) const; + + bool encryptionRequired() const; + + QByteArray getEncKey() const; + QByteArray getEncIv() const; + + static QByteArray getSecTag(const QString &tag); + static void setSecTag(const QString &tag, const QByteArray &data); + +private: + QSettings m_settings; + + mutable QMap m_cache; + + QStringList encryptedKeys; // encode only key listed here + + mutable QByteArray m_key; + mutable QByteArray m_iv; + + const QByteArray magicString { "EncData" }; // Magic keyword used for mark encrypted QByteArray + + mutable QMutex mutex; +}; + +#endif // SECUREQSETTINGS_H diff --git a/client/settings.cpp b/client/settings.cpp index 9dea8cc3..9fea6b68 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -2,15 +2,15 @@ #include "settings.h" #include "utilities.h" -#include #include "containers/containers_defs.h" const char Settings::cloudFlareNs1[] = "1.1.1.1"; const char Settings::cloudFlareNs2[] = "1.0.0.1"; + Settings::Settings(QObject* parent) : QObject(parent), - m_settings (ORGANIZATION_NAME, APPLICATION_NAME, this) + m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this) { // Import old settings if (serversCount() == 0) { @@ -178,7 +178,6 @@ void Settings::clearLastConnectionConfig(int serverIndex, DockerContainer contai QJsonObject c = protocolConfig(serverIndex, container, proto); c.remove(config_key::last_config); setProtocolConfig(serverIndex, container, proto, c); - qDebug() << "Settings::clearLastConnectionConfig for" << serverIndex << container << proto; } bool Settings::haveAuthData(int serverIndex) const @@ -292,60 +291,10 @@ void Settings::removeVpnSites(RouteMode mode, const QStringList &sites) setVpnSites(mode, sitesMap); } -//void Settings::addVpnForwardSite(const QString &site, const QString &ip) -//{ -// auto sites = vpnForwardSites(); -// QStringList l = sites.value(site).toStringList(); -// if (!l.contains(ip)) { -// l.append(ip); -// setVpnForwardSites(sites); -// } -//} - -//QStringList Settings::getVpnForwardIps() const -//{ -// QStringList ips; -// const QVariantMap &m = vpnForwardSites(); -// for (const QVariant &v : m) { -// ips.append(v.toStringList()); -// } -// ips.removeDuplicates(); -// return ips; -//} - -//void Settings::addVpnExceptSite(const QString &site, const QString &ip) -//{ -// auto sites = vpnExceptSites(); -// QStringList l = sites.value(site).toStringList(); -// if (!l.contains(ip)) { -// l.append(ip); -// setVpnExceptSites(sites); -// } -//} - -//QStringList Settings::getVpnExceptIps() const -//{ -// QStringList ips; -// const QVariantMap &m = vpnExceptSites(); -// for (const QVariant &v : m) { -// ips.append(v.toStringList()); -// } -// ips.removeDuplicates(); -// return ips; -//} - QString Settings::primaryDns() const { return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString(); } QString Settings::secondaryDns() const { return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString(); } -//void Settings::setServerCredentials(const ServerCredentials &credentials) -//{ -// setServerName(credentials.hostName); -// setServerPort(credentials.port); -// setUserName(credentials.userName); -// setPassword(credentials.password); -//} - ServerCredentials Settings::defaultServerCredentials() const { return serverCredentials(defaultServerIndex()); diff --git a/client/settings.h b/client/settings.h index d5cb1639..c78b9a79 100644 --- a/client/settings.h +++ b/client/settings.h @@ -11,6 +11,7 @@ #include "core/defs.h" #include "containers/containers_defs.h" +#include "secure_qsettings.h" using namespace amnezia; @@ -25,7 +26,6 @@ public: ServerCredentials defaultServerCredentials() const; ServerCredentials serverCredentials(int index) const; - //void setServerCredentials(const ServerCredentials &credentials); QJsonArray serversArray() const { return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array(); } void setServersArray(const QJsonArray &servers) { m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson()); } @@ -110,13 +110,11 @@ public: // static constexpr char openNicNs5[] = "94.103.153.176"; // static constexpr char openNicNs13[] = "144.76.103.143"; - -public: - - + QByteArray backupAppConfig() const { return m_settings.backupAppConfig(); } + bool restoreAppConfig(const QByteArray &cfg) { return m_settings.restoreAppConfig(cfg); } private: - QSettings m_settings; + SecureQSettings m_settings; }; diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index 15fc8f8d..5468452e 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -1,6 +1,7 @@ #include "containers_model.h" -ContainersModel::ContainersModel(QObject *parent) : +ContainersModel::ContainersModel(std::shared_ptr settings, QObject *parent) : + m_settings(settings), QAbstractListModel(parent) { @@ -37,13 +38,13 @@ QVariant ContainersModel::data(const QModelIndex &index, int role) const return ContainerProps::containerDescriptions().value(c); } if (role == DefaultRole) { - return c == m_settings.defaultContainer(m_selectedServerIndex); + return c == m_settings->defaultContainer(m_selectedServerIndex); } if (role == ServiceTypeRole) { return ContainerProps::containerService(c); } if (role == IsInstalledRole) { - return m_settings.containers(m_selectedServerIndex).contains(c); + return m_settings->containers(m_selectedServerIndex).contains(c); } return QVariant(); } diff --git a/client/ui/models/containers_model.h b/client/ui/models/containers_model.h index 479690b0..36206855 100644 --- a/client/ui/models/containers_model.h +++ b/client/ui/models/containers_model.h @@ -13,7 +13,7 @@ class ContainersModel : public QAbstractListModel { Q_OBJECT public: - ContainersModel(QObject *parent = nullptr); + ContainersModel(std::shared_ptr settings, QObject *parent = nullptr); public: enum SiteRoles { NameRole = Qt::UserRole + 1, @@ -33,7 +33,7 @@ protected: private: int m_selectedServerIndex; - Settings m_settings; + std::shared_ptr m_settings; }; #endif // CONTAINERS_MODEL_H diff --git a/client/ui/models/protocols_model.cpp b/client/ui/models/protocols_model.cpp index 76e5c623..7359bb36 100644 --- a/client/ui/models/protocols_model.cpp +++ b/client/ui/models/protocols_model.cpp @@ -1,6 +1,7 @@ #include "protocols_model.h" -ProtocolsModel::ProtocolsModel(QObject *parent) : +ProtocolsModel::ProtocolsModel(std::shared_ptr settings, QObject *parent) : + m_settings(settings), QAbstractListModel(parent) { diff --git a/client/ui/models/protocols_model.h b/client/ui/models/protocols_model.h index bd271558..48b6eeb6 100644 --- a/client/ui/models/protocols_model.h +++ b/client/ui/models/protocols_model.h @@ -13,7 +13,7 @@ class ProtocolsModel : public QAbstractListModel { Q_OBJECT public: - ProtocolsModel(QObject *parent = nullptr); + ProtocolsModel(std::shared_ptr settings, QObject *parent = nullptr); public: enum SiteRoles { NameRole = Qt::UserRole + 1, @@ -34,7 +34,7 @@ protected: private: int m_selectedServerIndex; DockerContainer m_selectedDockerContainer; - Settings m_settings; + std::shared_ptr m_settings; }; #endif // PROTOCOLS_MODEL_H diff --git a/client/ui/models/sites_model.cpp b/client/ui/models/sites_model.cpp index 9fc5452d..fe0f4ccf 100644 --- a/client/ui/models/sites_model.cpp +++ b/client/ui/models/sites_model.cpp @@ -1,7 +1,8 @@ #include "sites_model.h" -SitesModel::SitesModel(Settings::RouteMode mode, QObject *parent) +SitesModel::SitesModel(std::shared_ptr settings, Settings::RouteMode mode, QObject *parent) : QAbstractListModel(parent), + m_settings(settings), m_mode(mode) { } @@ -68,7 +69,7 @@ void SitesModel::genCache() const qDebug() << "SitesModel::genCache"; m_ipsCache.clear(); - const QVariantMap &sites = m_settings.vpnSites(m_mode); + const QVariantMap &sites = m_settings->vpnSites(m_mode); auto i = sites.constBegin(); while (i != sites.constEnd()) { m_ipsCache.append(qMakePair(i.key(), i.value().toString())); diff --git a/client/ui/models/sites_model.h b/client/ui/models/sites_model.h index 5e4feb0f..7bf04b50 100644 --- a/client/ui/models/sites_model.h +++ b/client/ui/models/sites_model.h @@ -15,7 +15,7 @@ public: IpRole }; - explicit SitesModel(Settings::RouteMode mode, QObject *parent = nullptr); + explicit SitesModel(std::shared_ptr settings, Settings::RouteMode mode, QObject *parent = nullptr); void resetCache(); // Basic functionality: @@ -32,7 +32,7 @@ private: private: Settings::RouteMode m_mode; - Settings m_settings; + std::shared_ptr m_settings; mutable QVector> m_ipsCache; mutable bool m_cacheReady = false; diff --git a/client/ui/pages.h b/client/ui/pages.h index d74c64a6..69f417fa 100644 --- a/client/ui/pages.h +++ b/client/ui/pages.h @@ -24,7 +24,7 @@ enum class Page {Start = 0, NewServer, NewServerProtocols, Vpn, Wizard, WizardLow, WizardMedium, WizardHigh, WizardVpnMode, ServerConfiguringProgress, GeneralSettings, AppSettings, NetworkSettings, ServerSettings, ServerContainers, ServersList, ShareConnection, Sites, - ProtocolSettings, ProtocolShare, QrDecoder, QrDecoderIos, About}; + ProtocolSettings, ProtocolShare, QrDecoder, QrDecoderIos, About, ViewConfig}; Q_ENUM_NS(Page) static void declareQmlPageEnum() { diff --git a/client/ui/pages_logic/AppSettingsLogic.cpp b/client/ui/pages_logic/AppSettingsLogic.cpp index 6429fc96..b2907c3d 100644 --- a/client/ui/pages_logic/AppSettingsLogic.cpp +++ b/client/ui/pages_logic/AppSettingsLogic.cpp @@ -7,6 +7,7 @@ #include #include +#include #include using namespace amnezia; @@ -25,9 +26,9 @@ AppSettingsLogic::AppSettingsLogic(UiLogic *logic, QObject *parent): void AppSettingsLogic::onUpdatePage() { set_checkBoxAutostartChecked(Autostart::isAutostart()); - set_checkBoxAutoConnectChecked(m_settings.isAutoConnect()); - set_checkBoxStartMinimizedChecked(m_settings.isStartMinimized()); - set_checkBoxSaveLogsChecked(m_settings.isSaveLogs()); + set_checkBoxAutoConnectChecked(m_settings->isAutoConnect()); + set_checkBoxStartMinimizedChecked(m_settings->isStartMinimized()); + set_checkBoxSaveLogsChecked(m_settings->isSaveLogs()); QString ver = QString("%1: %2 (%3)") .arg(tr("Software version")) @@ -46,17 +47,17 @@ void AppSettingsLogic::onCheckBoxAutostartToggled(bool checked) void AppSettingsLogic::onCheckBoxAutoconnectToggled(bool checked) { - m_settings.setAutoConnect(checked); + m_settings->setAutoConnect(checked); } void AppSettingsLogic::onCheckBoxStartMinimizedToggled(bool checked) { - m_settings.setStartMinimized(checked); + m_settings->setStartMinimized(checked); } void AppSettingsLogic::onCheckBoxSaveLogsCheckedToggled(bool checked) { - m_settings.setSaveLogs(checked); + m_settings->setSaveLogs(checked); } void AppSettingsLogic::onPushButtonOpenLogsClicked() @@ -74,3 +75,32 @@ void AppSettingsLogic::onPushButtonClearLogsClicked() Debug::clearLogs(); Debug::clearServiceLogs(); } + +void AppSettingsLogic::onPushButtonBackupAppConfigClicked() +{ + uiLogic()->saveTextFile("Backup application config", "AmneziaVPN.backup", ".backup", m_settings->backupAppConfig()); +} + +void AppSettingsLogic::onPushButtonRestoreAppConfigClicked() +{ + QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open backup"), + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.backup"); + + if (fileName.isEmpty()) return; + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QByteArray data = file.readAll(); + + bool ok = m_settings->restoreAppConfig(data); + if (ok) { + emit uiLogic()->goToPage(Page::Vpn); + emit uiLogic()->setStartPage(Page::Vpn); + } + else { + QMessageBox::warning(nullptr, APPLICATION_NAME, + tr("Can't import config, file is corrupted.")); + } + +} + diff --git a/client/ui/pages_logic/AppSettingsLogic.h b/client/ui/pages_logic/AppSettingsLogic.h index b597b129..fc9f0da7 100644 --- a/client/ui/pages_logic/AppSettingsLogic.h +++ b/client/ui/pages_logic/AppSettingsLogic.h @@ -25,6 +25,10 @@ public: Q_INVOKABLE void onPushButtonExportLogsClicked(); Q_INVOKABLE void onPushButtonClearLogsClicked(); + Q_INVOKABLE void onPushButtonBackupAppConfigClicked(); + Q_INVOKABLE void onPushButtonRestoreAppConfigClicked(); + + public: explicit AppSettingsLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~AppSettingsLogic() = default; diff --git a/client/ui/pages_logic/GeneralSettingsLogic.cpp b/client/ui/pages_logic/GeneralSettingsLogic.cpp index 92696afa..a71a7f91 100644 --- a/client/ui/pages_logic/GeneralSettingsLogic.cpp +++ b/client/ui/pages_logic/GeneralSettingsLogic.cpp @@ -12,28 +12,28 @@ GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent): void GeneralSettingsLogic::onUpdatePage() { - uiLogic()->selectedServerIndex = m_settings.defaultServerIndex(); - uiLogic()->selectedDockerContainer = m_settings.defaultContainer(m_settings.defaultServerIndex()); + uiLogic()->selectedServerIndex = m_settings->defaultServerIndex(); + uiLogic()->selectedDockerContainer = m_settings->defaultContainer(m_settings->defaultServerIndex()); - set_pushButtonGeneralSettingsShareConnectionEnable(m_settings.haveAuthData(m_settings.defaultServerIndex())); + set_pushButtonGeneralSettingsShareConnectionEnable(m_settings->haveAuthData(m_settings->defaultServerIndex())); } void GeneralSettingsLogic::onPushButtonGeneralSettingsServerSettingsClicked() { - uiLogic()->selectedServerIndex = m_settings.defaultServerIndex(); - uiLogic()->selectedDockerContainer = m_settings.defaultContainer(m_settings.defaultServerIndex()); + uiLogic()->selectedServerIndex = m_settings->defaultServerIndex(); + uiLogic()->selectedDockerContainer = m_settings->defaultContainer(m_settings->defaultServerIndex()); emit uiLogic()->goToPage(Page::ServerSettings); } void GeneralSettingsLogic::onPushButtonGeneralSettingsShareConnectionClicked() { - uiLogic()->selectedServerIndex = m_settings.defaultServerIndex(); - uiLogic()->selectedDockerContainer = m_settings.defaultContainer(uiLogic()->selectedServerIndex); + uiLogic()->selectedServerIndex = m_settings->defaultServerIndex(); + uiLogic()->selectedDockerContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex); qobject_cast(uiLogic()->protocolsModel())->setSelectedServerIndex(uiLogic()->selectedServerIndex); qobject_cast(uiLogic()->protocolsModel())->setSelectedDockerContainer(uiLogic()->selectedDockerContainer); - uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + uiLogic()->pageLogic()->updateSharingPage(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); emit uiLogic()->goToPage(Page::ShareConnection); } diff --git a/client/ui/pages_logic/NetworkSettingsLogic.cpp b/client/ui/pages_logic/NetworkSettingsLogic.cpp index 6b85b89a..fa49df38 100644 --- a/client/ui/pages_logic/NetworkSettingsLogic.cpp +++ b/client/ui/pages_logic/NetworkSettingsLogic.cpp @@ -2,6 +2,7 @@ #include "defines.h" #include "utilities.h" +#include "settings.h" NetworkSettingsLogic::NetworkSettingsLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), @@ -13,39 +14,39 @@ NetworkSettingsLogic::NetworkSettingsLogic(UiLogic *logic, QObject *parent): void NetworkSettingsLogic::onUpdatePage() { - set_checkBoxUseAmneziaDnsChecked(m_settings.useAmneziaDns()); + set_checkBoxUseAmneziaDnsChecked(m_settings->useAmneziaDns()); - set_lineEditDns1Text(m_settings.primaryDns()); - set_lineEditDns2Text(m_settings.secondaryDns()); + set_lineEditDns1Text(m_settings->primaryDns()); + set_lineEditDns2Text(m_settings->secondaryDns()); } void NetworkSettingsLogic::onLineEditDns1EditFinished(const QString &text) { if (ipAddressRegex().match(text).hasMatch()) { - m_settings.setPrimaryDns(text); + m_settings->setPrimaryDns(text); } } void NetworkSettingsLogic::onLineEditDns2EditFinished(const QString &text) { if (ipAddressRegex().match(text).hasMatch()) { - m_settings.setSecondaryDns(text); + m_settings->setSecondaryDns(text); } } void NetworkSettingsLogic::onPushButtonResetDns1Clicked() { - m_settings.setPrimaryDns(m_settings.cloudFlareNs1); + m_settings->setPrimaryDns(m_settings->cloudFlareNs1); onUpdatePage(); } void NetworkSettingsLogic::onPushButtonResetDns2Clicked() { - m_settings.setSecondaryDns(m_settings.cloudFlareNs2); + m_settings->setSecondaryDns(m_settings->cloudFlareNs2); onUpdatePage(); } void NetworkSettingsLogic::onCheckBoxUseAmneziaDnsToggled(bool checked) { - m_settings.setUseAmneziaDns(checked); + m_settings->setUseAmneziaDns(checked); } diff --git a/client/ui/pages_logic/NetworkSettingsLogic.h b/client/ui/pages_logic/NetworkSettingsLogic.h index b1a32275..237706eb 100644 --- a/client/ui/pages_logic/NetworkSettingsLogic.h +++ b/client/ui/pages_logic/NetworkSettingsLogic.h @@ -3,6 +3,8 @@ #include "PageLogicBase.h" +#include + class UiLogic; class NetworkSettingsLogic : public PageLogicBase diff --git a/client/ui/pages_logic/NewServerProtocolsLogic.h b/client/ui/pages_logic/NewServerProtocolsLogic.h index a06e6590..abf3d102 100644 --- a/client/ui/pages_logic/NewServerProtocolsLogic.h +++ b/client/ui/pages_logic/NewServerProtocolsLogic.h @@ -2,6 +2,7 @@ #define NEW_SERVER_PROTOCOLS_LOGIC_H #include "PageLogicBase.h" +#include "containers/containers_defs.h" class UiLogic; diff --git a/client/ui/pages_logic/PageLogicBase.cpp b/client/ui/pages_logic/PageLogicBase.cpp index f0638213..9274ef49 100644 --- a/client/ui/pages_logic/PageLogicBase.cpp +++ b/client/ui/pages_logic/PageLogicBase.cpp @@ -1,11 +1,17 @@ #include "PageLogicBase.h" +#include "ui/uilogic.h" +#include "settings.h" +#include "configurators/vpn_configurator.h" PageLogicBase::PageLogicBase(UiLogic *logic, QObject *parent): QObject(parent), m_pageEnabled{true}, m_uiLogic(logic) { - + m_settings = logic->m_settings; + m_configurator = logic->m_configurator; + m_serverController = logic->m_serverController; } + diff --git a/client/ui/pages_logic/PageLogicBase.h b/client/ui/pages_logic/PageLogicBase.h index 26858ee0..887b0ec7 100644 --- a/client/ui/pages_logic/PageLogicBase.h +++ b/client/ui/pages_logic/PageLogicBase.h @@ -1,14 +1,15 @@ #ifndef PAGE_LOGIC_BASE_H #define PAGE_LOGIC_BASE_H -#include "settings.h" #include "../pages.h" #include "../property_helper.h" -using namespace amnezia; using namespace PageEnumNS; class UiLogic; +class Settings; +class VpnConfigurator; +class ServerController; class PageLogicBase : public QObject { @@ -22,10 +23,12 @@ public: Q_INVOKABLE virtual void onUpdatePage() {} protected: + UiLogic *m_uiLogic; UiLogic *uiLogic() const { return m_uiLogic; } - Settings m_settings; - UiLogic *m_uiLogic; + std::shared_ptr m_settings; + std::shared_ptr m_configurator; + std::shared_ptr m_serverController; signals: void updatePage(); diff --git a/client/ui/pages_logic/QrDecoderLogic.cpp b/client/ui/pages_logic/QrDecoderLogic.cpp index 01c607f7..0c24ca1c 100644 --- a/client/ui/pages_logic/QrDecoderLogic.cpp +++ b/client/ui/pages_logic/QrDecoderLogic.cpp @@ -54,7 +54,7 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code) data.append(m_chunks.value(i)); } - bool ok = uiLogic()->startPageLogic()->importConnectionFromQr(data); + bool ok = uiLogic()->pageLogic()->importConnectionFromQr(data); if (ok) { set_detectingEnabled(false); emit stopDecode(); @@ -67,7 +67,7 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code) } } else { - bool ok = uiLogic()->startPageLogic()->importConnectionFromQr(ba); + bool ok = uiLogic()->pageLogic()->importConnectionFromQr(ba); if (ok) { set_detectingEnabled(false); emit stopDecode(); diff --git a/client/ui/pages_logic/ServerConfiguringProgressLogic.h b/client/ui/pages_logic/ServerConfiguringProgressLogic.h index 9620d4c3..3ca94878 100644 --- a/client/ui/pages_logic/ServerConfiguringProgressLogic.h +++ b/client/ui/pages_logic/ServerConfiguringProgressLogic.h @@ -3,6 +3,9 @@ #include #include "PageLogicBase.h" +#include "core/defs.h" + +using namespace amnezia; class UiLogic; diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index e01e1bac..27f865b5 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -36,64 +36,64 @@ void ServerContainersLogic::onPushButtonProtoSettingsClicked(DockerContainer c, { qDebug()<< "ServerContainersLogic::onPushButtonProtoSettingsClicked" << c << p; uiLogic()->selectedDockerContainer = c; - uiLogic()->protocolLogic(p)->updateProtocolPage(m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, p), + uiLogic()->protocolLogic(p)->updateProtocolPage(m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, p), uiLogic()->selectedDockerContainer, - m_settings.haveAuthData(uiLogic()->selectedServerIndex)); + m_settings->haveAuthData(uiLogic()->selectedServerIndex)); emit uiLogic()->goToProtocolPage(p); } void ServerContainersLogic::onPushButtonDefaultClicked(DockerContainer c) { - if (m_settings.defaultContainer(uiLogic()->selectedServerIndex) == c) return; + if (m_settings->defaultContainer(uiLogic()->selectedServerIndex) == c) return; - m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, c); + m_settings->setDefaultContainer(uiLogic()->selectedServerIndex, c); uiLogic()->onUpdateAllPages(); - if (uiLogic()->selectedServerIndex != m_settings.defaultServerIndex()) return; + if (uiLogic()->selectedServerIndex != m_settings->defaultServerIndex()) return; if (!uiLogic()->m_vpnConnection) return; if (!uiLogic()->m_vpnConnection->isConnected()) return; - uiLogic()->vpnLogic()->onDisconnect(); - uiLogic()->vpnLogic()->onConnect(); + uiLogic()->pageLogic()->onDisconnect(); + uiLogic()->pageLogic()->onConnect(); } void ServerContainersLogic::onPushButtonShareClicked(DockerContainer c) { - uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, c); + uiLogic()->pageLogic()->updateSharingPage(uiLogic()->selectedServerIndex, c); emit uiLogic()->goToPage(Page::ShareConnection); } void ServerContainersLogic::onPushButtonRemoveClicked(DockerContainer container) { //buttonSetEnabledFunc(false); - ErrorCode e = ServerController::removeContainer(m_settings.serverCredentials(uiLogic()->selectedServerIndex), container); - m_settings.removeContainerConfig(uiLogic()->selectedServerIndex, container); + ErrorCode e = m_serverController->removeContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex), container); + m_settings->removeContainerConfig(uiLogic()->selectedServerIndex, container); //buttonSetEnabledFunc(true); - if (m_settings.defaultContainer(uiLogic()->selectedServerIndex) == container) { - const auto &c = m_settings.containers(uiLogic()->selectedServerIndex); - if (c.isEmpty()) m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, DockerContainer::None); - else m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, c.keys().first()); + if (m_settings->defaultContainer(uiLogic()->selectedServerIndex) == container) { + const auto &c = m_settings->containers(uiLogic()->selectedServerIndex); + if (c.isEmpty()) m_settings->setDefaultContainer(uiLogic()->selectedServerIndex, DockerContainer::None); + else m_settings->setDefaultContainer(uiLogic()->selectedServerIndex, c.keys().first()); } uiLogic()->onUpdateAllPages(); } void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int port, TransportProto tp) { - QJsonObject config = ServerController::createContainerInitialConfig(c, port, tp); + QJsonObject config = m_serverController->createContainerInitialConfig(c, port, tp); emit uiLogic()->goToPage(Page::ServerConfiguringProgress); qApp->processEvents(); - ErrorCode e = uiLogic()->serverConfiguringProgressLogic()->doInstallAction([this, c, &config](){ - return ServerController::setupContainer(m_settings.serverCredentials(uiLogic()->selectedServerIndex), c, config); + ErrorCode e = uiLogic()->pageLogic()->doInstallAction([this, c, &config](){ + return m_serverController->setupContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex), c, config); }); if (!e) { - m_settings.setContainerConfig(uiLogic()->selectedServerIndex, c, config); + m_settings->setContainerConfig(uiLogic()->selectedServerIndex, c, config); if (ContainerProps::containerService(c) == ServiceType::Vpn) { - m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, c); + m_settings->setDefaultContainer(uiLogic()->selectedServerIndex, c); } } diff --git a/client/ui/pages_logic/ServerContainersLogic.h b/client/ui/pages_logic/ServerContainersLogic.h index a49882fa..3f2a26cd 100644 --- a/client/ui/pages_logic/ServerContainersLogic.h +++ b/client/ui/pages_logic/ServerContainersLogic.h @@ -2,6 +2,7 @@ #define SERVER_CONTAINERS_LOGIC_H #include "PageLogicBase.h" +#include "containers/containers_defs.h" class UiLogic; diff --git a/client/ui/pages_logic/ServerListLogic.cpp b/client/ui/pages_logic/ServerListLogic.cpp index 05cac928..fe3f6512 100644 --- a/client/ui/pages_logic/ServerListLogic.cpp +++ b/client/ui/pages_logic/ServerListLogic.cpp @@ -13,7 +13,7 @@ ServerListLogic::ServerListLogic(UiLogic *logic, QObject *parent): void ServerListLogic::onServerListPushbuttonDefaultClicked(int index) { - m_settings.setDefaultServer(index); + m_settings->setDefaultServer(index); uiLogic()->onUpdateAllPages(); } @@ -25,8 +25,8 @@ void ServerListLogic::onServerListPushbuttonSettingsClicked(int index) void ServerListLogic::onUpdatePage() { - const QJsonArray &servers = m_settings.serversArray(); - int defaultServer = m_settings.defaultServerIndex(); + const QJsonArray &servers = m_settings->serversArray(); + int defaultServer = m_settings->defaultServerIndex(); std::vector serverListContent; for(int i = 0; i < servers.size(); i++) { ServerModelContent c; diff --git a/client/ui/pages_logic/ServerSettingsLogic.cpp b/client/ui/pages_logic/ServerSettingsLogic.cpp index e15207b7..3b123174 100644 --- a/client/ui/pages_logic/ServerSettingsLogic.cpp +++ b/client/ui/pages_logic/ServerSettingsLogic.cpp @@ -10,6 +10,12 @@ #include #include +#if defined(Q_OS_ANDROID) +#include +#include +#include +#endif + ServerSettingsLogic::ServerSettingsLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), m_labelWaitInfoVisible{true}, @@ -26,10 +32,10 @@ void ServerSettingsLogic::onUpdatePage() { set_labelWaitInfoVisible(false); set_labelWaitInfoText(""); - set_pushButtonClearVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex)); - set_pushButtonClearClientCacheVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex)); - set_pushButtonShareFullVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex)); - const QJsonObject &server = m_settings.server(uiLogic()->selectedServerIndex); + set_pushButtonClearVisible(m_settings->haveAuthData(uiLogic()->selectedServerIndex)); + set_pushButtonClearClientCacheVisible(m_settings->haveAuthData(uiLogic()->selectedServerIndex)); + set_pushButtonShareFullVisible(m_settings->haveAuthData(uiLogic()->selectedServerIndex)); + const QJsonObject &server = m_settings->server(uiLogic()->selectedServerIndex); const QString &port = server.value(config_key::port).toString(); set_labelServerText(QString("%1@%2%3%4") .arg(server.value(config_key::userName).toString()) @@ -38,7 +44,7 @@ void ServerSettingsLogic::onUpdatePage() .arg(port)); set_lineEditDescriptionText(server.value(config_key::description).toString()); - DockerContainer selectedContainer = m_settings.defaultContainer(uiLogic()->selectedServerIndex); + DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex); QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer); set_labelCurrentVpnProtocolText(tr("Service: ") + selectedContainerName); } @@ -48,14 +54,14 @@ void ServerSettingsLogic::onPushButtonClearServer() set_pageEnabled(false); set_pushButtonClearText(tr("Uninstalling Amnezia software...")); - if (m_settings.defaultServerIndex() == uiLogic()->selectedServerIndex) { - uiLogic()->vpnLogic()->onDisconnect(); + if (m_settings->defaultServerIndex() == uiLogic()->selectedServerIndex) { + uiLogic()->pageLogic()->onDisconnect(); } - ErrorCode e = ServerController::removeAllContainers(m_settings.serverCredentials(uiLogic()->selectedServerIndex)); - ServerController::disconnectFromHost(m_settings.serverCredentials(uiLogic()->selectedServerIndex)); + ErrorCode e = m_serverController->removeAllContainers(m_settings->serverCredentials(uiLogic()->selectedServerIndex)); + m_serverController->disconnectFromHost(m_settings->serverCredentials(uiLogic()->selectedServerIndex)); if (e) { - uiLogic()->setDialogConnectErrorText( + uiLogic()->set_dialogConnectErrorText( tr("Error occurred while configuring server.") + "\n" + errorString(e) + "\n" + tr("See logs for details.")); @@ -66,8 +72,8 @@ void ServerSettingsLogic::onPushButtonClearServer() set_labelWaitInfoText(tr("Amnezia server successfully uninstalled")); } - m_settings.setContainers(uiLogic()->selectedServerIndex, {}); - m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, DockerContainer::None); + m_settings->setContainers(uiLogic()->selectedServerIndex, {}); + m_settings->setDefaultContainer(uiLogic()->selectedServerIndex, DockerContainer::None); set_pageEnabled(true); set_pushButtonClearText(tr("Clear server from Amnezia software")); @@ -75,27 +81,27 @@ void ServerSettingsLogic::onPushButtonClearServer() void ServerSettingsLogic::onPushButtonForgetServer() { - if (m_settings.defaultServerIndex() == uiLogic()->selectedServerIndex && uiLogic()->m_vpnConnection->isConnected()) { - uiLogic()->vpnLogic()->onDisconnect(); + if (m_settings->defaultServerIndex() == uiLogic()->selectedServerIndex && uiLogic()->m_vpnConnection->isConnected()) { + uiLogic()->pageLogic()->onDisconnect(); } - m_settings.removeServer(uiLogic()->selectedServerIndex); + m_settings->removeServer(uiLogic()->selectedServerIndex); - if (m_settings.defaultServerIndex() == uiLogic()->selectedServerIndex) { - m_settings.setDefaultServer(0); + if (m_settings->defaultServerIndex() == uiLogic()->selectedServerIndex) { + m_settings->setDefaultServer(0); } - else if (m_settings.defaultServerIndex() > uiLogic()->selectedServerIndex) { - m_settings.setDefaultServer(m_settings.defaultServerIndex() - 1); + else if (m_settings->defaultServerIndex() > uiLogic()->selectedServerIndex) { + m_settings->setDefaultServer(m_settings->defaultServerIndex() - 1); } - if (m_settings.serversCount() == 0) { - m_settings.setDefaultServer(-1); + if (m_settings->serversCount() == 0) { + m_settings->setDefaultServer(-1); } uiLogic()->selectedServerIndex = -1; uiLogic()->onUpdateAllPages(); - if (m_settings.serversCount() == 0) { + if (m_settings->serversCount() == 0) { uiLogic()->setStartPage(Page::Start); } else { @@ -107,9 +113,9 @@ void ServerSettingsLogic::onPushButtonClearClientCacheClicked() { set_pushButtonClearClientCacheText(tr("Cache cleared")); - const auto &containers = m_settings.containers(uiLogic()->selectedServerIndex); + const auto &containers = m_settings->containers(uiLogic()->selectedServerIndex); for (DockerContainer container: containers.keys()) { - m_settings.clearLastConnectionConfig(uiLogic()->selectedServerIndex, container); + m_settings->clearLastConnectionConfig(uiLogic()->selectedServerIndex, container); } QTimer::singleShot(3000, this, [this]() { @@ -120,14 +126,47 @@ void ServerSettingsLogic::onPushButtonClearClientCacheClicked() void ServerSettingsLogic::onLineEditDescriptionEditingFinished() { const QString &newText = lineEditDescriptionText(); - QJsonObject server = m_settings.server(uiLogic()->selectedServerIndex); + QJsonObject server = m_settings->server(uiLogic()->selectedServerIndex); server.insert(config_key::description, newText); - m_settings.editServer(uiLogic()->selectedServerIndex, server); + m_settings->editServer(uiLogic()->selectedServerIndex, server); uiLogic()->onUpdateAllPages(); } +#if defined(Q_OS_ANDROID) +/* Auth result handler for Android */ +void authResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) +{ + qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode; + + if (resultCode == -1) { //ResultOK + uiLogic()->pageLogic()->updateSharingPage(m_serverIndex, DockerContainer::None); + emit uiLogic()->goToShareProtocolPage(Proto::Any); + } +} +#endif + void ServerSettingsLogic::onPushButtonShareFullClicked() { - uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None); +#if defined(Q_OS_ANDROID) +/* We use builtin keyguard for ssh key export protection on Android */ + auto appContext = QtAndroid::androidActivity().callObjectMethod( + "getApplicationContext", "()Landroid/content/Context;"); + if (appContext.isValid()) { + QAndroidActivityResultReceiver *receiver = new authResultReceiver(uiLogic(), uiLogic()->selectedServerIndex); + auto intent = QAndroidJniObject::callStaticObjectMethod( + "org/amnezia/vpn/AuthHelper", "getAuthIntent", + "(Landroid/content/Context;)Landroid/content/Intent;", appContext.object()); + if (intent.isValid()) { + if (intent.object() != nullptr) { + QtAndroid::startActivity(intent.object(), 1, receiver); + } + } else { + uiLogic()->pageLogic()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None); + emit uiLogic()->goToShareProtocolPage(Proto::Any); + } + } +#else + uiLogic()->pageLogic()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None); emit uiLogic()->goToShareProtocolPage(Proto::Any); +#endif } diff --git a/client/ui/pages_logic/ServerSettingsLogic.h b/client/ui/pages_logic/ServerSettingsLogic.h index 282b90ea..e2d57422 100644 --- a/client/ui/pages_logic/ServerSettingsLogic.h +++ b/client/ui/pages_logic/ServerSettingsLogic.h @@ -3,6 +3,10 @@ #include "PageLogicBase.h" +#if defined(Q_OS_ANDROID) +#include +#endif + class UiLogic; class ServerSettingsLogic : public PageLogicBase @@ -34,4 +38,25 @@ public: ~ServerSettingsLogic() = default; }; + +#if defined(Q_OS_ANDROID) +/* Auth result handler for Android */ +class authResultReceiver final : public PageLogicBase, public QAndroidActivityResultReceiver +{ +Q_OBJECT + +public: + authResultReceiver(UiLogic *uiLogic, int serverIndex , QObject *parent = nullptr) : PageLogicBase(uiLogic, parent) { + m_serverIndex = serverIndex; + } + ~authResultReceiver() {} + +public: + void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override; + +private: + int m_serverIndex; +}; +#endif + #endif // SERVER_SETTINGS_LOGIC_H diff --git a/client/ui/pages_logic/ShareConnectionLogic.cpp b/client/ui/pages_logic/ShareConnectionLogic.cpp index 80d98dc4..98f928c0 100644 --- a/client/ui/pages_logic/ShareConnectionLogic.cpp +++ b/client/ui/pages_logic/ShareConnectionLogic.cpp @@ -1,12 +1,10 @@ #include #include #include -//#include #include -#include "QZXing.h" -#include "QZXingImageProvider.h" -//#include "QZXingFilter.h" + +#include "qrcodegen.hpp" #include "ShareConnectionLogic.h" @@ -21,6 +19,7 @@ #include "defines.h" #include "core/defs.h" #include "core/errorstrings.h" +#include "core/servercontroller.h" #include #include "../uilogic.h" @@ -29,6 +28,8 @@ #include #endif +using namespace qrcodegen; + ShareConnectionLogic::ShareConnectionLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), m_textEditShareOpenVpnCodeText{}, @@ -73,19 +74,19 @@ void ShareConnectionLogic::onPushButtonShareAmneziaGenerateClicked() // Full access if (shareFullAccess()) { - serverConfig = m_settings.server(serverIndex); + serverConfig = m_settings->server(serverIndex); } // Container share else { - ServerCredentials credentials = m_settings.serverCredentials(serverIndex); - QJsonObject containerConfig = m_settings.containerConfig(serverIndex, container); + ServerCredentials credentials = m_settings->serverCredentials(serverIndex); + QJsonObject containerConfig = m_settings->containerConfig(serverIndex, container); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode e = ErrorCode::NoError; for (Proto p: ContainerProps::protocolsForContainer(container)) { - QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, p); + QJsonObject protoConfig = m_settings->protocolConfig(serverIndex, container, p); - QString cfg = VpnConfigurator::genVpnProtocolConfig(credentials, container, containerConfig, p, &e); + QString cfg = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, p, &e); if (e) { cfg = "Error generating config"; break; @@ -96,14 +97,14 @@ void ShareConnectionLogic::onPushButtonShareAmneziaGenerateClicked() QByteArray ba; if (!e) { - serverConfig = m_settings.server(serverIndex); + serverConfig = m_settings->server(serverIndex); serverConfig.remove(config_key::userName); serverConfig.remove(config_key::password); serverConfig.remove(config_key::port); serverConfig.insert(config_key::containers, QJsonArray {containerConfig}); serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); - auto dns = VpnConfigurator::getDnsForConfig(serverIndex); + auto dns = m_configurator->getDnsForConfig(serverIndex); serverConfig.insert(config_key::dns1, dns.first); serverConfig.insert(config_key::dns2, dns.second); @@ -129,13 +130,13 @@ void ShareConnectionLogic::onPushButtonShareOpenVpnGenerateClicked() { int serverIndex = uiLogic()->selectedServerIndex; DockerContainer container = uiLogic()->selectedDockerContainer; - ServerCredentials credentials = m_settings.serverCredentials(serverIndex); + ServerCredentials credentials = m_settings->serverCredentials(serverIndex); - const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + const QJsonObject &containerConfig = m_settings->containerConfig(serverIndex, container); ErrorCode e = ErrorCode::NoError; - QString cfg = OpenVpnConfigurator::genOpenVpnConfig(credentials, container, containerConfig, &e); - cfg = VpnConfigurator::processConfigWithExportSettings(serverIndex, container, Proto::OpenVpn, cfg); + QString cfg = m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, &e); + cfg = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::OpenVpn, cfg); set_textEditShareOpenVpnCodeText(QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString()); } @@ -144,16 +145,16 @@ void ShareConnectionLogic::onPushButtonShareShadowSocksGenerateClicked() { int serverIndex = uiLogic()->selectedServerIndex; DockerContainer container = uiLogic()->selectedDockerContainer; - ServerCredentials credentials = m_settings.serverCredentials(serverIndex); + ServerCredentials credentials = m_settings->serverCredentials(serverIndex); - QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, Proto::ShadowSocks); + QJsonObject protoConfig = m_settings->protocolConfig(serverIndex, container, Proto::ShadowSocks); QString cfg = protoConfig.value(config_key::last_config).toString(); if (cfg.isEmpty()) { - const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + const QJsonObject &containerConfig = m_settings->containerConfig(serverIndex, container); ErrorCode e = ErrorCode::NoError; - cfg = ShadowSocksConfigurator::genShadowSocksConfig(credentials, container, containerConfig, &e); + cfg = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, &e); } QJsonObject ssConfig = QJsonDocument::fromJson(cfg.toUtf8()).object(); @@ -167,8 +168,10 @@ void ShareConnectionLogic::onPushButtonShareShadowSocksGenerateClicked() ssString = "ss://" + ssString.toUtf8().toBase64(); set_lineEditShareShadowSocksStringText(ssString); - QImage qr = QZXing::encodeData(ssString.toUtf8(), QZXing::EncoderFormat_QR_CODE, QSize(512,512), QZXing::EncodeErrorCorrectionLevel_L); - set_shareShadowSocksQrCodeText(imageToBase64(qr)); + QrCode qr = QrCode::encodeText(ssString.toUtf8(), QrCode::Ecc::LOW); + QString svg = QString::fromStdString(toSvgString(qr, 0)); + + set_shareShadowSocksQrCodeText(svgToBase64(svg)); QString humanString = QString("Server: %3\n" "Port: %4\n" @@ -186,16 +189,16 @@ void ShareConnectionLogic::onPushButtonShareCloakGenerateClicked() { int serverIndex = uiLogic()->selectedServerIndex; DockerContainer container = uiLogic()->selectedDockerContainer; - ServerCredentials credentials = m_settings.serverCredentials(serverIndex); + ServerCredentials credentials = m_settings->serverCredentials(serverIndex); - QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, Proto::Cloak); + QJsonObject protoConfig = m_settings->protocolConfig(serverIndex, container, Proto::Cloak); QString cfg = protoConfig.value(config_key::last_config).toString(); if (cfg.isEmpty()) { - const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + const QJsonObject &containerConfig = m_settings->containerConfig(serverIndex, container); ErrorCode e = ErrorCode::NoError; - cfg = CloakConfigurator::genCloakConfig(credentials, container, containerConfig, &e); + cfg = m_configurator->cloakConfigurator->genCloakConfig(credentials, container, containerConfig, &e); } QJsonObject cloakConfig = QJsonDocument::fromJson(cfg.toUtf8()).object(); @@ -209,48 +212,47 @@ void ShareConnectionLogic::onPushButtonShareWireGuardGenerateClicked() { int serverIndex = uiLogic()->selectedServerIndex; DockerContainer container = uiLogic()->selectedDockerContainer; - ServerCredentials credentials = m_settings.serverCredentials(serverIndex); + ServerCredentials credentials = m_settings->serverCredentials(serverIndex); - const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + const QJsonObject &containerConfig = m_settings->containerConfig(serverIndex, container); ErrorCode e = ErrorCode::NoError; - QString cfg = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, &e); + QString cfg = m_configurator->wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, &e); if (e) { QMessageBox::warning(nullptr, APPLICATION_NAME, tr("Error occurred while configuring server.") + "\n" + errorString(e)); return; } - cfg = VpnConfigurator::processConfigWithExportSettings(serverIndex, container, Proto::WireGuard, cfg); + cfg = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::WireGuard, cfg); cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString(); set_textEditShareWireGuardCodeText(cfg); - QImage qr = QZXing::encodeData(cfg.toUtf8(), QZXing::EncoderFormat_QR_CODE, QSize(512,512), QZXing::EncodeErrorCorrectionLevel_L); + QrCode qr = QrCode::encodeText(cfg.toUtf8(), QrCode::Ecc::LOW); + QString svg = QString::fromStdString(toSvgString(qr, 0)); - set_shareWireGuardQrCodeText(imageToBase64(qr)); + set_shareWireGuardQrCodeText(svgToBase64(svg)); } void ShareConnectionLogic::onPushButtonShareIkev2GenerateClicked() { int serverIndex = uiLogic()->selectedServerIndex; DockerContainer container = uiLogic()->selectedDockerContainer; - ServerCredentials credentials = m_settings.serverCredentials(serverIndex); + ServerCredentials credentials = m_settings->serverCredentials(serverIndex); - //const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + Ikev2Configurator::ConnectionData connData = m_configurator->ikev2Configurator->prepareIkev2Config(credentials, container); - Ikev2Configurator::ConnectionData connData = Ikev2Configurator::prepareIkev2Config(credentials, container); - - QString cfg = Ikev2Configurator::genIkev2Config(connData); - cfg = VpnConfigurator::processConfigWithExportSettings(serverIndex, container, Proto::Ikev2, cfg); + QString cfg = m_configurator->ikev2Configurator->genIkev2Config(connData); + cfg = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Ikev2, cfg); cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::cert].toString(); set_textEditShareIkev2CertText(cfg); - QString mobileCfg = Ikev2Configurator::genMobileConfig(connData); + QString mobileCfg = m_configurator->ikev2Configurator->genMobileConfig(connData); set_textEditShareIkev2MobileConfigText(mobileCfg); - QString strongSwanCfg = Ikev2Configurator::genStrongSwanConfig(connData); + QString strongSwanCfg = m_configurator->ikev2Configurator->genStrongSwanConfig(connData); set_textEditShareIkev2StrongSwanConfigText(strongSwanCfg); } @@ -268,7 +270,7 @@ void ShareConnectionLogic::updateSharingPage(int serverIndex, DockerContainer co QList ShareConnectionLogic::genQrCodeImageSeries(const QByteArray &data) { - double k = 1500; + double k = 850; quint8 chunksCount = std::ceil(data.size() / k); QList chunks; @@ -279,18 +281,15 @@ QList ShareConnectionLogic::genQrCodeImageSeries(const QByteArray &data QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - QImage qr = QZXing::encodeData(ba, QZXing::EncoderFormat_QR_CODE, QSize(512,512), QZXing::EncodeErrorCorrectionLevel_L); - chunks.append(imageToBase64(qr)); + QrCode qr = QrCode::encodeText(ba, QrCode::Ecc::LOW); + QString svg = QString::fromStdString(toSvgString(qr, 0)); + chunks.append(svgToBase64(svg)); } return chunks; } -QString ShareConnectionLogic::imageToBase64(const QImage &image) +QString ShareConnectionLogic::svgToBase64(const QString &image) { - QByteArray ba; - QBuffer bu(&ba); - bu.open(QIODevice::WriteOnly); - image.save(&bu, "PNG"); - return "data:image/png;base64," + QString::fromLatin1(ba.toBase64().data()); + return "data:image/svg;base64," + QString::fromLatin1(image.toUtf8().toBase64().data()); } diff --git a/client/ui/pages_logic/ShareConnectionLogic.h b/client/ui/pages_logic/ShareConnectionLogic.h index c3c2ad6d..5d46be97 100644 --- a/client/ui/pages_logic/ShareConnectionLogic.h +++ b/client/ui/pages_logic/ShareConnectionLogic.h @@ -2,6 +2,7 @@ #define SHARE_CONNECTION_LOGIC_H #include "PageLogicBase.h" +#include "containers/containers_defs.h" class UiLogic; @@ -48,8 +49,7 @@ public: void updateSharingPage(int serverIndex, DockerContainer container); QList genQrCodeImageSeries(const QByteArray &data); - QString imageToBase64(const QImage &image); - + QString svgToBase64(const QString &image); }; #endif // SHARE_CONNECTION_LOGIC_H diff --git a/client/ui/pages_logic/SitesLogic.cpp b/client/ui/pages_logic/SitesLogic.cpp index 83f712ea..17357073 100644 --- a/client/ui/pages_logic/SitesLogic.cpp +++ b/client/ui/pages_logic/SitesLogic.cpp @@ -18,13 +18,13 @@ SitesLogic::SitesLogic(UiLogic *logic, QObject *parent): m_tableViewSitesModel{nullptr}, m_lineEditSitesAddCustomText{} { - sitesModels.insert(Settings::VpnOnlyForwardSites, new SitesModel(Settings::VpnOnlyForwardSites)); - sitesModels.insert(Settings::VpnAllExceptSites, new SitesModel(Settings::VpnAllExceptSites)); + sitesModels.insert(Settings::VpnOnlyForwardSites, new SitesModel(m_settings, Settings::VpnOnlyForwardSites)); + sitesModels.insert(Settings::VpnAllExceptSites, new SitesModel(m_settings, Settings::VpnAllExceptSites)); } void SitesLogic::onUpdatePage() { - Settings::RouteMode m = m_settings.routeMode(); + Settings::RouteMode m = m_settings->routeMode(); if (m == Settings::VpnAllSites) return; if (m == Settings::VpnOnlyForwardSites) { @@ -40,10 +40,10 @@ void SitesLogic::onUpdatePage() void SitesLogic::onPushButtonAddCustomSitesClicked() { - if (uiLogic()->vpnLogic()->radioButtonVpnModeAllSitesChecked()) { + if (uiLogic()->pageLogic()->radioButtonVpnModeAllSitesChecked()) { return; } - Settings::RouteMode mode = m_settings.routeMode(); + Settings::RouteMode mode = m_settings->routeMode(); QString newSite = lineEditSitesAddCustomText(); @@ -60,7 +60,7 @@ void SitesLogic::onPushButtonAddCustomSitesClicked() } const auto &cbProcess = [this, mode](const QString &newSite, const QString &ip) { - m_settings.addVpnSite(mode, newSite, ip); + m_settings->addVpnSite(mode, newSite, ip); if (!ip.isEmpty()) { uiLogic()->m_vpnConnection->addRoutes(QStringList() << ip); @@ -100,7 +100,7 @@ void SitesLogic::onPushButtonAddCustomSitesClicked() void SitesLogic::onPushButtonSitesDeleteClicked(QStringList items) { - Settings::RouteMode mode = m_settings.routeMode(); + Settings::RouteMode mode = m_settings->routeMode(); auto siteModel = qobject_cast (tableViewSitesModel()); if (!siteModel || items.isEmpty()) { @@ -121,7 +121,7 @@ void SitesLogic::onPushButtonSitesDeleteClicked(QStringList items) } } - m_settings.removeVpnSites(mode, sites); + m_settings->removeVpnSites(mode, sites); if (uiLogic()->m_vpnConnection->connectionState() == VpnProtocol::Connected) { uiLogic()->m_vpnConnection->deleteRoutes(ips); @@ -139,7 +139,7 @@ void SitesLogic::onPushButtonSitesImportClicked(const QString& fileName) return; } - Settings::RouteMode mode = m_settings.routeMode(); + Settings::RouteMode mode = m_settings->routeMode(); QStringList ips; QMap sites; @@ -187,8 +187,8 @@ void SitesLogic::onPushButtonSitesImportClicked(const QString& fileName) } - m_settings.addVpnIps(mode, ips); - m_settings.addVpnSites(mode, sites); + m_settings->addVpnIps(mode, ips); + m_settings->addVpnSites(mode, sites); uiLogic()->m_vpnConnection->addRoutes(QStringList() << ips); uiLogic()->m_vpnConnection->flushDns(); @@ -198,9 +198,9 @@ void SitesLogic::onPushButtonSitesImportClicked(const QString& fileName) void SitesLogic::onPushButtonSitesExportClicked() { - Settings::RouteMode mode = m_settings.routeMode(); + Settings::RouteMode mode = m_settings->routeMode(); - QVariantMap sites = m_settings.vpnSites(mode); + QVariantMap sites = m_settings->vpnSites(mode); QString data; for (auto s : sites.keys()) { diff --git a/client/ui/pages_logic/SitesLogic.h b/client/ui/pages_logic/SitesLogic.h index 2afb3805..35bf1f90 100644 --- a/client/ui/pages_logic/SitesLogic.h +++ b/client/ui/pages_logic/SitesLogic.h @@ -2,6 +2,7 @@ #define SITES_LOGIC_H #include "PageLogicBase.h" +#include "settings.h" class UiLogic; class SitesModel; diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 57966fb1..e5fe6df3 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -1,6 +1,9 @@ #include "StartPageLogic.h" +#include "ViewConfigLogic.h" + #include "core/errorstrings.h" #include "configurators/ssh_configurator.h" +#include "configurators/vpn_configurator.h" #include "../uilogic.h" #include "utilities.h" @@ -8,6 +11,7 @@ #include #ifdef Q_OS_ANDROID +#include #include "platforms/android/android_controller.h" #endif @@ -16,18 +20,20 @@ StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent): m_pushButtonConnectEnabled{true}, m_pushButtonConnectText{tr("Connect")}, m_pushButtonConnectKeyChecked{false}, - m_lineEditStartExistingCodeText{}, - m_textEditSshKeyText{}, - m_lineEditIpText{}, - m_lineEditPasswordText{}, - m_lineEditLoginText{}, m_labelWaitInfoVisible{true}, - m_labelWaitInfoText{}, m_pushButtonBackFromStartVisible{true}, - m_pushButtonConnectVisible{true}, m_ipAddressPortRegex{Utils::ipAddressPortRegExp()} { - +#ifdef Q_OS_ANDROID + // Set security screen for Android app + QtAndroid::runOnAndroidThread([]() { + QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;"); + if (window.isValid()){ + const int FLAG_SECURE = 8192; + window.callMethod("addFlags", "(I)V", FLAG_SECURE); + } + }); +#endif } void StartPageLogic::onUpdatePage() @@ -41,7 +47,6 @@ void StartPageLogic::onUpdatePage() set_labelWaitInfoVisible(false); set_labelWaitInfoText(""); - set_pushButtonConnectVisible(true); set_pushButtonConnectKeyChecked(false); @@ -50,8 +55,6 @@ void StartPageLogic::onUpdatePage() void StartPageLogic::onPushButtonConnect() { -// uiLogic()->goToPage(Page::NewServer); -// return; if (pushButtonConnectKeyChecked()){ if (lineEditIpText().isEmpty() || lineEditLoginText().isEmpty() || @@ -68,7 +71,6 @@ void StartPageLogic::onPushButtonConnect() return; } } - qDebug() << "UiLogic::onPushButtonConnect checking new server"; ServerCredentials serverCredentials; serverCredentials.hostName = lineEditIpText(); @@ -85,7 +87,7 @@ void StartPageLogic::onPushButtonConnect() } if (key.contains("OPENSSH") && key.contains("BEGIN") && key.contains("PRIVATE KEY")) { - key = SshConfigurator::convertOpenSShKey(key); + key = m_configurator->sshConfigurator->convertOpenSShKey(key); } serverCredentials.password = key; @@ -99,7 +101,7 @@ void StartPageLogic::onPushButtonConnect() ErrorCode e = ErrorCode::NoError; #ifdef Q_DEBUG - //QString output = ServerController::checkSshConnection(serverCredentials, &e); + //QString output = m_serverController->checkSshConnection(serverCredentials, &e); #else QString output; #endif @@ -153,19 +155,10 @@ bool StartPageLogic::importConnection(const QJsonObject &profile) credentials.userName = profile.value(config_key::userName).toString(); credentials.password = profile.value(config_key::password).toString(); -// qDebug() << QString("Added server %3@%1:%2"). -// arg(credentials.hostName). -// arg(credentials.port). -// arg(credentials.userName); - - //qDebug() << QString("Password") << credentials.password; - if (credentials.isValid() || profile.contains(config_key::containers)) { - m_settings.addServer(profile); - m_settings.setDefaultServer(m_settings.serversCount() - 1); - - emit uiLogic()->goToPage(Page::Vpn); - emit uiLogic()->setStartPage(Page::Vpn); + // check config + uiLogic()->pageLogic()->set_configJson(profile); + emit uiLogic()->goToPage(Page::ViewConfig); } else { qDebug() << "Failed to import profile"; @@ -174,8 +167,8 @@ bool StartPageLogic::importConnection(const QJsonObject &profile) } if (!profile.contains(config_key::containers)) { - uiLogic()->selectedServerIndex = m_settings.defaultServerIndex(); - uiLogic()->selectedDockerContainer = m_settings.defaultContainer(uiLogic()->selectedServerIndex); + uiLogic()->selectedServerIndex = m_settings->defaultServerIndex(); + uiLogic()->selectedDockerContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex); uiLogic()->onUpdateAllPages(); emit uiLogic()->goToPage(Page::ServerContainers); @@ -209,7 +202,6 @@ bool StartPageLogic::importConnectionFromCode(QString code) bool StartPageLogic::importConnectionFromQr(const QByteArray &data) { - qDebug() << "StartPageLogic::importConnectionFromQr" << data; QJsonObject dataObj = QJsonDocument::fromJson(data).object(); if (!dataObj.isEmpty()) { return importConnection(dataObj); diff --git a/client/ui/pages_logic/StartPageLogic.h b/client/ui/pages_logic/StartPageLogic.h index 49e95047..e7f4d557 100644 --- a/client/ui/pages_logic/StartPageLogic.h +++ b/client/ui/pages_logic/StartPageLogic.h @@ -3,7 +3,7 @@ #include "PageLogicBase.h" -#include +#include class UiLogic; @@ -22,7 +22,6 @@ class StartPageLogic : public PageLogicBase AUTO_PROPERTY(bool, labelWaitInfoVisible) AUTO_PROPERTY(QString, labelWaitInfoText) AUTO_PROPERTY(bool, pushButtonBackFromStartVisible) - AUTO_PROPERTY(bool, pushButtonConnectVisible) READONLY_PROPERTY(QRegularExpression, ipAddressPortRegex) public: diff --git a/client/ui/pages_logic/ViewConfigLogic.cpp b/client/ui/pages_logic/ViewConfigLogic.cpp new file mode 100644 index 00000000..17d8814e --- /dev/null +++ b/client/ui/pages_logic/ViewConfigLogic.cpp @@ -0,0 +1,67 @@ +#include "ViewConfigLogic.h" +#include "core/errorstrings.h" +#include "../uilogic.h" + + +ViewConfigLogic::ViewConfigLogic(UiLogic *logic, QObject *parent): + PageLogicBase(logic, parent) +{ + +} + +void ViewConfigLogic::onUpdatePage() +{ + set_configText(QJsonDocument(configJson()).toJson()); + + m_openVpnLastConfigs = m_openVpnMalStrings = + "
"; + + m_warningStringNumber = 3; + m_warningActive = false; + + const QJsonArray &containers = configJson()[config_key::containers].toArray(); + int i = 0; + for (const QJsonValue &v: containers) { + QString cfg_json = v.toObject()[ProtocolProps::protoToString(Proto::OpenVpn)] + .toObject()[config_key::last_config].toString(); + + QString openvpn_cfg = QJsonDocument::fromJson(cfg_json.toUtf8()).object()[config_key::config] + .toString(); + + openvpn_cfg.replace("\r", ""); + + QStringList lines = openvpn_cfg.split("\n"); + for (const QString &l: lines) { + i++; + QRegularExpressionMatch match = m_re.match(l); + if (dangerousTags.contains(match.captured(0))) { + QString t = QString("

%1").arg(l); + m_openVpnLastConfigs.append(t + "\n"); + m_openVpnMalStrings.append(t); + if (m_warningStringNumber == 3) m_warningStringNumber = i - 3; + m_warningActive = true; + qDebug() << "ViewConfigLogic : malicious scripts warning:" << l; + } + else { + m_openVpnLastConfigs.append("

" + l + " \n"); + } + } + } + + emit openVpnLastConfigsChanged(m_openVpnLastConfigs); + emit openVpnMalStringsChanged(m_openVpnMalStrings); + emit warningStringNumberChanged(m_warningStringNumber); + emit warningActiveChanged(m_warningActive); +} + +void ViewConfigLogic::importConfig() +{ + m_settings->addServer(configJson()); + m_settings->setDefaultServer(m_settings->serversCount() - 1); + + emit uiLogic()->goToPage(Page::Vpn); + emit uiLogic()->setStartPage(Page::Vpn); +} + diff --git a/client/ui/pages_logic/ViewConfigLogic.h b/client/ui/pages_logic/ViewConfigLogic.h new file mode 100644 index 00000000..4713158e --- /dev/null +++ b/client/ui/pages_logic/ViewConfigLogic.h @@ -0,0 +1,47 @@ +#ifndef VIEW_CONFIG_LOGIC_H +#define VIEW_CONFIG_LOGIC_H + +#include "PageLogicBase.h" + +#include + +class UiLogic; + +class ViewConfigLogic : public PageLogicBase +{ + Q_OBJECT + + AUTO_PROPERTY(QString, configText) + AUTO_PROPERTY(QString, openVpnLastConfigs) + AUTO_PROPERTY(QString, openVpnMalStrings) + AUTO_PROPERTY(QJsonObject, configJson) + AUTO_PROPERTY(int, warningStringNumber) + AUTO_PROPERTY(bool, warningActive) + +public: + Q_INVOKABLE void onUpdatePage() override; + Q_INVOKABLE void importConfig(); + + +public: + explicit ViewConfigLogic(UiLogic *uiLogic, QObject *parent = nullptr); + ~ViewConfigLogic() = default; + +private: + QRegularExpression m_re {"(\\w+-\\w+|\\w+)"}; + + // https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst + QStringList dangerousTags { + "up", + "tls-verify", + "ipchange", + "client-connect", + "route-up", + "route-pre-down", + "client-disconnect", + "down", + "learn-address", + "auth-user-pass-verify" + }; +}; +#endif // VIEW_CONFIG_LOGIC_H diff --git a/client/ui/pages_logic/VpnLogic.cpp b/client/ui/pages_logic/VpnLogic.cpp index 5812441e..44a37a5c 100644 --- a/client/ui/pages_logic/VpnLogic.cpp +++ b/client/ui/pages_logic/VpnLogic.cpp @@ -33,7 +33,7 @@ VpnLogic::VpnLogic(UiLogic *logic, QObject *parent): connect(this, &VpnLogic::connectToVpn, uiLogic()->m_vpnConnection, &VpnConnection::connectToVpn, Qt::QueuedConnection); connect(this, &VpnLogic::disconnectFromVpn, uiLogic()->m_vpnConnection, &VpnConnection::disconnectFromVpn, Qt::QueuedConnection); - if (m_settings.isAutoConnect() && m_settings.defaultServerIndex() >= 0) { + if (m_settings->isAutoConnect() && m_settings->defaultServerIndex() >= 0) { QTimer::singleShot(1000, this, [this](){ set_pushButtonConnectEnabled(false); onConnect(); @@ -47,20 +47,20 @@ VpnLogic::VpnLogic(UiLogic *logic, QObject *parent): void VpnLogic::onUpdatePage() { - Settings::RouteMode mode = m_settings.routeMode(); - DockerContainer selectedContainer = m_settings.defaultContainer(m_settings.defaultServerIndex()); + Settings::RouteMode mode = m_settings->routeMode(); + DockerContainer selectedContainer = m_settings->defaultContainer(m_settings->defaultServerIndex()); set_isCustomRoutesSupported (selectedContainer == DockerContainer::OpenVpn || selectedContainer == DockerContainer::ShadowSocks|| selectedContainer == DockerContainer::Cloak); - set_isContainerHaveAuthData(m_settings.haveAuthData(m_settings.defaultServerIndex())); + set_isContainerHaveAuthData(m_settings->haveAuthData(m_settings->defaultServerIndex())); set_radioButtonVpnModeAllSitesChecked(mode == Settings::VpnAllSites || !isCustomRoutesSupported()); set_radioButtonVpnModeForwardSitesChecked(mode == Settings::VpnOnlyForwardSites && isCustomRoutesSupported()); set_radioButtonVpnModeExceptSitesChecked(mode == Settings::VpnAllExceptSites && isCustomRoutesSupported()); - const QJsonObject &server = uiLogic()->m_settings.defaultServer(); + const QJsonObject &server = uiLogic()->m_settings->defaultServer(); QString serverString = QString("%2 (%3)") .arg(server.value(config_key::description).toString()) .arg(server.value(config_key::hostName).toString()); @@ -69,7 +69,7 @@ void VpnLogic::onUpdatePage() QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer); set_labelCurrentService(selectedContainerName); - auto dns = VpnConfigurator::getDnsForConfig(m_settings.defaultServerIndex()); + auto dns = m_configurator->getDnsForConfig(m_settings->defaultServerIndex()); set_amneziaDnsEnabled(dns.first == protocols::dns::amneziaDnsIp); if (dns.first == protocols::dns::amneziaDnsIp) { set_labelCurrentDns("On your server"); @@ -93,19 +93,19 @@ void VpnLogic::onUpdatePage() void VpnLogic::onRadioButtonVpnModeAllSitesClicked() { - m_settings.setRouteMode(Settings::VpnAllSites); + m_settings->setRouteMode(Settings::VpnAllSites); onUpdatePage(); } void VpnLogic::onRadioButtonVpnModeForwardSitesClicked() { - m_settings.setRouteMode(Settings::VpnOnlyForwardSites); + m_settings->setRouteMode(Settings::VpnOnlyForwardSites); onUpdatePage(); } void VpnLogic::onRadioButtonVpnModeExceptSitesClicked() { - m_settings.setRouteMode(Settings::VpnAllExceptSites); + m_settings->setRouteMode(Settings::VpnAllExceptSites); onUpdatePage(); } @@ -203,11 +203,11 @@ void VpnLogic::onPushButtonConnectClicked() void VpnLogic::onConnect() { - int serverIndex = m_settings.defaultServerIndex(); - ServerCredentials credentials = m_settings.serverCredentials(serverIndex); - DockerContainer container = m_settings.defaultContainer(serverIndex); + int serverIndex = m_settings->defaultServerIndex(); + ServerCredentials credentials = m_settings->serverCredentials(serverIndex); + DockerContainer container = m_settings->defaultContainer(serverIndex); - if (m_settings.containers(serverIndex).isEmpty()) { + if (m_settings->containers(serverIndex).isEmpty()) { set_labelErrorText(tr("VPN Protocols is not installed.\n Please install VPN container at first")); set_pushButtonConnectChecked(false); return; @@ -220,7 +220,7 @@ void VpnLogic::onConnect() } - const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + const QJsonObject &containerConfig = m_settings->containerConfig(serverIndex, container); onConnectWorker(serverIndex, credentials, container, containerConfig); } @@ -233,14 +233,6 @@ void VpnLogic::onConnectWorker(int serverIndex, const ServerCredentials &credent qApp->processEvents(); emit connectToVpn(serverIndex, credentials, container, containerConfig); - -// if (errorCode) { -// //ui->pushButton_connect->setChecked(false); -// uiLogic()->setDialogConnectErrorText(errorString(errorCode)); -// emit uiLogic()->showConnectErrorDialog(); -// return; -// } - } void VpnLogic::onDisconnect() diff --git a/client/ui/pages_logic/WizardLogic.cpp b/client/ui/pages_logic/WizardLogic.cpp index 7936524d..5fe820f1 100644 --- a/client/ui/pages_logic/WizardLogic.cpp +++ b/client/ui/pages_logic/WizardLogic.cpp @@ -55,9 +55,9 @@ void WizardLogic::onPushButtonVpnModeFinishClicked() auto containers = getInstallConfigsFromWizardPage(); uiLogic()->installServer(containers); if (checkBoxVpnModeChecked()) { - m_settings.setRouteMode(Settings::VpnOnlyForwardSites); + m_settings->setRouteMode(Settings::VpnOnlyForwardSites); } else { - m_settings.setRouteMode(Settings::VpnAllSites); + m_settings->setRouteMode(Settings::VpnAllSites); } } diff --git a/client/ui/pages_logic/WizardLogic.h b/client/ui/pages_logic/WizardLogic.h index cfba5d2d..3827c86e 100644 --- a/client/ui/pages_logic/WizardLogic.h +++ b/client/ui/pages_logic/WizardLogic.h @@ -2,6 +2,7 @@ #define WIZARD_LOGIC_H #include "PageLogicBase.h" +#include "containers/containers_defs.h" class UiLogic; diff --git a/client/ui/pages_logic/protocols/CloakLogic.cpp b/client/ui/pages_logic/protocols/CloakLogic.cpp index 2e341383..59019990 100644 --- a/client/ui/pages_logic/protocols/CloakLogic.cpp +++ b/client/ui/pages_logic/protocols/CloakLogic.cpp @@ -52,10 +52,10 @@ QJsonObject CloakLogic::getProtocolConfigFromPage(QJsonObject oldConfig) void CloakLogic::onPushButtonSaveClicked() { - QJsonObject protocolConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::Cloak); + QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::Cloak); protocolConfig = getProtocolConfigFromPage(protocolConfig); - QJsonObject containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); QJsonObject newContainerConfig = containerConfig; newContainerConfig.insert(ProtocolProps::protoToString(Proto::Cloak), protocolConfig); @@ -89,14 +89,14 @@ void CloakLogic::onPushButtonSaveClicked() }; ErrorCode e = uiLogic()->doInstallAction([this, containerConfig, &newContainerConfig](){ - return ServerController::updateContainer(m_settings.serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); + return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); }, page_func, progressBar_reset, pushButton_save_func, label_info_func); if (!e) { - m_settings.setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig); - m_settings.clearLastConnectionConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + m_settings->setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig); + m_settings->clearLastConnectionConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); } qDebug() << "Protocol saved with code:" << e << "for" << uiLogic()->selectedServerIndex << uiLogic()->selectedDockerContainer; diff --git a/client/ui/pages_logic/protocols/CloakLogic.h b/client/ui/pages_logic/protocols/CloakLogic.h index e110c465..d88252c8 100644 --- a/client/ui/pages_logic/protocols/CloakLogic.h +++ b/client/ui/pages_logic/protocols/CloakLogic.h @@ -32,7 +32,6 @@ public: QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: - Settings m_settings; UiLogic *m_uiLogic; }; diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp index 98115f29..3b672747 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp @@ -81,10 +81,10 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo void OpenVpnLogic::onPushButtonProtoOpenVpnSaveClicked() { - QJsonObject protocolConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::OpenVpn); + QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::OpenVpn); protocolConfig = getProtocolConfigFromPage(protocolConfig); - QJsonObject containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); QJsonObject newContainerConfig = containerConfig; newContainerConfig.insert(ProtocolProps::protoToString(Proto::OpenVpn), protocolConfig); @@ -118,14 +118,14 @@ void OpenVpnLogic::onPushButtonProtoOpenVpnSaveClicked() }; ErrorCode e = uiLogic()->doInstallAction([this, containerConfig, &newContainerConfig](){ - return ServerController::updateContainer(m_settings.serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); + return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); }, page_proto_openvpn, progressBar_proto_openvpn_reset, pushButton_proto_openvpn_save, label_proto_openvpn_info); if (!e) { - m_settings.setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig); - m_settings.clearLastConnectionConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + m_settings->setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig); + m_settings->clearLastConnectionConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); } qDebug() << "Protocol saved with code:" << e << "for" << uiLogic()->selectedServerIndex << uiLogic()->selectedDockerContainer; } diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.h b/client/ui/pages_logic/protocols/OpenVpnLogic.h index 1d5c7f78..45141050 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.h +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.h @@ -44,7 +44,6 @@ public: QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: - Settings m_settings; UiLogic *m_uiLogic; }; diff --git a/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp b/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp index 908be509..8bd9d105 100644 --- a/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp +++ b/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp @@ -5,7 +5,6 @@ #include #include "OtherProtocolsLogic.h" -#include "core/servercontroller.h" #include #include "../../uilogic.h" #include "utilities.h" @@ -82,7 +81,7 @@ void OtherProtocolsLogic::onPushButtonSftpMountDriveClicked() { QString mountPath; QString cmd; - QString host = m_settings.serverCredentials(uiLogic()->selectedServerIndex).hostName; + QString host = m_settings->serverCredentials(uiLogic()->selectedServerIndex).hostName; #ifdef Q_OS_WINDOWS diff --git a/client/ui/pages_logic/protocols/OtherProtocolsLogic.h b/client/ui/pages_logic/protocols/OtherProtocolsLogic.h index 8639ba54..508c2914 100644 --- a/client/ui/pages_logic/protocols/OtherProtocolsLogic.h +++ b/client/ui/pages_logic/protocols/OtherProtocolsLogic.h @@ -31,7 +31,6 @@ public: //QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: - Settings m_settings; UiLogic *m_uiLogic; #ifdef AMNEZIA_DESKTOP diff --git a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp index 9aa8d0f3..1d5da936 100644 --- a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp +++ b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp @@ -46,10 +46,9 @@ QJsonObject ShadowSocksLogic::getProtocolConfigFromPage(QJsonObject oldConfig) void ShadowSocksLogic::onPushButtonSaveClicked() { - QJsonObject protocolConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::ShadowSocks); - //protocolConfig = getShadowSocksConfigFromPage(protocolConfig); + QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::ShadowSocks); - QJsonObject containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); QJsonObject newContainerConfig = containerConfig; newContainerConfig.insert(ProtocolProps::protoToString(Proto::ShadowSocks), protocolConfig); UiLogic::PageFunc page_proto_shadowsocks; @@ -82,14 +81,14 @@ void ShadowSocksLogic::onPushButtonSaveClicked() }; ErrorCode e = uiLogic()->doInstallAction([this, containerConfig, &newContainerConfig](){ - return ServerController::updateContainer(m_settings.serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); + return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); }, page_proto_shadowsocks, progressBar_reset, pushButton_proto_shadowsocks_save, label_proto_shadowsocks_info); if (!e) { - m_settings.setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig); - m_settings.clearLastConnectionConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + m_settings->setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig); + m_settings->clearLastConnectionConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); } qDebug() << "Protocol saved with code:" << e << "for" << uiLogic()->selectedServerIndex << uiLogic()->selectedDockerContainer; } diff --git a/client/ui/pages_logic/protocols/ShadowSocksLogic.h b/client/ui/pages_logic/protocols/ShadowSocksLogic.h index b7c2c9ee..4e566825 100644 --- a/client/ui/pages_logic/protocols/ShadowSocksLogic.h +++ b/client/ui/pages_logic/protocols/ShadowSocksLogic.h @@ -30,7 +30,6 @@ public: QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: - Settings m_settings; UiLogic *m_uiLogic; }; diff --git a/client/ui/property_helper.h b/client/ui/property_helper.h index 3d254c71..927105b3 100644 --- a/client/ui/property_helper.h +++ b/client/ui/property_helper.h @@ -14,7 +14,7 @@ } \ Q_SIGNAL void NAME ## Changed(TYPE value);\ private: \ - TYPE m_ ## NAME; + TYPE m_ ## NAME{}; #define READONLY_PROPERTY(TYPE, NAME) \ Q_PROPERTY(TYPE NAME READ NAME CONSTANT ) \ @@ -22,6 +22,6 @@ TYPE NAME() const { return m_ ## NAME ; } \ private: \ void NAME(TYPE value) {m_ ## NAME = value; } \ - TYPE m_ ## NAME; + TYPE m_ ## NAME{}; #endif // PROPERTY_HELPER_H diff --git a/client/ui/qml/Pages/PageAppSetting.qml b/client/ui/qml/Pages/PageAppSetting.qml index b795ceb5..7e8d415a 100644 --- a/client/ui/qml/Pages/PageAppSetting.qml +++ b/client/ui/qml/Pages/PageAppSetting.qml @@ -107,7 +107,7 @@ PageBase { BlueButtonType { Layout.fillWidth: true - Layout.topMargin: 15 + Layout.topMargin: 10 Layout.preferredHeight: 41 text: qsTr("Export logs") onClicked: { @@ -117,7 +117,7 @@ PageBase { BlueButtonType { Layout.fillWidth: true - Layout.topMargin: 15 + Layout.topMargin: 10 Layout.preferredHeight: 41 property string start_text: qsTr("Clear logs") @@ -135,6 +135,31 @@ PageBase { AppSettingsLogic.onPushButtonClearLogsClicked() } } + + LabelType { + Layout.fillWidth: true + Layout.topMargin: 30 + text: qsTr("Backup and restore configuration") + } + + BlueButtonType { + Layout.fillWidth: true + Layout.topMargin: 10 + Layout.preferredHeight: 41 + text: qsTr("Backup app config") + onClicked: { + AppSettingsLogic.onPushButtonBackupAppConfigClicked() + } + } + BlueButtonType { + Layout.fillWidth: true + Layout.topMargin: 10 + Layout.preferredHeight: 41 + text: qsTr("Restore app config") + onClicked: { + AppSettingsLogic.onPushButtonRestoreAppConfigClicked() + } + } } } diff --git a/client/ui/qml/Pages/PageNetworkSetting.qml b/client/ui/qml/Pages/PageNetworkSetting.qml index 924b32dd..0e890446 100644 --- a/client/ui/qml/Pages/PageNetworkSetting.qml +++ b/client/ui/qml/Pages/PageNetworkSetting.qml @@ -87,7 +87,7 @@ If AmneziaDNS service is not installed on the same server, or this option is unc anchors.topMargin: 20 width: parent.width - 60 height: 21 - text: qsTr("Secondray DNS server") + text: qsTr("Secondary DNS server") } TextFieldType { id: dns2 diff --git a/client/ui/qml/Pages/PageServerContainers.qml b/client/ui/qml/Pages/PageServerContainers.qml index fb5dd97e..4b05568a 100644 --- a/client/ui/qml/Pages/PageServerContainers.qml +++ b/client/ui/qml/Pages/PageServerContainers.qml @@ -1,462 +1,439 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Dialogs 1.1 -import QtQuick.Layouts 1.15 -import SortFilterProxyModel 0.2 -import ContainerProps 1.0 -import ProtocolProps 1.0 -import PageEnum 1.0 -import ProtocolEnum 1.0 -import "./" -import "../Controls" -import "../Config" -import "InstallSettings" - -PageBase { - id: root - page: PageEnum.ServerContainers - logic: ServerContainersLogic - - enabled: ServerContainersLogic.pageEnabled - - function resetPage() { - container_selector.selectedIndex = -1 - } - - Connections { - target: logic - function onUpdatePage() { - root.resetPage() - } - } - - BackButton { - id: back - } - Caption { - id: caption - text: container_selector.selectedIndex > 0 ? qsTr("Install new service") : qsTr("Installed services") - } - - SelectContainer { - id: container_selector - - onAboutToHide: { - pageLoader.focus = true - } - - onContainerSelected: { - var containerProto = ContainerProps.defaultProtocol(c_index) - - - if (ProtocolProps.defaultPort(containerProto) < 0) { - tf_port_num.enabled = false - tf_port_num.text = qsTr("Default") - } - else tf_port_num.text = ProtocolProps.defaultPort(containerProto) - - cb_port_proto.currentIndex = ProtocolProps.defaultTransportProto(containerProto) - - tf_port_num.enabled = ProtocolProps.defaultPortChangeable(containerProto) - cb_port_proto.enabled = ProtocolProps.defaultTransportProtoChangeable(containerProto) - } - } - - Column { - id: c1 - visible: container_selector.selectedIndex > 0 - width: parent.width - anchors.top: caption.bottom - anchors.topMargin: 10 - - Caption { - font.pixelSize: 22 - text: UiLogic.containerName(container_selector.selectedIndex) - } - - Text { - width: parent.width - anchors.topMargin: 10 - padding: 10 - - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 16 - color: "#181922" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.Wrap - - text: UiLogic.containerDesc(container_selector.selectedIndex) - } - } - - Rectangle { - id: frame_settings - visible: container_selector.selectedIndex > 0 - width: parent.width - anchors.top: c1.bottom - anchors.topMargin: 10 - - border.width: 1 - border.color: "lightgray" - anchors.bottomMargin: 5 - anchors.horizontalCenter: parent.horizontalCenter - radius: 2 - Grid { - id: grid - visible: container_selector.selectedIndex > 0 - anchors.fill: parent - columns: 2 - horizontalItemAlignment: Grid.AlignHCenter - verticalItemAlignment: Grid.AlignVCenter - topPadding: 5 - leftPadding: 10 - spacing: 5 - - - LabelType { - width: 130 - text: qsTr("Port") - } - TextFieldType { - id: tf_port_num - width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 - } - LabelType { - width: 130 - text: qsTr("Network Protocol") - } - ComboBoxType { - id: cb_port_proto - width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 - model: [ - qsTr("udp"), - qsTr("tcp"), - ] - } - } - } - - BlueButtonType { - id: pb_cancel_add - visible: container_selector.selectedIndex > 0 - - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: pb_continue_add.top - anchors.bottomMargin: 20 - - width: parent.width - 40 - height: 40 - text: qsTr("Cancel") - font.pixelSize: 16 - onClicked: container_selector.selectedIndex = -1 - - } - - BlueButtonType { - id: pb_continue_add - visible: container_selector.selectedIndex > 0 - - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: 20 - - width: parent.width - 40 - height: 40 - text: qsTr("Continue") - font.pixelSize: 16 - onClicked: { - let cont = container_selector.selectedIndex - let tp = ProtocolProps.transportProtoFromString(cb_port_proto.currentText) - let port = tf_port_num.text - ServerContainersLogic.onPushButtonContinueClicked(cont, port, tp) - } - } - - - - - - Flickable { - visible: container_selector.selectedIndex <= 0 - clip: true - width: parent.width - anchors.top: caption.bottom - anchors.bottom: pb_add_container.top - contentHeight: col.height - - Column { - visible: container_selector.selectedIndex <= 0 - id: col - anchors { - left: parent.left; - right: parent.right; - } - topPadding: 20 - spacing: 10 - - Caption { - id: cap1 - text: qsTr("Installed Protocols and Services") - font.pixelSize: 20 - - } - - SortFilterProxyModel { - id: proxyContainersModel - sourceModel: UiLogic.containersModel - filters: ValueFilter { - roleName: "is_installed_role" - value: true - } - } - - SortFilterProxyModel { - id: proxyProtocolsModel - sourceModel: UiLogic.protocolsModel - filters: ValueFilter { - roleName: "is_installed_role" - value: true - } - } - - - ListView { - id: tb_c - x: 10 - width: parent.width - 10 - height: tb_c.contentItem.height - currentIndex: -1 - spacing: 5 - clip: true - interactive: false - model: proxyContainersModel - - delegate: Item { - implicitWidth: tb_c.width - 10 - implicitHeight: c_item.height - Item { - id: c_item - width: parent.width - height: row_container.height + tb_p.height - anchors.left: parent.left - Rectangle { - anchors.top: parent.top - width: parent.width - height: 1 - color: "lightgray" - visible: index !== tb_c.currentIndex - } - Rectangle { - anchors.top: row_container.top - anchors.bottom: row_container.bottom - anchors.left: parent.left - anchors.right: parent.right - - color: "#63B4FB" - visible: index === tb_c.currentIndex - } - - RowLayout { - id: row_container - //width: parent.width - anchors.left: parent.left - anchors.right: parent.right - -// anchors.top: lb_container_name.top -// anchors.bottom: lb_container_name.bottom - - Text { - id: lb_container_name - text: name_role - font.pixelSize: 17 - //font.bold: true - color: "#100A44" - topPadding: 5 - bottomPadding: 5 - leftPadding: 10 - verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - Layout.fillWidth: true - - MouseArea { - enabled: col.visible - anchors.top: lb_container_name.top - anchors.bottom: lb_container_name.bottom - anchors.left: parent.left - anchors.right: parent.right - propagateComposedEvents: true - onClicked: { - if (tb_c.currentIndex === index) tb_c.currentIndex = -1 - else tb_c.currentIndex = index - - UiLogic.protocolsModel.setSelectedDockerContainer(proxyContainersModel.mapToSource(index)) - } - } - } - - ImageButtonType { - id: button_remove - visible: index === tb_c.currentIndex - Layout.alignment: Qt.AlignRight - checkable: true - icon.source: "qrc:/images/delete.png" - implicitWidth: 30 - implicitHeight: 30 - - checked: default_role - - MessageDialog { - id: dialogRemove - standardButtons: StandardButton.Yes | StandardButton.Cancel - title: "AmneziaVPN" - text: qsTr("Remove container") + " " + name_role + "?" + "\n" + qsTr("This action will erase all data of this container on the server.") - onAccepted: { - tb_c.currentIndex = -1 - ServerContainersLogic.onPushButtonRemoveClicked(proxyContainersModel.mapToSource(index)) - } - } - - onClicked: dialogRemove.open() - - VisibleBehavior on visible { } - } - - ImageButtonType { - id: button_share - visible: index === tb_c.currentIndex - Layout.alignment: Qt.AlignRight - icon.source: "qrc:/images/share.png" - implicitWidth: 30 - implicitHeight: 30 - onClicked: { - ServerContainersLogic.onPushButtonShareClicked(proxyContainersModel.mapToSource(index)) - } - - VisibleBehavior on visible { } - } - - ImageButtonType { - id: button_default - visible: service_type_role == ProtocolEnum.Vpn - - Layout.alignment: Qt.AlignRight - checkable: true - img.source: checked ? "qrc:/images/check.png" : "qrc:/images/uncheck.png" - implicitWidth: 30 - implicitHeight: 30 - - checked: default_role - onClicked: { - ServerContainersLogic.onPushButtonDefaultClicked(proxyContainersModel.mapToSource(index)) - } - } - } - - - ListView { - id: tb_p - currentIndex: -1 - visible: index === tb_c.currentIndex - x: 10 - anchors.top: row_container.bottom - - width: parent.width - 40 - height: visible ? tb_p.contentItem.height : 0 - - spacing: 0 - clip: true - interactive: false - model: proxyProtocolsModel - - VisibleBehavior on visible { } - - - delegate: Item { - id: dp_item - - implicitWidth: tb_p.width - 10 - implicitHeight: p_item.height - Item { - id: p_item - width: parent.width - height: lb_protocol_name.height - anchors.left: parent.left - Rectangle { - anchors.top: parent.top - width: parent.width - height: 1 - color: "lightgray" - visible: index !== tb_p.currentIndex - } -// Rectangle { -// anchors.top: lb_protocol_name.top -// anchors.bottom: lb_protocol_name.bottom -// width: parent.width - -// color: "#63B4FB" -// visible: index === tb_p.currentIndex -// } - -// Text { -// id: lb_protocol_name -// text: name_role -// font.pixelSize: 16 -// topPadding: 5 -// bottomPadding: 5 -// leftPadding: 10 -// verticalAlignment: Text.AlignVCenter -// wrapMode: Text.WordWrap -// } - - SettingButtonType { - id: lb_protocol_name - -// anchors.top: lb_protocol_name.top -// anchors.bottom: lb_protocol_name.bottom - topPadding: 10 - bottomPadding: 10 - leftPadding: 10 - - anchors.left: parent.left - - width: parent.width - height: 30 - text: qsTr(name_role + " settings") - textItem.font.pixelSize: 16 - icon.source: "qrc:/images/settings.png" - onClicked: { - tb_p.currentIndex = index - ServerContainersLogic.onPushButtonProtoSettingsClicked( - proxyContainersModel.mapToSource(tb_c.currentIndex), - proxyProtocolsModel.mapToSource(tb_p.currentIndex)) - } - } - } - } - } - } - } - } - } - } - - - BlueButtonType { - id: pb_add_container - visible: container_selector.selectedIndex < 0 - - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.topMargin: 10 - anchors.bottomMargin: 20 - - width: parent.width - 40 - height: 40 - text: qsTr("Install new protocols container") - font.pixelSize: 16 - onClicked: container_selector.visible ? container_selector.close() : container_selector.open() - - } -} +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.15 +import SortFilterProxyModel 0.2 +import ContainerProps 1.0 +import ProtocolProps 1.0 +import PageEnum 1.0 +import ProtocolEnum 1.0 +import "./" +import "../Controls" +import "../Config" +import "InstallSettings" + +PageBase { + id: root + page: PageEnum.ServerContainers + logic: ServerContainersLogic + + enabled: ServerContainersLogic.pageEnabled + + function resetPage() { + container_selector.selectedIndex = -1 + } + + Connections { + target: logic + function onUpdatePage() { + root.resetPage() + } + } + + BackButton { + id: back + } + Caption { + id: caption + text: container_selector.selectedIndex > 0 ? qsTr("Install new service") : qsTr("Installed services") + } + + SelectContainer { + id: container_selector + + onAboutToHide: { + pageLoader.focus = true + } + + onContainerSelected: { + var containerProto = ContainerProps.defaultProtocol(c_index) + + + if (ProtocolProps.defaultPort(containerProto) < 0) { + tf_port_num.enabled = false + tf_port_num.text = qsTr("Default") + } + else tf_port_num.text = ProtocolProps.defaultPort(containerProto) + + cb_port_proto.currentIndex = ProtocolProps.defaultTransportProto(containerProto) + + tf_port_num.enabled = ProtocolProps.defaultPortChangeable(containerProto) + cb_port_proto.enabled = ProtocolProps.defaultTransportProtoChangeable(containerProto) + } + } + + Column { + id: c1 + visible: container_selector.selectedIndex > 0 + width: parent.width + anchors.top: caption.bottom + anchors.topMargin: 10 + + Caption { + font.pixelSize: 22 + text: UiLogic.containerName(container_selector.selectedIndex) + } + + Text { + width: parent.width + anchors.topMargin: 10 + padding: 10 + + font.family: "Lato" + font.styleName: "normal" + font.pixelSize: 16 + color: "#181922" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + + text: UiLogic.containerDesc(container_selector.selectedIndex) + } + } + + Rectangle { + id: frame_settings + visible: container_selector.selectedIndex > 0 + width: parent.width + anchors.top: c1.bottom + anchors.topMargin: 10 + + border.width: 1 + border.color: "lightgray" + anchors.bottomMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + radius: 2 + Grid { + id: grid + visible: container_selector.selectedIndex > 0 + anchors.fill: parent + columns: 2 + horizontalItemAlignment: Grid.AlignHCenter + verticalItemAlignment: Grid.AlignVCenter + topPadding: 5 + leftPadding: 10 + spacing: 5 + + + LabelType { + width: 130 + text: qsTr("Port") + } + TextFieldType { + id: tf_port_num + width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 + } + LabelType { + width: 130 + text: qsTr("Network Protocol") + } + ComboBoxType { + id: cb_port_proto + width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 + model: [ + qsTr("udp"), + qsTr("tcp"), + ] + } + } + } + + BlueButtonType { + id: pb_cancel_add + visible: container_selector.selectedIndex > 0 + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: pb_continue_add.top + anchors.bottomMargin: 20 + + width: parent.width - 40 + height: 40 + text: qsTr("Cancel") + font.pixelSize: 16 + onClicked: container_selector.selectedIndex = -1 + + } + + BlueButtonType { + id: pb_continue_add + visible: container_selector.selectedIndex > 0 + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + + width: parent.width - 40 + height: 40 + text: qsTr("Continue") + font.pixelSize: 16 + onClicked: { + let cont = container_selector.selectedIndex + let tp = ProtocolProps.transportProtoFromString(cb_port_proto.currentText) + let port = tf_port_num.text + ServerContainersLogic.onPushButtonContinueClicked(cont, port, tp) + } + } + + + + + + Flickable { + visible: container_selector.selectedIndex <= 0 + clip: true + width: parent.width + anchors.top: caption.bottom + anchors.bottom: pb_add_container.top + contentHeight: col.height + + Column { + visible: container_selector.selectedIndex <= 0 + id: col + anchors { + left: parent.left; + right: parent.right; + } + topPadding: 20 + spacing: 10 + + Caption { + id: cap1 + text: qsTr("Installed Protocols and Services") + font.pixelSize: 20 + + } + + SortFilterProxyModel { + id: proxyContainersModel + sourceModel: UiLogic.containersModel + filters: ValueFilter { + roleName: "is_installed_role" + value: true + } + } + + SortFilterProxyModel { + id: proxyProtocolsModel + sourceModel: UiLogic.protocolsModel + filters: ValueFilter { + roleName: "is_installed_role" + value: true + } + } + + + ListView { + id: tb_c + x: 10 + width: parent.width - 10 + height: tb_c.contentItem.height + currentIndex: -1 + spacing: 5 + clip: true + interactive: false + model: proxyContainersModel + + delegate: Item { + implicitWidth: tb_c.width - 10 + implicitHeight: c_item.height + Item { + id: c_item + width: parent.width + height: row_container.height + tb_p.height + anchors.left: parent.left + Rectangle { + anchors.top: parent.top + width: parent.width + height: 1 + color: "lightgray" + visible: index !== tb_c.currentIndex + } + Rectangle { + anchors.top: row_container.top + anchors.bottom: row_container.bottom + anchors.left: parent.left + anchors.right: parent.right + + color: "#63B4FB" + visible: index === tb_c.currentIndex + } + + RowLayout { + id: row_container + anchors.left: parent.left + anchors.right: parent.right + + Text { + id: lb_container_name + text: name_role + font.pixelSize: 17 + color: "#100A44" + topPadding: 16 + bottomPadding: 12 + leftPadding: 10 + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + Layout.fillWidth: true + + MouseArea { + enabled: col.visible + anchors.top: lb_container_name.top + anchors.bottom: lb_container_name.bottom + anchors.left: parent.left + anchors.right: parent.right + propagateComposedEvents: true + onClicked: { + if (tb_c.currentIndex === index) tb_c.currentIndex = -1 + else tb_c.currentIndex = index + + UiLogic.protocolsModel.setSelectedDockerContainer(proxyContainersModel.mapToSource(index)) + } + } + } + + ImageButtonType { + id: button_remove + visible: index === tb_c.currentIndex + Layout.alignment: Qt.AlignRight + checkable: true + icon.source: "qrc:/images/delete.png" + implicitWidth: 30 + implicitHeight: 30 + + checked: default_role + + MessageDialog { + id: dialogRemove + standardButtons: StandardButton.Yes | StandardButton.Cancel + title: "AmneziaVPN" + text: qsTr("Remove container") + " " + name_role + "?" + "\n" + qsTr("This action will erase all data of this container on the server.") + onAccepted: { + tb_c.currentIndex = -1 + ServerContainersLogic.onPushButtonRemoveClicked(proxyContainersModel.mapToSource(index)) + } + } + + onClicked: dialogRemove.open() + + VisibleBehavior on visible { } + } + + ImageButtonType { + id: button_share + visible: index === tb_c.currentIndex + Layout.alignment: Qt.AlignRight + icon.source: "qrc:/images/share.png" + implicitWidth: 30 + implicitHeight: 30 + onClicked: { + ServerContainersLogic.onPushButtonShareClicked(proxyContainersModel.mapToSource(index)) + } + + VisibleBehavior on visible { } + } + + ImageButtonType { + id: button_default + visible: service_type_role == ProtocolEnum.Vpn + + Layout.alignment: Qt.AlignRight + checkable: true + img.source: checked ? "qrc:/images/check.png" : "qrc:/images/uncheck.png" + implicitWidth: 30 + implicitHeight: 30 + + checked: default_role + onClicked: { + ServerContainersLogic.onPushButtonDefaultClicked(proxyContainersModel.mapToSource(index)) + } + } + } + + + ListView { + id: tb_p + currentIndex: -1 + x: 10 + anchors.top: row_container.bottom + + width: parent.width - 40 + height: index === tb_c.currentIndex ? tb_p.contentItem.height : 0 + implicitHeight: height + + spacing: 0 + clip: true + interactive: false + model: proxyProtocolsModel + + + Behavior on height { + NumberAnimation { + duration: 200 + } + } + + delegate: Item { + id: dp_item + + implicitWidth: tb_p.width - 10 + implicitHeight: p_item.height + Item { + id: p_item + width: parent.width + height: lb_protocol_name.height + anchors.left: parent.left + Rectangle { + anchors.top: parent.top + width: parent.width + height: 1 + color: "lightgray" + visible: index > 0 + } + + SettingButtonType { + id: lb_protocol_name + topPadding: 10 + bottomPadding: 10 + + anchors.left: parent.left + anchors.leftMargin: 10 + + width: parent.width + height: 45 + text: qsTr(name_role + " settings") + textItem.font.pixelSize: 16 + icon.source: "qrc:/images/settings.png" + onClicked: { + tb_p.currentIndex = index + ServerContainersLogic.onPushButtonProtoSettingsClicked( + proxyContainersModel.mapToSource(tb_c.currentIndex), + proxyProtocolsModel.mapToSource(tb_p.currentIndex)) + } + } + } + } + } + } + } + } + } + } + + + BlueButtonType { + id: pb_add_container + visible: container_selector.selectedIndex < 0 + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.topMargin: 10 + anchors.bottomMargin: 20 + + width: parent.width - 40 + height: 40 + text: qsTr("Install new protocols container") + font.pixelSize: 16 + onClicked: container_selector.visible ? container_selector.close() : container_selector.open() + + } +} diff --git a/client/ui/qml/Pages/PageShareConnection.qml b/client/ui/qml/Pages/PageShareConnection.qml index c124c969..9bd84bdd 100644 --- a/client/ui/qml/Pages/PageShareConnection.qml +++ b/client/ui/qml/Pages/PageShareConnection.qml @@ -59,7 +59,7 @@ PageBase { text: qsTr("Share for Amnezia") height: 40 width: tb_c.width - 10 - onClicked: UiLogic.onGotoShareProtocolPage(ProtocolEnum.Any) + onClicked: UiLogic.goToShareProtocolPage(ProtocolEnum.Any) } ListView { @@ -82,7 +82,7 @@ PageBase { text: qsTr("Share for ") + name_role height: 40 width: tb_c.width - 10 - onClicked: UiLogic.onGotoShareProtocolPage(proxyProtocolsModel.mapToSource(index)) + onClicked: UiLogic.goToShareProtocolPage(proxyProtocolsModel.mapToSource(index)) } } } diff --git a/client/ui/qml/Pages/PageStart.qml b/client/ui/qml/Pages/PageStart.qml index 3ff11a86..d7f2a49c 100644 --- a/client/ui/qml/Pages/PageStart.qml +++ b/client/ui/qml/Pages/PageStart.qml @@ -119,7 +119,6 @@ PageBase { anchors.topMargin: 40 text: qsTr("Open file") - visible: StartPageLogic.pushButtonConnectVisible onClicked: { StartPageLogic.onPushButtonImportOpenFile() } @@ -128,12 +127,12 @@ PageBase { BlueButtonType { id: qr_code_import + visible: GC.isMobile() anchors.horizontalCenter: parent.horizontalCenter anchors.top: qr_code_import_open.bottom anchors.topMargin: 10 text: qsTr("Scan QR code") - visible: StartPageLogic.pushButtonConnectVisible onClicked: { if (Qt.platform.os == "ios") { UiLogic.goToPage(PageEnum.QrDecoderIos) @@ -144,7 +143,19 @@ PageBase { enabled: StartPageLogic.pushButtonConnectEnabled } + BlueButtonType { + id: btn_restore_cfg + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: qr_code_import.bottom + anchors.topMargin: 30 + visible: UiLogic.pagesStackDepth == 1 + enabled: StartPageLogic.pushButtonConnectEnabled + text: qsTr("Restore app config") + onClicked: { + AppSettingsLogic.onPushButtonRestoreAppConfigClicked() + } + } } @@ -270,7 +281,6 @@ PageBase { anchors.topMargin: 10 text: StartPageLogic.pushButtonConnectText - visible: StartPageLogic.pushButtonConnectVisible onClicked: { StartPageLogic.onPushButtonConnect() } diff --git a/client/ui/qml/Pages/PageViewConfig.qml b/client/ui/qml/Pages/PageViewConfig.qml new file mode 100644 index 00000000..acefb9b3 --- /dev/null +++ b/client/ui/qml/Pages/PageViewConfig.qml @@ -0,0 +1,148 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import PageEnum 1.0 +import "./" +import "../Controls" +import "../Config" + +PageBase { + id: root + page: PageEnum.ViewConfig + logic: ViewConfigLogic + + readonly property double rowHeight: ta_last_config.contentHeight / ta_last_config.textArea.lineCount + + BackButton {} + + Caption { + id: caption + text: qsTr("Check config") + } + + Flickable { + id: fl + width: root.width + anchors.top: caption.bottom + anchors.topMargin: 20 + anchors.bottom: root.bottom + anchors.bottomMargin: 20 + anchors.left: root.left + anchors.leftMargin: 30 + anchors.right: root.right + anchors.rightMargin: 30 + + contentHeight: content.height + clip: true + + ColumnLayout { + id: content + enabled: logic.pageEnabled + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + TextAreaType { + id: ta_config + + Layout.topMargin: 5 + Layout.bottomMargin: 20 + Layout.fillWidth: true + Layout.leftMargin: 1 + Layout.rightMargin: 1 + Layout.preferredHeight: ViewConfigLogic.warningActive ? 250 : fl.height - 70 + flickableDirection: Flickable.AutoFlickIfNeeded + + textArea.readOnly: true + textArea.text: logic.configText + } + + LabelType { + id: lb_att + visible: ViewConfigLogic.warningActive + text: qsTr("Attention! +The config above contains cached OpenVPN connection profile. +AmneziaVPN detected this profile may contain malicious scripts. Please, carefully review the config and import this config only if you completely trust it.") + Layout.fillWidth: true + } + + LabelType { + visible: ViewConfigLogic.warningActive + text: qsTr("Suspicious string:") + Layout.fillWidth: true + } + + TextAreaType { + id: ta_mal + visible: ViewConfigLogic.warningActive + + Layout.topMargin: 5 + Layout.bottomMargin: 20 + Layout.fillWidth: true + Layout.leftMargin: 1 + Layout.rightMargin: 1 + Layout.preferredHeight: 60 + flickableDirection: Flickable.AutoFlickIfNeeded + + textArea.readOnly: true + textArea.text: logic.openVpnMalStrings + textArea.textFormat: TextEdit.RichText + } + + LabelType { + visible: ViewConfigLogic.warningActive + text: qsTr("Cached connection profile:") + Layout.fillWidth: true + } + + TextAreaType { + id: ta_last_config + visible: ViewConfigLogic.warningActive + + Layout.topMargin: 5 + Layout.bottomMargin: 20 + Layout.fillWidth: true + Layout.leftMargin: 1 + Layout.rightMargin: 1 + Layout.preferredHeight: 350 + flickableDirection: Flickable.AutoFlickIfNeeded + + textArea.readOnly: true + textArea.text: logic.openVpnLastConfigs + textArea.textFormat: TextEdit.RichText + + Connections { + target: logic + function onWarningStringNumberChanged(n) { + ta_last_config.contentY = rowHeight * n - ta_last_config.height / 2 + } + } + } + + RowLayout { + id: btns_row + + BasicButtonType { + Layout.preferredWidth: (content.width - parent.spacing) /2 + Layout.preferredHeight: 41 + font.pixelSize: btn_import.font.pixelSize + text: qsTr("Cancel") + onClicked: { + UiLogic.closePage() + } + } + + BlueButtonType { + id: btn_import + Layout.preferredWidth: (content.width - parent.spacing) /2 + Layout.preferredHeight: 41 + text: qsTr("Import config") + onClicked: { + logic.importConfig() + } + } + } + } + } + + } diff --git a/client/ui/systemtray_notificationhandler.cpp b/client/ui/systemtray_notificationhandler.cpp index 21e24ac1..e142caf5 100644 --- a/client/ui/systemtray_notificationhandler.cpp +++ b/client/ui/systemtray_notificationhandler.cpp @@ -75,7 +75,6 @@ void SystemTrayNotificationHandler::onTrayActivated(QSystemTrayIcon::ActivationR void SystemTrayNotificationHandler::setTrayState(VpnProtocol::VpnConnectionState state) { - qDebug() << "SystemTrayNotificationHandler::setTrayState" << state; QString resourcesPath = ":/images/tray/%1"; switch (state) { diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index 5f5cb619..6a0b8d20 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -16,9 +15,11 @@ #include #include #include -#include #include -#include +#include + +#include "amnezia_application.h" + #include "configurators/cloak_configurator.h" #include "configurators/vpn_configurator.h" #include "configurators/openvpn_configurator.h" @@ -62,6 +63,7 @@ #include "pages_logic/ShareConnectionLogic.h" #include "pages_logic/SitesLogic.h" #include "pages_logic/StartPageLogic.h" +#include "pages_logic/ViewConfigLogic.h" #include "pages_logic/VpnLogic.h" #include "pages_logic/WizardLogic.h" @@ -74,30 +76,20 @@ using namespace amnezia; using namespace PageEnumNS; -UiLogic::UiLogic(QObject *parent) : +UiLogic::UiLogic(std::shared_ptr settings, std::shared_ptr configurator, + std::shared_ptr serverController, + QObject *parent) : QObject(parent), - m_dialogConnectErrorText{} + m_settings(settings), + m_configurator(configurator), + m_serverController(serverController) { - m_containersModel = new ContainersModel(this); - m_protocolsModel = new ProtocolsModel(this); - m_vpnConnection = new VpnConnection(); + m_containersModel = new ContainersModel(settings, this); + m_protocolsModel = new ProtocolsModel(settings, this); + m_vpnConnection = new VpnConnection(settings, configurator, serverController); m_vpnConnection->moveToThread(&m_vpnConnectionThread); m_vpnConnectionThread.start(); - m_appSettingsLogic = new AppSettingsLogic(this); - m_generalSettingsLogic = new GeneralSettingsLogic(this); - m_networkSettingsLogic = new NetworkSettingsLogic(this); - m_newServerProtocolsLogic = new NewServerProtocolsLogic(this); - m_qrDecoderLogic = new QrDecoderLogic(this); - m_serverConfiguringProgressLogic = new ServerConfiguringProgressLogic(this); - m_serverListLogic = new ServerListLogic(this); - m_serverSettingsLogic = new ServerSettingsLogic(this); - m_serverprotocolsLogic = new ServerContainersLogic(this); - m_shareConnectionLogic = new ShareConnectionLogic(this); - m_sitesLogic = new SitesLogic(this); - m_startPageLogic = new StartPageLogic(this); - m_vpnLogic = new VpnLogic(this); - m_wizardLogic = new WizardLogic(this); m_protocolLogicMap.insert(Proto::OpenVpn, new OpenVpnLogic(this)); m_protocolLogicMap.insert(Proto::ShadowSocks, new ShadowSocksLogic(this)); @@ -139,55 +131,40 @@ void UiLogic::initalizeUiLogic() #ifdef Q_OS_ANDROID connect(AndroidController::instance(), &AndroidController::initialized, [this](bool status, bool connected, const QDateTime& connectionDate) { if (connected) { - vpnLogic()->onConnectionStateChanged(VpnProtocol::Connected); + pageLogic()->onConnectionStateChanged(VpnProtocol::Connected); } }); if (!AndroidController::instance()->initialize()) { - qDebug() << QString("Init failed") ; + qCritical() << QString("Init failed") ; emit VpnProtocol::Error; return; } #endif - qDebug() << "UiLogic::initalizeUiLogic()"; - m_notificationHandler = NotificationHandler::create(qmlRoot()); connect(m_vpnConnection, &VpnConnection::connectionStateChanged, m_notificationHandler, &NotificationHandler::setConnectionState); connect(m_notificationHandler, &NotificationHandler::raiseRequested, this, &UiLogic::raise); - connect(m_notificationHandler, &NotificationHandler::connectRequested, vpnLogic(), &VpnLogic::onConnect); - connect(m_notificationHandler, &NotificationHandler::disconnectRequested, vpnLogic(), &VpnLogic::onDisconnect); + connect(m_notificationHandler, &NotificationHandler::connectRequested, pageLogic(), &VpnLogic::onConnect); + connect(m_notificationHandler, &NotificationHandler::disconnectRequested, pageLogic(), &VpnLogic::onDisconnect); - if (m_settings.serversCount() > 0) { - if (m_settings.defaultServerIndex() < 0) m_settings.setDefaultServer(0); + if (m_settings->serversCount() > 0) { + if (m_settings->defaultServerIndex() < 0) m_settings->setDefaultServer(0); emit goToPage(Page::Vpn, true, false); } else { emit goToPage(Page::Start, true, false); } - selectedServerIndex = m_settings.defaultServerIndex(); + selectedServerIndex = m_settings->defaultServerIndex(); qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME).arg(APP_VERSION); qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName()).arg(QSysInfo::currentCpuArchitecture()); } -QString UiLogic::getDialogConnectErrorText() const -{ - return m_dialogConnectErrorText; -} - -void UiLogic::setDialogConnectErrorText(const QString &dialogConnectErrorText) -{ - if (m_dialogConnectErrorText != dialogConnectErrorText) { - m_dialogConnectErrorText = dialogConnectErrorText; - emit dialogConnectErrorTextChanged(); - } -} - void UiLogic::showOnStartup() { - if (! m_settings.isStartMinimized()) { + if (! m_settings->isStartMinimized()) { emit show(); } else { @@ -201,21 +178,7 @@ void UiLogic::showOnStartup() void UiLogic::onUpdateAllPages() { - for (PageLogicBase *logic : { - (PageLogicBase *) m_appSettingsLogic, - (PageLogicBase *) m_generalSettingsLogic, - (PageLogicBase *) m_networkSettingsLogic, - (PageLogicBase *) m_serverConfiguringProgressLogic, - (PageLogicBase *) m_newServerProtocolsLogic, - (PageLogicBase *) m_serverListLogic, - (PageLogicBase *) m_serverSettingsLogic, - (PageLogicBase *) m_serverprotocolsLogic, - (PageLogicBase *) m_shareConnectionLogic, - (PageLogicBase *) m_sitesLogic, - (PageLogicBase *) m_startPageLogic, - (PageLogicBase *) m_vpnLogic, - (PageLogicBase *) m_wizardLogic - }) { + for (auto logic : m_logicMap) { logic->onUpdatePage(); } } @@ -235,30 +198,29 @@ void UiLogic::keyPressEvent(Qt::Key key) qApp->quit(); break; case Qt::Key_H: - selectedServerIndex = m_settings.defaultServerIndex(); - selectedDockerContainer = m_settings.defaultContainer(selectedServerIndex); + selectedServerIndex = m_settings->defaultServerIndex(); + selectedDockerContainer = m_settings->defaultContainer(selectedServerIndex); - //updateSharingPage(selectedServerIndex, m_settings.serverCredentials(selectedServerIndex), selectedDockerContainer); + //updateSharingPage(selectedServerIndex, m_settings->serverCredentials(selectedServerIndex), selectedDockerContainer); emit goToPage(Page::ShareConnection); break; #endif case Qt::Key_C: - qDebug().noquote() << "Def server" << m_settings.defaultServerIndex() << m_settings.defaultContainerName(m_settings.defaultServerIndex()); - //qDebug().noquote() << QJsonDocument(m_settings.containerConfig(m_settings.defaultServerIndex(), m_settings.defaultContainer(m_settings.defaultServerIndex()))).toJson(); - qDebug().noquote() << QJsonDocument(m_settings.defaultServer()).toJson(); + qDebug().noquote() << "Def server" << m_settings->defaultServerIndex() << m_settings->defaultContainerName(m_settings->defaultServerIndex()); + qDebug().noquote() << QJsonDocument(m_settings->defaultServer()).toJson(); break; case Qt::Key_A: emit goToPage(Page::Start); break; case Qt::Key_S: - selectedServerIndex = m_settings.defaultServerIndex(); + selectedServerIndex = m_settings->defaultServerIndex(); emit goToPage(Page::ServerSettings); break; case Qt::Key_P: onGotoCurrentProtocolsPage(); break; case Qt::Key_T: - SshConfigurator::openSshTerminal(m_settings.serverCredentials(m_settings.defaultServerIndex())); + m_configurator->sshConfigurator->openSshTerminal(m_settings->serverCredentials(m_settings->defaultServerIndex())); break; case Qt::Key_Escape: case Qt::Key_Back: @@ -280,7 +242,7 @@ void UiLogic::keyPressEvent(Qt::Key key) void UiLogic::onCloseWindow() { - if (m_settings.serversCount() == 0) qApp->quit(); + if (m_settings->serversCount() == 0) qApp->quit(); else { hide(); } @@ -299,8 +261,8 @@ QString UiLogic::containerDesc(int container) void UiLogic::onGotoCurrentProtocolsPage() { - selectedServerIndex = m_settings.defaultServerIndex(); - selectedDockerContainer = m_settings.defaultContainer(selectedServerIndex); + selectedServerIndex = m_settings->defaultServerIndex(); + selectedDockerContainer = m_settings->defaultContainer(selectedServerIndex); emit goToPage(Page::ServerContainers); } @@ -344,34 +306,34 @@ void UiLogic::installServer(QMap &containers) PageFunc page_new_server_configuring; page_new_server_configuring.setEnabledFunc = [this] (bool enabled) -> void { - serverConfiguringProgressLogic()->set_pageEnabled(enabled); + pageLogic()->set_pageEnabled(enabled); }; ButtonFunc no_button; LabelFunc label_new_server_configuring_wait_info; label_new_server_configuring_wait_info.setTextFunc = [this] (const QString& text) -> void { - serverConfiguringProgressLogic()->set_labelWaitInfoText(text); + pageLogic()->set_labelWaitInfoText(text); }; label_new_server_configuring_wait_info.setVisibleFunc = [this] (bool visible) ->void { - serverConfiguringProgressLogic()->set_labelWaitInfoVisible(visible); + pageLogic()->set_labelWaitInfoVisible(visible); }; ProgressFunc progressBar_new_server_configuring; progressBar_new_server_configuring.setVisibleFunc = [this] (bool visible) ->void { - serverConfiguringProgressLogic()->set_progressBarVisible(visible); + pageLogic()->set_progressBarVisible(visible); }; progressBar_new_server_configuring.setValueFunc = [this] (int value) ->void { - serverConfiguringProgressLogic()->set_progressBarValue(value); + pageLogic()->set_progressBarValue(value); }; progressBar_new_server_configuring.getValueFunc = [this] (void) -> int { - return serverConfiguringProgressLogic()->progressBarValue(); + return pageLogic()->progressBarValue(); }; progressBar_new_server_configuring.getMaximiumFunc = [this] (void) -> int { - return serverConfiguringProgressLogic()->progressBarMaximium(); + return pageLogic()->progressBarMaximium(); }; progressBar_new_server_configuring.setTextVisibleFunc = [this] (bool visible) ->void { - serverConfiguringProgressLogic()->set_progressBarTextVisible(visible); + pageLogic()->set_progressBarTextVisible(visible); }; progressBar_new_server_configuring.setTextFunc = [this] (const QString& text) ->void { - serverConfiguringProgressLogic()->set_progressBarText(text); + pageLogic()->set_progressBarText(text); }; bool ok = installContainers(installCredentials, containers, page_new_server_configuring, @@ -385,7 +347,7 @@ void UiLogic::installServer(QMap &containers) server.insert(config_key::userName, installCredentials.userName); server.insert(config_key::password, installCredentials.password); server.insert(config_key::port, installCredentials.port); - server.insert(config_key::description, m_settings.nextAvailableServerName()); + server.insert(config_key::description, m_settings->nextAvailableServerName()); QJsonArray containerConfigs; for (const QJsonObject &cfg : containers) { @@ -394,8 +356,8 @@ void UiLogic::installServer(QMap &containers) server.insert(config_key::containers, containerConfigs); server.insert(config_key::defaultContainer, ContainerProps::containerToString(containers.firstKey())); - m_settings.addServer(server); - m_settings.setDefaultServer(m_settings.serversCount() - 1); + m_settings->addServer(server); + m_settings->setDefaultServer(m_settings->serversCount() - 1); onUpdateAllPages(); emit setStartPage(Page::Vpn); @@ -442,9 +404,9 @@ bool UiLogic::installContainers(ServerCredentials credentials, progress.setTextVisibleFunc(true); progress.setTextFunc(QString("Installing %1 %2 %3").arg(cnt+1).arg(tr("of")).arg(containers.size())); - ErrorCode e = ServerController::setupContainer(credentials, i.key(), i.value()); + ErrorCode e = m_serverController->setupContainer(credentials, i.key(), i.value()); qDebug() << "Setup server finished with code" << e; - ServerController::disconnectFromHost(credentials); + m_serverController->disconnectFromHost(credentials); if (e) { if (page.setEnabledFunc) { @@ -587,7 +549,7 @@ PageProtocolLogicBase *UiLogic::protocolLogic(Proto p) PageProtocolLogicBase *logic = m_protocolLogicMap.value(p); if (logic) return logic; else { - qDebug() << "UiLogic::protocolLogic Warning: logic missing for" << p; + qCritical() << "UiLogic::protocolLogic Warning: logic missing for" << p; return new PageProtocolLogicBase(this); } } @@ -607,6 +569,11 @@ NotificationHandler *UiLogic::notificationHandler() const return m_notificationHandler; } +void UiLogic::setQmlContextProperty(PageLogicBase *logic) +{ + amnApp->qmlEngine()->rootContext()->setContextProperty(logic->metaObject()->className(), logic); +} + PageEnumNS::Page UiLogic::currentPage() { return static_cast(currentPageValue()); @@ -632,13 +599,11 @@ void UiLogic::saveTextFile(const QString& desc, const QString& suggestedName, QS QUrl::fromLocalFile(docDir), "*" + ext); #endif - qDebug() << "UiLogic::saveTextFile" << fileName; if (fileName.isEmpty()) return; #ifdef AMNEZIA_DESKTOP QFile save(fileName.toLocalFile()); #else - qDebug() << "UiLogic::saveTextFile" << QQmlFile::urlToLocalFileOrQrc(fileName); QFile save(QQmlFile::urlToLocalFileOrQrc(fileName)); #endif @@ -681,7 +646,6 @@ void UiLogic::shareTempFile(const QString &suggestedName, QString ext, const QSt if (!fileName.endsWith(ext)) fileName.append(ext); QFile::remove(fileName); - qDebug() << "UiLogic::shareTempFile" << fileName; QFile save(fileName); save.open(QIODevice::WriteOnly); @@ -692,3 +656,24 @@ void UiLogic::shareTempFile(const QString &suggestedName, QString ext, const QSt filesToSend.append(fileName); MobileUtils::shareText(filesToSend); } + +void UiLogic::registerPagesLogic() +{ + amnApp->qmlEngine()->rootContext()->setContextProperty("UiLogic", this); + + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); + registerPageLogic(); +} diff --git a/client/ui/uilogic.h b/client/ui/uilogic.h index 7a535270..82f2d6ea 100644 --- a/client/ui/uilogic.h +++ b/client/ui/uilogic.h @@ -7,6 +7,10 @@ #include #include +#include +#include +#include + #include "property_helper.h" #include "pages.h" #include "protocols/vpnprotocol.h" @@ -16,7 +20,12 @@ #include "models/protocols_model.h" #include "notificationhandler.h" -#include "settings.h" + +class Settings; +class VpnConfigurator; +class ServerController; + +class PageLogicBase; class AppSettingsLogic; class GeneralSettingsLogic; @@ -30,6 +39,7 @@ class ServerContainersLogic; class ShareConnectionLogic; class SitesLogic; class StartPageLogic; +class ViewConfigLogic; class VpnLogic; class WizardLogic; @@ -50,18 +60,19 @@ class UiLogic : public QObject AUTO_PROPERTY(bool, pageEnabled) AUTO_PROPERTY(int, pagesStackDepth) AUTO_PROPERTY(int, currentPageValue) + AUTO_PROPERTY(QString, dialogConnectErrorText) READONLY_PROPERTY(QObject *, containersModel) READONLY_PROPERTY(QObject *, protocolsModel) - // TODO: review - Q_PROPERTY(QString dialogConnectErrorText READ getDialogConnectErrorText WRITE setDialogConnectErrorText NOTIFY dialogConnectErrorTextChanged) - public: - explicit UiLogic(QObject *parent = nullptr); + explicit UiLogic(std::shared_ptr settings, std::shared_ptr configurator, + std::shared_ptr serverController, QObject *parent = nullptr); ~UiLogic(); void showOnStartup(); + friend class PageLogicBase; + friend class AppSettingsLogic; friend class GeneralSettingsLogic; friend class NetworkSettingsLogic; @@ -73,6 +84,7 @@ public: friend class ShareConnectionLogic; friend class SitesLogic; friend class StartPageLogic; + friend class ViewConfigLogic; friend class VpnLogic; friend class WizardLogic; @@ -92,10 +104,6 @@ public: Q_INVOKABLE QString containerName(int container); Q_INVOKABLE QString containerDesc(int container); - Q_INVOKABLE void onGotoPage(PageEnumNS::Page p, bool reset = true, bool slide = true) { emit goToPage(p, reset, slide); } - Q_INVOKABLE void onGotoProtocolPage(Proto p, bool reset = true, bool slide = true) { emit goToProtocolPage(p, reset, slide); } - Q_INVOKABLE void onGotoShareProtocolPage(Proto p, bool reset = true, bool slide = true) { emit goToShareProtocolPage(p, reset, slide); } - Q_INVOKABLE void onGotoCurrentProtocolsPage(); Q_INVOKABLE void keyPressEvent(Qt::Key key); @@ -106,9 +114,6 @@ public: void shareTempFile(const QString &suggestedName, QString ext, const QString& data); - QString getDialogConnectErrorText() const; - void setDialogConnectErrorText(const QString &dialogConnectErrorText); - signals: void dialogConnectErrorTextChanged(); @@ -125,9 +130,6 @@ signals: void raise(); void toggleLogPanel(); -private: - QString m_dialogConnectErrorText; - private slots: // containers - INOUT arg void installServer(QMap &containers); @@ -168,21 +170,6 @@ private: public: - AppSettingsLogic *appSettingsLogic() { return m_appSettingsLogic; } - GeneralSettingsLogic *generalSettingsLogic() { return m_generalSettingsLogic; } - NetworkSettingsLogic *networkSettingsLogic() { return m_networkSettingsLogic; } - NewServerProtocolsLogic *newServerProtocolsLogic() { return m_newServerProtocolsLogic; } - QrDecoderLogic *qrDecoderLogic() { return m_qrDecoderLogic; } - ServerConfiguringProgressLogic *serverConfiguringProgressLogic() { return m_serverConfiguringProgressLogic; } - ServerListLogic *serverListLogic() { return m_serverListLogic; } - ServerSettingsLogic *serverSettingsLogic() { return m_serverSettingsLogic; } - ServerContainersLogic *serverprotocolsLogic() { return m_serverprotocolsLogic; } - ShareConnectionLogic *shareConnectionLogic() { return m_shareConnectionLogic; } - SitesLogic *sitesLogic() { return m_sitesLogic; } - StartPageLogic *startPageLogic() { return m_startPageLogic; } - VpnLogic *vpnLogic() { return m_vpnLogic; } - WizardLogic *wizardLogic() { return m_wizardLogic; } - Q_INVOKABLE PageProtocolLogicBase *protocolLogic(Proto p); QObject *qmlRoot() const; @@ -190,29 +177,36 @@ public: NotificationHandler *notificationHandler() const; + void setQmlContextProperty(PageLogicBase *logic); + void registerPagesLogic(); + + template + void registerPageLogic() + { + T* logic = new T(this); + m_logicMap[std::type_index(typeid(T))] = logic; + setQmlContextProperty(logic); + } + + template + T* pageLogic() + { + return static_cast(m_logicMap.value(std::type_index(typeid(T)))); + } + private: QObject *m_qmlRoot{nullptr}; - AppSettingsLogic *m_appSettingsLogic; - GeneralSettingsLogic *m_generalSettingsLogic; - NetworkSettingsLogic *m_networkSettingsLogic; - NewServerProtocolsLogic *m_newServerProtocolsLogic; - QrDecoderLogic *m_qrDecoderLogic; - ServerConfiguringProgressLogic *m_serverConfiguringProgressLogic; - ServerListLogic *m_serverListLogic; - ServerSettingsLogic *m_serverSettingsLogic; - ServerContainersLogic *m_serverprotocolsLogic; - ShareConnectionLogic *m_shareConnectionLogic; - SitesLogic *m_sitesLogic; - StartPageLogic *m_startPageLogic; - VpnLogic *m_vpnLogic; - WizardLogic *m_wizardLogic; + QMap m_logicMap; QMap m_protocolLogicMap; VpnConnection* m_vpnConnection; QThread m_vpnConnectionThread; - Settings m_settings; + + std::shared_ptr m_settings; + std::shared_ptr m_configurator; + std::shared_ptr m_serverController; NotificationHandler* m_notificationHandler; diff --git a/client/utilities.cpp b/client/utilities.cpp index 4223014e..dc17e33f 100644 --- a/client/utilities.cpp +++ b/client/utilities.cpp @@ -122,7 +122,6 @@ QString Utils::getIPAddress(const QString& host) QList adresses = QHostInfo::fromName(host).addresses(); if (!adresses.isEmpty()) { - qDebug() << "Resolved address for" << host << adresses.first().toString(); return adresses.first().toString(); } qDebug() << "Unable to resolve address for " << host; @@ -216,6 +215,38 @@ QStringList Utils::summarizeRoutes(const QStringList &ips, const QString cidr) return QStringList(); } +QString Utils::openVpnExecPath() +{ +#ifdef Q_OS_WIN + return Utils::executable("openvpn/openvpn", true); +#elif defined Q_OS_LINUX + return Utils::usrExecutable("openvpn"); +#else + return Utils::executable("/openvpn", true); +#endif +} + +QString Utils::wireguardExecPath() +{ +#ifdef Q_OS_WIN + return Utils::executable("wireguard/wireguard-service", true); +#elif defined Q_OS_LINUX + return Utils::usrExecutable("wg"); +#else + return Utils::executable("/wireguard", true); +#endif +} + +QString Utils::certUtilPath() +{ +#ifdef Q_OS_WIN + QString winPath = QString::fromUtf8(qgetenv("windir")); + return winPath + "system32\\certutil.exe"; +#else + return ""; +#endif +} + #ifdef Q_OS_WIN // Inspired from http://stackoverflow.com/a/15281070/1529139 // and http://stackoverflow.com/q/40059902/1529139 diff --git a/client/utilities.h b/client/utilities.h index 43c7d2fb..932b9b27 100644 --- a/client/utilities.h +++ b/client/utilities.h @@ -46,6 +46,11 @@ public: static QStringList summarizeRoutes(const QStringList &ips, const QString cidr); + static QString openVpnExecPath(); + static QString wireguardExecPath(); + static QString certUtilPath(); + + #ifdef Q_OS_WIN static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent); #endif diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index d848f4c2..5eff0175 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -29,9 +29,12 @@ #include "utilities.h" #include "vpnconnection.h" -VpnConnection::VpnConnection(QObject* parent) : QObject(parent), - m_settings(this) - +VpnConnection::VpnConnection(std::shared_ptr settings, + std::shared_ptr configurator, + std::shared_ptr serverController, QObject* parent) : QObject(parent), + m_settings(settings), + m_configurator(configurator), + m_serverController(serverController) { } @@ -56,7 +59,7 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState sta IpcClient::Interface()->resetIpStack(); IpcClient::Interface()->flushDns(); - if (m_settings.routeMode() != Settings::VpnAllSites) { + if (m_settings->routeMode() != Settings::VpnAllSites) { IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0"); //qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size(); } @@ -67,17 +70,17 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState sta QStringList() << dns1 << dns2); - if (m_settings.routeMode() == Settings::VpnOnlyForwardSites) { + if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { QTimer::singleShot(1000, m_vpnProtocol.data(), [this](){ - addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings.routeMode()); + addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); }); } - else if (m_settings.routeMode() == Settings::VpnAllExceptSites) { + else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1"); IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1"); IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress()); - addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings.routeMode()); + addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode()); } @@ -85,7 +88,7 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState sta else if (state == VpnProtocol::Error) { IpcClient::Interface()->flushDns(); - if (m_settings.routeMode() == Settings::VpnOnlyForwardSites) { + if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { IpcClient::Interface()->clearSavedRoutes(); } } @@ -104,7 +107,7 @@ void VpnConnection::addSitesRoutes(const QString &gw, Settings::RouteMode mode) #ifdef AMNEZIA_DESKTOP QStringList ips; QStringList sites; - const QVariantMap &m = m_settings.vpnSites(mode); + const QVariantMap &m = m_settings->vpnSites(mode); for (auto i = m.constBegin(); i != m.constEnd(); ++i) { if (Utils::checkIpSubnetFormat(i.key())) { ips.append(i.key()); @@ -132,7 +135,7 @@ void VpnConnection::addSitesRoutes(const QString &gw, Settings::RouteMode mode) //qDebug() << "VpnConnection::addSitesRoutes updating site" << site << ip; if (!ips.contains(ip)) { IpcClient::Interface()->routeAddList(gw, QStringList() << ip); - m_settings.addVpnSite(mode, site, ip); + m_settings->addVpnSite(mode, site, ip); } flushDns(); break; @@ -153,10 +156,10 @@ void VpnConnection::addRoutes(const QStringList &ips) { #ifdef AMNEZIA_DESKTOP if (connectionState() == VpnProtocol::Connected && IpcClient::Interface()) { - if (m_settings.routeMode() == Settings::VpnOnlyForwardSites) { + if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), ips); } - else if (m_settings.routeMode() == Settings::VpnAllExceptSites) { + else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), ips); } } @@ -167,10 +170,10 @@ void VpnConnection::deleteRoutes(const QStringList &ips) { #ifdef AMNEZIA_DESKTOP if (connectionState() == VpnProtocol::Connected && IpcClient::Interface()) { - if (m_settings.routeMode() == Settings::VpnOnlyForwardSites) { + if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { IpcClient::Interface()->routeDeleteList(vpnProtocol()->vpnGateway(), ips); } - else if (m_settings.routeMode() == Settings::VpnAllExceptSites) { + else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { IpcClient::Interface()->routeDeleteList(m_vpnProtocol->routeGateway(), ips); } } @@ -215,18 +218,15 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, QString configData; if (lastVpnConfig.contains(proto)) { configData = lastVpnConfig.value(proto); - configData = VpnConfigurator::processConfigWithLocalSettings(serverIndex, container, proto, configData); - - qDebug() << "VpnConnection::createVpnConfiguration: using saved config for" << ProtocolProps::protoToString(proto); + configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData); } else { - qDebug() << "VpnConnection::createVpnConfiguration: gen new config for" << ProtocolProps::protoToString(proto); - configData = VpnConfigurator::genVpnProtocolConfig(credentials, + configData = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, proto, &e); QString configDataBeforeLocalProcessing = configData; - configData = VpnConfigurator::processConfigWithLocalSettings(serverIndex, container, proto, configData); + configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData); if (errorCode && e) { @@ -237,9 +237,9 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, if (serverIndex >= 0) { qDebug() << "VpnConnection::createVpnConfiguration: saving config for server #" << serverIndex << container << proto; - QJsonObject protoObject = m_settings.protocolConfig(serverIndex, container, proto); + QJsonObject protoObject = m_settings->protocolConfig(serverIndex, container, proto); protoObject.insert(config_key::last_config, configDataBeforeLocalProcessing); - m_settings.setProtocolConfig(serverIndex, container, proto, protoObject); + m_settings->setProtocolConfig(serverIndex, container, proto, protoObject); } } @@ -272,7 +272,7 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, Proto proto = ContainerProps::defaultProtocol(container); vpnConfiguration[config_key::vpnproto] = ProtocolProps::protoToString(proto); - auto dns = VpnConfigurator::getDnsForConfig(serverIndex); + auto dns = m_configurator->getDnsForConfig(serverIndex); vpnConfiguration[config_key::dns1] = dns.first; vpnConfiguration[config_key::dns2] = dns.second; @@ -284,7 +284,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig) { qDebug() << QString("СonnectToVpn, Server index is %1, container is %2, route mode is") - .arg(serverIndex).arg(ContainerProps::containerToString(container)) << m_settings.routeMode(); + .arg(serverIndex).arg(ContainerProps::containerToString(container)) << m_settings->routeMode(); #if !defined (Q_OS_ANDROID) && !defined (Q_OS_IOS) if (!m_IpcClient) { @@ -346,7 +346,7 @@ void VpnConnection::connectToVpn(int serverIndex, connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState))); connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); - ServerController::disconnectFromHost(credentials); + m_serverController->disconnectFromHost(credentials); e = m_vpnProtocol.data()->start(); if (e) emit VpnProtocol::Error; @@ -360,8 +360,6 @@ QString VpnConnection::bytesPerSecToText(quint64 bytes) void VpnConnection::disconnectFromVpn() { - // qDebug() << "Disconnect from VPN 1"; - #ifdef AMNEZIA_DESKTOP if (IpcClient::Interface()) { IpcClient::Interface()->flushDns(); @@ -382,7 +380,6 @@ void VpnConnection::disconnectFromVpn() return; } m_vpnProtocol.data()->stop(); - // qDebug() << "Disconnect from VPN 2"; } VpnProtocol::VpnConnectionState VpnConnection::connectionState() diff --git a/client/vpnconnection.h b/client/vpnconnection.h index d0dc2135..18446e74 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -14,6 +14,9 @@ #include "core/ipcclient.h" #endif +class VpnConfigurator; +class ServerController; + using namespace amnezia; class VpnConnection : public QObject @@ -21,7 +24,9 @@ class VpnConnection : public QObject Q_OBJECT public: - explicit VpnConnection(QObject* parent = nullptr); + explicit VpnConnection(std::shared_ptr settings, + std::shared_ptr configurator, + std::shared_ptr serverController, QObject* parent = nullptr); ~VpnConnection() override; static QString bytesPerSecToText(quint64 bytes); @@ -73,7 +78,10 @@ protected: QSharedPointer m_vpnProtocol; private: - Settings m_settings; + std::shared_ptr m_settings; + std::shared_ptr m_configurator; + std::shared_ptr m_serverController; + QJsonObject m_vpnConfiguration; QJsonObject m_routeMode; QString m_remoteAddress; diff --git a/deploy/build_android.sh b/deploy/build_android.sh index 9afe49d3..75e8c46c 100644 --- a/deploy/build_android.sh +++ b/deploy/build_android.sh @@ -3,7 +3,6 @@ echo "Build script started ..." set -o errexit -o nounset - # Hold on to current directory PROJECT_DIR=$(pwd) DEPLOY_DIR=$PROJECT_DIR/deploy @@ -30,38 +29,38 @@ QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash # Seacrh Qt if [ -z "${QT_VERSION+x}" ]; then QT_VERSION=5.15.2; -QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin +QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin fi echo "Using Qt in $QT_BIN_DIR" +echo "Using Android SDK in $ANDROID_SDK_ROOT" +echo "Using Android NDK in $ANDROID_NDK_ROOT" # Checking env $QT_BIN_DIR/qmake -v -make -v -gcc -v +$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -v # Build App echo "Building App..." cd $BUILD_DIR $QT_BIN_DIR/qmake -r -spec android-clang CONFIG+=qtquickcompiler ANDROID_ABIS="armeabi-v7a arm64-v8a x86 x86_64" $PROJECT_DIR/AmneziaVPN.pro +echo "Executing make... may take long time" $ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -j2 +echo "Make install..." $ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make install INSTALL_ROOT=android +echo "Build OK" +echo "............Deploy.................." +cd $OUT_APP_DIR - -# Build and run tests here - -#echo "............Deploy.................." - -# TODO possible solution: https://github.com/mhoeher/opentodolist/blob/b8981852e500589851132a02c5a62af9b0ed592c/ci/android-cmake-build.sh -#$QT_BIN_DIR/androiddeployqt \ -# --output $OUT_APP_DIR \ -# --gradle \ -# --release \ -# --deployment bundled - -#cp $OUT_APP_DIR/build/outputs/apk/release/android-build-release-unsigned.apk \ -# OpenTodoList-${ANDROID_ABIS}-${OTL_VERSION}.apk - +$QT_BIN_DIR/androiddeployqt \ + --output $OUT_APP_DIR/android \ + --gradle \ + --release \ + --input android-AmneziaVPN-deployment-settings.json + +echo "............Copy apk.................." +cp $OUT_APP_DIR/android/build/outputs/apk/release/android-release-unsigned.apk \ + $PROJECT_DIR/AmneziaVPN-release-unsigned.apk diff --git a/deploy/build_linux.sh b/deploy/build_linux.sh old mode 100644 new mode 100755 index 996c758a..f72f1a0c --- a/deploy/build_linux.sh +++ b/deploy/build_linux.sh @@ -11,6 +11,15 @@ DEPLOY_DIR=$PROJECT_DIR/deploy mkdir -p $DEPLOY_DIR/build BUILD_DIR=$DEPLOY_DIR/build +APP_DIR=$DEPLOY_DIR/AppDir +mkdir -p $APP_DIR + +TOOLS_DIR=$DEPLOY_DIR/Tools +mkdir -p $TOOLS_DIR + +CQTDEPLOYER_DIR=$TOOLS_DIR/cqtdeployer +mkdir -p $CQTDEPLOYER_DIR + echo "Project dir: ${PROJECT_DIR}" echo "Build dir: ${BUILD_DIR}" @@ -18,26 +27,23 @@ APP_NAME=AmneziaVPN APP_FILENAME=$APP_NAME.app APP_DOMAIN=org.amneziavpn.package -OUT_APP_DIR=$BUILD_DIR/client -BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME - DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/linux -INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data -INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME +INSTALLER_DATA_DIR=$PROJECT_DIR/deploy/installer/packages/$APP_DOMAIN/data PRO_FILE_PATH=$PROJECT_DIR/$APP_NAME.pro QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash # Seacrh Qt if [ -z "${QT_VERSION+x}" ]; then -QT_VERSION=5.15.2; -QIF_VERSION=4.1 -QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin -QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin + QT_VERSION=5.15.2 + if [ -f /opt/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then + QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin + elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then + QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin + fi fi echo "Using Qt in $QT_BIN_DIR" -echo "Using QIF in $QIF_BIN_DIR" # Checking env @@ -56,4 +62,24 @@ make #echo "............Deploy.................." +cp -r $DEPLOY_DATA_DIR/* $APP_DIR + +if [ ! -f $CQTDEPLOYER_DIR/cqtdeployer.sh ]; then + wget -O $TOOLS_DIR/CQtDeployer.zip https://github.com/QuasarApp/CQtDeployer/releases/download/v1.5.4.17/CQtDeployer_1.5.4.17_Linux_x86_64.zip + unzip -o $TOOLS_DIR/CQtDeployer.zip -d $CQTDEPLOYER_DIR/ + chmod +x -R $CQTDEPLOYER_DIR +fi + + +$CQTDEPLOYER_DIR/cqtdeployer.sh -bin $BUILD_DIR/client/AmneziaVPN -qmake $QT_BIN_DIR/qmake -qmlDir $PROJECT_DIR/client/ui/qml/ -targetDir $APP_DIR/client/ +$CQTDEPLOYER_DIR/cqtdeployer.sh -bin $BUILD_DIR/service/server/AmneziaVPN-service -qmake $QT_BIN_DIR/qmake -targetDir $APP_DIR/service/ + +rm -f $INSTALLER_DATA_DIR/data.7z + +7z a $INSTALLER_DATA_DIR/data.7z $APP_DIR/* + +ldd $CQTDEPLOYER_DIR/bin/binarycreator + +$CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $PROJECT_DIR/deploy/installer/config/linux.xml -p $PROJECT_DIR/deploy/installer/packages/ -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer + diff --git a/deploy/data/linux/AmneziaVPN.service b/deploy/data/linux/AmneziaVPN.service index 5e7753e0..4f829312 100755 --- a/deploy/data/linux/AmneziaVPN.service +++ b/deploy/data/linux/AmneziaVPN.service @@ -8,6 +8,7 @@ Type=simple Restart=always RestartSec=1 ExecStart=/opt/AmneziaVPN/service/AmneziaVPN-service.sh +Environment=LD_LIBRARY_PATH=/opt/AmneziaVPN/client/lib [Install] WantedBy=multi-user.target diff --git a/ipc/ipc.h b/ipc/ipc.h index d78a450b..e867c063 100644 --- a/ipc/ipc.h +++ b/ipc/ipc.h @@ -4,10 +4,32 @@ #include #include +#include "../client/utilities.h" + #define IPC_SERVICE_URL "local:AmneziaVpnIpcInterface" namespace amnezia { +enum PermittedProcess { + OpenVPN, + Wireguard, + CertUtil +}; + +inline QString permittedProcessPath(PermittedProcess pid) +{ + if (pid == PermittedProcess::OpenVPN) { + return Utils::openVpnExecPath(); + } + if (pid == PermittedProcess::Wireguard) { + return Utils::wireguardExecPath(); + } + else if (pid == PermittedProcess::CertUtil) { + return Utils::certUtilPath(); + } +} + + inline QString getIpcServiceUrl() { #ifdef Q_OS_WIN return IPC_SERVICE_URL; diff --git a/ipc/ipc_process_interface.rep b/ipc/ipc_process_interface.rep index fc82c49e..ba42332c 100644 --- a/ipc/ipc_process_interface.rep +++ b/ipc/ipc_process_interface.rep @@ -3,7 +3,7 @@ class IpcProcessInterface { - SLOT( start(const QString &program, const QStringList &args) ); + //SLOT( start(const QString &program, const QStringList &args) ); SLOT( start() ); SLOT( close() ); @@ -11,7 +11,7 @@ class IpcProcessInterface SLOT( setInputChannelMode(QProcess::InputChannelMode mode) ); SLOT( setNativeArguments(const QString &arguments) ); SLOT( setProcessChannelMode(QProcess::ProcessChannelMode mode) ); - SLOT( setProgram(const QString &program) ); + SLOT( setProgram(int programId) ); SLOT( setWorkingDirectory(const QString &dir) ); SLOT( QByteArray readAll() ); diff --git a/ipc/ipcserverprocess.cpp b/ipc/ipcserverprocess.cpp index f9cdd20f..b8f085c3 100644 --- a/ipc/ipcserverprocess.cpp +++ b/ipc/ipcserverprocess.cpp @@ -1,4 +1,5 @@ #include "ipcserverprocess.h" +#include "ipc.h" #include #ifndef Q_OS_IOS @@ -19,18 +20,6 @@ IpcServerProcess::IpcServerProcess(QObject *parent) : qDebug() << "IpcServerProcess errorOccurred " << error; }); -// connect(m_process.data(), &QProcess::readyReadStandardError, this, [this](){ -// qDebug() << "IpcServerProcess StandardError " << m_process->readAllStandardError(); - -// }); -// connect(m_process.data(), &QProcess::readyReadStandardOutput, this, [this](){ -// qDebug() << "IpcServerProcess StandardOutput " << m_process->readAllStandardOutput(); -// }); - -// connect(m_process.data(), &QProcess::readyRead, this, [this](){ -// qDebug() << "IpcServerProcess StandardOutput " << m_process->readAll(); -// }); - } IpcServerProcess::~IpcServerProcess() @@ -38,16 +27,11 @@ IpcServerProcess::~IpcServerProcess() qDebug() << "IpcServerProcess::~IpcServerProcess"; } -void IpcServerProcess::start(const QString &program, const QStringList &arguments) -{ - m_process->start(program, arguments); - qDebug() << "IpcServerProcess started, " << arguments; - - m_process->waitForStarted(); -} - void IpcServerProcess::start() { + if (m_process->program().isEmpty()) { + qDebug() << "IpcServerProcess failed to start, program is empty"; + } m_process->start(); qDebug() << "IpcServerProcess started, " << m_process->program() << m_process->arguments(); @@ -81,9 +65,9 @@ void IpcServerProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode) m_process->setProcessChannelMode(mode); } -void IpcServerProcess::setProgram(const QString &program) +void IpcServerProcess::setProgram(int programId) { - m_process->setProgram(program); + m_process->setProgram(amnezia::permittedProcessPath(static_cast(programId))); } void IpcServerProcess::setWorkingDirectory(const QString &dir) diff --git a/ipc/ipcserverprocess.h b/ipc/ipcserverprocess.h index 0ed61ee8..b427d639 100644 --- a/ipc/ipcserverprocess.h +++ b/ipc/ipcserverprocess.h @@ -13,7 +13,6 @@ public: explicit IpcServerProcess(QObject *parent = nullptr); virtual ~IpcServerProcess(); - void start(const QString &program, const QStringList &arguments) override; void start() override; void close() override; @@ -21,7 +20,7 @@ public: void setInputChannelMode(QProcess::InputChannelMode mode) override; void setNativeArguments(const QString &arguments) override; void setProcessChannelMode(QProcess::ProcessChannelMode mode) override; - void setProgram(const QString &program) override; + void setProgram(int programId) override; void setWorkingDirectory(const QString &dir) override; QByteArray readAll() override;