diff --git a/client/3rd/QRCodeGenerator/QRCodeGenerator.cpp b/client/3rd/QRCodeGenerator/QRCodeGenerator.cpp index 1360099b..6e83fa1e 100644 --- a/client/3rd/QRCodeGenerator/QRCodeGenerator.cpp +++ b/client/3rd/QRCodeGenerator/QRCodeGenerator.cpp @@ -597,7 +597,7 @@ CQR_Encode::~CQR_Encode() ///////////////////////////////////////////////////////////////////////////// // CQR_Encode::EncodeData -bool CQR_Encode::EncodeData(int nLevel, int nVersion, bool bAutoExtent, int nMaskingNo, char* lpsSource, int ncSource) +bool CQR_Encode::EncodeData(int nLevel, int nVersion, bool bAutoExtent, int nMaskingNo, const char* lpsSource, int ncSource) { int i, j; @@ -746,7 +746,7 @@ bool CQR_Encode::EncodeData(int nLevel, int nVersion, bool bAutoExtent, int nMas ///////////////////////////////////////////////////////////////////////////// // CQR_Encode::GetEncodeVersion -int CQR_Encode::GetEncodeVersion(int nVersion, char* lpsSource, int ncLength) +int CQR_Encode::GetEncodeVersion(int nVersion, const char* lpsSource, int ncLength) { int nVerGroup = nVersion >= 27 ? QR_VRESION_L : (nVersion >= 10 ? QR_VRESION_M : QR_VRESION_S); int i, j; @@ -788,7 +788,7 @@ int CQR_Encode::GetEncodeVersion(int nVersion, char* lpsSource, int ncLength) ///////////////////////////////////////////////////////////////////////////// // CQR_Encode::EncodeSourceData -bool CQR_Encode::EncodeSourceData(char* lpsSource, int ncLength, int nVerGroup) +bool CQR_Encode::EncodeSourceData(const char* lpsSource, int ncLength, int nVerGroup) { memset(m_nBlockLength, 0, sizeof(m_nBlockLength)); diff --git a/client/3rd/QRCodeGenerator/QRCodeGenerator.h b/client/3rd/QRCodeGenerator/QRCodeGenerator.h index 01479175..2e8ed923 100644 --- a/client/3rd/QRCodeGenerator/QRCodeGenerator.h +++ b/client/3rd/QRCodeGenerator/QRCodeGenerator.h @@ -92,11 +92,11 @@ private: unsigned char m_byRSWork[MAX_CODEBLOCK]; public: - bool EncodeData(int nLevel, int nVersion, bool bAutoExtent, int nMaskingNo, char* lpsSource, int ncSource = 0); + bool EncodeData(int nLevel, int nVersion, bool bAutoExtent, int nMaskingNo, const char* lpsSource, int ncSource = 0); private: - int GetEncodeVersion(int nVersion, char* lpsSource, int ncLength); - bool EncodeSourceData(char* lpsSource, int ncLength, int nVerGroup); + int GetEncodeVersion(int nVersion, const char* lpsSource, int ncLength); + bool EncodeSourceData(const char* lpsSource, int ncLength, int nVerGroup); int GetBitLength(unsigned char nMode, int ncData, int nVerGroup); diff --git a/client/android/build.gradle b/client/android/build.gradle index 6b1aa0da..92d34e56 100644 --- a/client/android/build.gradle +++ b/client/android/build.gradle @@ -106,6 +106,8 @@ android { resConfig "en" minSdkVersion = 24 targetSdkVersion = 30 + versionCode 2 // Change to a higher number + versionName "2.0.1" // Change to a higher number } buildTypes { diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt index 7e66fd56..b824610b 100644 --- a/client/android/src/org/amnezia/vpn/VPNService.kt +++ b/client/android/src/org/amnezia/vpn/VPNService.kt @@ -287,48 +287,74 @@ class VPNService : android.net.VpnService() { return null } - /** - * Create a Wireguard [Config] from a [json] string - - * The [json] will be created in AndroidVpnProtocol.cpp - */ - private fun buildWireugardConfig(obj: JSONObject): Config { - val confBuilder = Config.Builder() - val jServer = obj.getJSONObject("server") - val peerBuilder = Peer.Builder() - val ep = - InetEndpoint.parse(jServer.getString("ipv4AddrIn") + ":" + jServer.getString("port")) - peerBuilder.setEndpoint(ep) - peerBuilder.setPublicKey(Key.fromBase64(jServer.getString("publicKey"))) - - val jAllowedIPList = obj.getJSONArray("allowedIPs") - if (jAllowedIPList.length() == 0) { - val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet. - peerBuilder.addAllowedIp(internet) - } else { - (0 until jAllowedIPList.length()).toList().forEach { - val network = InetNetwork.parse(jAllowedIPList.getString(it)) - peerBuilder.addAllowedIp(network) + private fun parseConfigData(data: String): Map> { + val parseData = mutableMapOf>() + var currentSection: Pair>? = null + data.lines().forEach { line -> + if (line.isNotEmpty()) { + if (line.startsWith('[')) { + currentSection?.let { + parseData.put(it.first, it.second) + } + currentSection = line.substring(1, line.indexOfLast { it == ']' }) to mutableMapOf() + } else { + val parameter = line.split("=", limit = 2) + currentSection!!.second.put(parameter.first().trim(), parameter.last().trim()) } } - - confBuilder.addPeer(peerBuilder.build()) - - val privateKey = obj.getJSONObject("keys").getString("privateKey") - val jDevice = obj.getJSONObject("device") - - val ifaceBuilder = Interface.Builder() - ifaceBuilder.parsePrivateKey(privateKey) - ifaceBuilder.addAddress(InetNetwork.parse(jDevice.getString("ipv4Address"))) - ifaceBuilder.addAddress(InetNetwork.parse(jDevice.getString("ipv6Address"))) - ifaceBuilder.addDnsServer(InetNetwork.parse(obj.getString("dns")).address) - val jExcludedApplication = obj.getJSONArray("excludedApps") - (0 until jExcludedApplication.length()).toList().forEach { - val appName = jExcludedApplication.get(it).toString() - ifaceBuilder.excludeApplication(appName) - } - confBuilder.setInterface(ifaceBuilder.build()) - return confBuilder.build() } + currentSection?.let { + parseData.put(it.first, it.second) + } + return parseData + } + + /** + * Create a Wireguard [Config] from a [json] string - + * The [json] will be created in AndroidVpnProtocol.cpp + */ + private fun buildWireugardConfig(obj: JSONObject): Config { + val confBuilder = Config.Builder() + val wireguardConfigData = obj.getJSONObject("wireguard_config_data") + val config = parseConfigData(wireguardConfigData.getString("config")) + val peerBuilder = Peer.Builder() + val peerConfig = config["Peer"]!! + peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"])) + peerConfig["PresharedKey"]?.let { + peerBuilder.setPreSharedKey(Key.fromBase64(it)) + } + val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList() + if (allowedIPList.isEmpty()) { + val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet. + peerBuilder.addAllowedIp(internet) + } else { + allowedIPList.forEach { + val network = InetNetwork.parse(it.trim()) + peerBuilder.addAllowedIp(network) + } + } + peerBuilder.setEndpoint(InetEndpoint.parse(peerConfig["Endpoint"])) + peerConfig["PersistentKeepalive"]?.let { + peerBuilder.setPersistentKeepalive(it.toInt()) + } + confBuilder.addPeer(peerBuilder.build()) + + val ifaceBuilder = Interface.Builder() + val ifaceConfig = config["Interface"]!! + ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"]) + ifaceBuilder.addAddress(InetNetwork.parse(ifaceConfig["Address"])) + ifaceConfig["DNS"]!!.split(",").forEach { + ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address) + } + /*val jExcludedApplication = obj.getJSONArray("excludedApps") + (0 until jExcludedApplication.length()).toList().forEach { + val appName = jExcludedApplication.get(it).toString() + ifaceBuilder.excludeApplication(appName) + }*/ + confBuilder.setInterface(ifaceBuilder.build()) + + return confBuilder.build() + } fun getVpnConfig(): JSONObject { return mConfig!! diff --git a/client/configurators/ikev2_configurator.cpp b/client/configurators/ikev2_configurator.cpp index e3788f90..de9f0e82 100644 --- a/client/configurators/ikev2_configurator.cpp +++ b/client/configurators/ikev2_configurator.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "sftpdefs.h" @@ -21,6 +22,7 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se connData.host = credentials.hostName; connData.clientId = Utils::getRandomString(16); connData.password = Utils::getRandomString(16); + connData.password = ""; QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12"; @@ -41,8 +43,11 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se .arg(certFileName); e = ServerController::runContainerScript(credentials, container, scriptExportCert); - connData.cert = ServerController::getTextFileFromContainer(container, credentials, certFileName, &e); - qDebug() << "Ikev2Configurator::ConnectionData cert size:" << connData.cert.size(); + connData.clientCert = ServerController::getTextFileFromContainer(container, credentials, certFileName, &e); + connData.caCert = 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(); return connData; } @@ -50,17 +55,62 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) { + Q_UNUSED(containerConfig) + ConnectionData connData = prepareIkev2Config(credentials, container, errorCode); if (errorCode && *errorCode) { return ""; } + return genIkev2Config(connData); +} + +QString Ikev2Configurator::genIkev2Config(const ConnectionData &connData) +{ QJsonObject config; config[config_key::hostName] = connData.host; config[config_key::userName] = connData.clientId; - config[config_key::cert] = QString(connData.cert.toBase64()); + config[config_key::cert] = QString(connData.clientCert.toBase64()); config[config_key::password] = connData.password; return QJsonDocument(config).toJson(); } +QString Ikev2Configurator::genMobileConfig(const ConnectionData &connData) +{ + QFile file(":/server_scripts/ipsec/mobileconfig.plist"); + file.open(QIODevice::ReadOnly); + QString config = QString(file.readAll()); + + config.replace("$CLIENT_NAME", connData.clientId); + config.replace("$UUID1", QUuid::createUuid().toString()); + config.replace("$SERVER_ADDR", connData.host); + + QString subStr("$(UUID_GEN)"); + while (config.indexOf(subStr) > 0) { + config.replace(config.indexOf(subStr), subStr.size(), QUuid::createUuid().toString()); + } + + config.replace("$P12_BASE64", connData.clientCert.toBase64()); + config.replace("$CA_BASE64", connData.caCert.toBase64()); + + return config; +} + +QString Ikev2Configurator::genStrongSwanConfig(const ConnectionData &connData) +{ + QFile file(":/server_scripts/ipsec/strongswan.profile"); + file.open(QIODevice::ReadOnly); + QString config = QString(file.readAll()); + + config.replace("$CLIENT_NAME", connData.clientId); + config.replace("$UUID", QUuid::createUuid().toString()); + config.replace("$SERVER_ADDR", connData.host); + + QByteArray cert = connData.clientCert.toBase64(); + cert.replace("\r", "").replace("\n", ""); + config.replace("$P12_BASE64", cert); + + return config; +} + diff --git a/client/configurators/ikev2_configurator.h b/client/configurators/ikev2_configurator.h index 5257fc7f..9061a47c 100644 --- a/client/configurators/ikev2_configurator.h +++ b/client/configurators/ikev2_configurator.h @@ -12,7 +12,8 @@ class Ikev2Configurator public: struct ConnectionData { - QByteArray cert; // p12 client cert + QByteArray clientCert; // p12 client cert + QByteArray caCert; // p12 server cert QString clientId; QString password; // certificate password QString host; // host ip @@ -21,8 +22,10 @@ public: static 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); -private: static ConnectionData prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container, ErrorCode *errorCode = nullptr); }; diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 17101f13..2d0a4fd3 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -6,6 +6,11 @@ #include #include +#include +#include +#include +#include + #include "sftpdefs.h" #include "core/server_defs.h" @@ -13,72 +18,34 @@ #include "core/scripts_registry.h" #include "utils.h" -QProcessEnvironment WireguardConfigurator::prepareEnv() -{ - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QString pathEnvVar = env.value("PATH"); - -#ifdef Q_OS_WIN - pathEnvVar.clear(); - pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\wireguard;"); -#else - pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS"); -#endif - - env.insert("PATH", pathEnvVar); - qDebug().noquote() << "ENV PATH" << pathEnvVar; - return env; -} - WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys() { + // TODO review + constexpr size_t EDDSA_KEY_LENGTH = 32; + ConnectionData connData; - QString program; -#ifdef Q_OS_WIN - program = QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\wireguard\\wg.exe"; -#else - program = QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS/wg"; -#endif + unsigned char buff[EDDSA_KEY_LENGTH]; + int ret = RAND_priv_bytes(buff, EDDSA_KEY_LENGTH); + if (ret <=0) return connData; -#ifndef Q_OS_IOS + EVP_PKEY * pKey = EVP_PKEY_new(); + q_check_ptr(pKey); + pKey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, &buff[0], EDDSA_KEY_LENGTH); - // Priv - { - QProcess p; - p.setProcessEnvironment(prepareEnv()); - p.setProcessChannelMode(QProcess::MergedChannels); - p.setProgram(program); - p.setArguments(QStringList() << "genkey"); + size_t keySize = EDDSA_KEY_LENGTH; - p.start(); - p.waitForFinished(); + // save private key + unsigned char priv[EDDSA_KEY_LENGTH]; + EVP_PKEY_get_raw_private_key(pKey, priv, &keySize); + connData.clientPrivKey = QByteArray::fromRawData((char*)priv, keySize).toBase64(); - connData.clientPrivKey = QString(p.readAll()); - connData.clientPrivKey.replace("\r", ""); - connData.clientPrivKey.replace("\n", ""); - } + // save public key + unsigned char pub[EDDSA_KEY_LENGTH]; + EVP_PKEY_get_raw_public_key(pKey, pub, &keySize); + connData.clientPubKey = QByteArray::fromRawData((char*)pub, keySize).toBase64(); - // Pub - { - QProcess p; - p.setProcessEnvironment(prepareEnv()); - p.setProcessChannelMode(QProcess::MergedChannels); - p.setProgram(program); - - p.setArguments(QStringList() << "pubkey"); - - p.start(); - p.write(connData.clientPrivKey.toUtf8()); - p.closeWriteChannel(); - p.waitForFinished(); - - connData.clientPubKey = QString(p.readAll()); - connData.clientPubKey.replace("\r", ""); - connData.clientPubKey.replace("\n", ""); - } -#endif return connData; } diff --git a/client/configurators/wireguard_configurator.h b/client/configurators/wireguard_configurator.h index 777dfaaa..2bd715ad 100644 --- a/client/configurators/wireguard_configurator.h +++ b/client/configurators/wireguard_configurator.h @@ -28,8 +28,6 @@ public: private: - static QProcessEnvironment prepareEnv(); - static ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container, ErrorCode *errorCode = nullptr); diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index b56145bf..d1efda53 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -43,11 +43,14 @@ QVector ContainerProps::protocolsForContainer(amnezia::Docker return { Protocol::OpenVpn, Protocol::ShadowSocks, Protocol::Cloak }; case DockerContainer::Ipsec: - return { Protocol::Ikev2, Protocol::L2tp }; + return { Protocol::Ikev2 /*, Protocol::L2tp */}; case DockerContainer::Dns: return { }; + case DockerContainer::Sftp: + return { Protocol::Sftp}; + default: return { defaultProtocol(container) }; } @@ -72,11 +75,11 @@ QMap ContainerProps::containerHumanNames() {DockerContainer::ShadowSocks, "OpenVpn over ShadowSocks"}, {DockerContainer::Cloak, "OpenVpn over Cloak"}, {DockerContainer::WireGuard, "WireGuard"}, - {DockerContainer::Ipsec, QObject::tr("IPsec container")}, + {DockerContainer::Ipsec, QObject::tr("IPsec")}, {DockerContainer::TorWebSite, QObject::tr("Web site in TOR network")}, {DockerContainer::Dns, QObject::tr("DNS Service")}, - {DockerContainer::FileShare, QObject::tr("SMB file sharing service")}, + //{DockerContainer::FileShare, QObject::tr("SMB file sharing service")}, {DockerContainer::Sftp, QObject::tr("Sftp file sharing service")} }; } @@ -93,7 +96,7 @@ QMap ContainerProps::containerDescriptions() {DockerContainer::TorWebSite, QObject::tr("Web site in TOR network")}, {DockerContainer::Dns, QObject::tr("DNS Service")}, - {DockerContainer::FileShare, QObject::tr("SMB file sharing service - is Window file sharing protocol")}, + //{DockerContainer::FileShare, QObject::tr("SMB file sharing service - is Window file sharing protocol")}, {DockerContainer::Sftp, QObject::tr("Sftp file sharing service - is secure FTP service")} }; } @@ -109,7 +112,7 @@ amnezia::ServiceType ContainerProps::containerService(DockerContainer c) case DockerContainer::Ipsec : return ServiceType::Vpn; case DockerContainer::TorWebSite : return ServiceType::Other; case DockerContainer::Dns : return ServiceType::Other; - case DockerContainer::FileShare : return ServiceType::Other; + //case DockerContainer::FileShare : return ServiceType::Other; case DockerContainer::Sftp : return ServiceType::Other; default: return ServiceType::Other; } @@ -127,8 +130,9 @@ Protocol ContainerProps::defaultProtocol(DockerContainer c) case DockerContainer::TorWebSite : return Protocol::TorWebSite; case DockerContainer::Dns : return Protocol::Dns; - case DockerContainer::FileShare : return Protocol::FileShare; + //case DockerContainer::FileShare : return Protocol::FileShare; case DockerContainer::Sftp : return Protocol::Sftp; + default: return Protocol::Any; } } diff --git a/client/containers/containers_defs.h b/client/containers/containers_defs.h index b27b61be..25f2dfc7 100644 --- a/client/containers/containers_defs.h +++ b/client/containers/containers_defs.h @@ -23,7 +23,7 @@ enum DockerContainer { //non-vpn TorWebSite, Dns, - FileShare, + //FileShare, Sftp }; Q_ENUM_NS(DockerContainer) diff --git a/client/core/ipcclient.cpp b/client/core/ipcclient.cpp index 03a3152c..475810a8 100644 --- a/client/core/ipcclient.cpp +++ b/client/core/ipcclient.cpp @@ -1,38 +1,83 @@ #include "ipcclient.h" #include -IpcClient &IpcClient::Instance() +IpcClient *IpcClient::m_instance = nullptr; + +IpcClient::IpcClient(QObject *parent) : QObject(parent) { - static IpcClient s; - return s; + } -bool IpcClient::init() +IpcClient::~IpcClient() { - Instance().m_localSocket->waitForConnected(); + if (m_localSocket) m_localSocket->close(); +} - if (!Instance().m_ipcClient) { +bool IpcClient::isSocketConnected() const +{ + return m_isSocketConnected; +} + +IpcClient *IpcClient::Instance() +{ + return m_instance; +} + +QSharedPointer IpcClient::Interface() +{ + if (!Instance()) return nullptr; + return Instance()->m_ipcClient; +} + +bool IpcClient::init(IpcClient *instance) +{ + m_instance = instance; + + Instance()->m_localSocket = new QLocalSocket(Instance()); + connect(Instance()->m_localSocket.data(), &QLocalSocket::connected, &Instance()->m_ClientNode, []() { + Instance()->m_ClientNode.addClientSideConnection(Instance()->m_localSocket.data()); + + Instance()->m_ipcClient.reset(Instance()->m_ClientNode.acquire()); + Instance()->m_ipcClient->waitForSource(1000); + + if (!Instance()->m_ipcClient->isReplicaValid()) { + qWarning() << "IpcClient replica is not connected!"; + } + + }); + + connect(Instance()->m_localSocket, &QLocalSocket::disconnected, [instance](){ + instance->m_isSocketConnected = false; + }); + + Instance()->m_localSocket->connectToServer(amnezia::getIpcServiceUrl()); + + Instance()->m_localSocket->waitForConnected(); + + if (!Instance()->m_ipcClient) { qDebug() << "IpcClient::init failed"; return false; } - return Instance().m_ipcClient->isReplicaValid(); + qDebug() << "IpcClient::init succeed"; + + return Instance()->m_ipcClient->isReplicaValid(); } QSharedPointer IpcClient::CreatePrivilegedProcess() { #ifndef Q_OS_IOS - if (! Instance().m_ipcClient || ! Instance().m_ipcClient->isReplicaValid()) { + if (! Instance()->m_ipcClient || ! Instance()->m_ipcClient->isReplicaValid()) { qWarning() << "IpcClient::createPrivilegedProcess : IpcClient IpcClient replica is not valid"; return nullptr; } - QRemoteObjectPendingReply futureResult = Instance().m_ipcClient->createPrivilegedProcess(); + QRemoteObjectPendingReply futureResult = Instance()->m_ipcClient->createPrivilegedProcess(); futureResult.waitForFinished(1000); int pid = futureResult.returnValue(); auto pd = QSharedPointer(new ProcessDescriptor()); - Instance().m_processNodes.insert(pid, pd); + Instance()->m_processNodes.insert(pid, pd); pd->localSocket.reset(new QLocalSocket(pd->replicaNode.data())); @@ -65,19 +110,4 @@ QSharedPointer IpcClient::CreatePrivilegedProcess() #endif } -IpcClient::IpcClient(QObject *parent) : QObject(parent) -{ - m_localSocket.reset(new QLocalSocket(this)); - connect(m_localSocket.data(), &QLocalSocket::connected, &m_ClientNode, [this]() { - m_ClientNode.addClientSideConnection(m_localSocket.data()); - m_ipcClient.reset(m_ClientNode.acquire()); - m_ipcClient->waitForSource(1000); - - if (!m_ipcClient->isReplicaValid()) { - qWarning() << "IpcClient replica is not connected!"; - } - - }); - m_localSocket->connectToServer(amnezia::getIpcServiceUrl()); -} diff --git a/client/core/ipcclient.h b/client/core/ipcclient.h index 6eef02b1..1548a6cb 100644 --- a/client/core/ipcclient.h +++ b/client/core/ipcclient.h @@ -19,19 +19,23 @@ class IpcClient : public QObject { Q_OBJECT public: - static IpcClient &Instance(); - static bool init(); - static QSharedPointer Interface() { return Instance().m_ipcClient; } + explicit IpcClient(QObject *parent = nullptr); + + static IpcClient *Instance(); + static bool init(IpcClient *instance); + static QSharedPointer Interface(); static QSharedPointer CreatePrivilegedProcess(); + bool isSocketConnected() const; + signals: private: - explicit IpcClient(QObject *parent = nullptr); + ~IpcClient() override; QRemoteObjectNode m_ClientNode; QSharedPointer m_ipcClient; - QSharedPointer m_localSocket; + QPointer m_localSocket; struct ProcessDescriptor { ProcessDescriptor () { @@ -45,6 +49,9 @@ private: }; QMap> m_processNodes; + bool m_isSocketConnected {false}; + + static IpcClient *m_instance; }; #endif // IPCCLIENT_H diff --git a/client/core/scripts_registry.cpp b/client/core/scripts_registry.cpp index 7644f012..77c056bd 100644 --- a/client/core/scripts_registry.cpp +++ b/client/core/scripts_registry.cpp @@ -15,7 +15,7 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container) case DockerContainer::TorWebSite: return QLatin1String("website_tor"); case DockerContainer::Dns: return QLatin1String("dns"); - case DockerContainer::FileShare: return QLatin1String("file_share"); + //case DockerContainer::FileShare: return QLatin1String("file_share"); case DockerContainer::Sftp: return QLatin1String("sftp"); default: return ""; } diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 802bbb69..9375cd1d 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -24,6 +24,12 @@ using namespace QSsh; +Settings &ServerController::m_settings() +{ + static Settings s; + return s; +} + ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script, const std::function)> &cbReadStdOut, const std::function)> &cbReadStdErr) @@ -127,16 +133,22 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti const std::function)> &cbReadStdErr) { QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh"; - ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName); + + QString mkdir = "sudo docker exec -i $CONTAINER_NAME mkdir -p /opt/amnezia/"; + ErrorCode e = runScript(credentials, + replaceVars(mkdir, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr); + if (e) return e; + + e = uploadTextFileToContainer(container, credentials, script, fileName); if (e) return e; QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName); e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr); -// QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName); -// runScript(credentials, -// replaceVars(remover, genVarsForScript(credentials, container))); + QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName); + runScript(credentials, + replaceVars(remover, genVarsForScript(credentials, container))); return e; } @@ -672,6 +684,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()}}); // Sftp vars diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index 30e86ff4..f29b6fd5 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -6,6 +6,8 @@ #include "sshconnection.h" #include "sshremoteprocess.h" #include "defs.h" +#include "settings.h" + #include "containers/containers_defs.h" #include "sftpdefs.h" @@ -75,6 +77,7 @@ private: static ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); static ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); + static Settings &m_settings(); }; #endif // SERVERCONTROLLER_H diff --git a/client/images/animation.gif b/client/images/animation.gif new file mode 100644 index 00000000..6f7f38e8 Binary files /dev/null and b/client/images/animation.gif differ diff --git a/client/images/connect_button_connected.png b/client/images/connect_button_connected.png deleted file mode 100644 index 171eb79e..00000000 Binary files a/client/images/connect_button_connected.png and /dev/null differ diff --git a/client/images/connect_button_disconnected.png b/client/images/connect_button_disconnected.png deleted file mode 100644 index a88e26e4..00000000 Binary files a/client/images/connect_button_disconnected.png and /dev/null differ diff --git a/client/images/connected.png b/client/images/connected.png new file mode 100644 index 00000000..b3c907c8 Binary files /dev/null and b/client/images/connected.png differ diff --git a/client/images/disconnected.png b/client/images/disconnected.png new file mode 100644 index 00000000..199f71dc Binary files /dev/null and b/client/images/disconnected.png differ diff --git a/client/main.cpp b/client/main.cpp index 74bf6e8d..19c505d8 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -116,6 +116,9 @@ int main(int argc, char *argv[]) app.setQuitOnLastWindowClosed(false); + qRegisterMetaType("VpnProtocol::ConnectionState"); + qRegisterMetaType("ServerCredentials"); + qRegisterMetaType("DockerContainer"); qRegisterMetaType("TransportProto"); qRegisterMetaType("Protocol"); @@ -127,12 +130,14 @@ int main(int argc, char *argv[]) UiLogic *uiLogic = new UiLogic; - QQmlApplicationEngine engine; + QQmlApplicationEngine *engine = new QQmlApplicationEngine; declareQmlPageEnum(); declareQmlProtocolEnum(); declareQmlContainerEnum(); + qmlRegisterType("PageType", 1, 0, "PageType"); + QScopedPointer containerProps(new ContainerProps); qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", containerProps.get()); @@ -140,29 +145,41 @@ int main(int argc, char *argv[]) qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", protocolProps.get()); const QUrl url(QStringLiteral("qrc:/ui/qml/main.qml")); - QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + QObject::connect(engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); - engine.rootContext()->setContextProperty("UiLogic", uiLogic); + 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("ServerConfiguringProgressLogic", uiLogic->serverConfiguringProgressLogic()); - engine.rootContext()->setContextProperty("NewServerProtocolsLogic", uiLogic->newServerProtocolsLogic()); - engine.rootContext()->setContextProperty("ServerListLogic", uiLogic->serverListLogic()); - engine.rootContext()->setContextProperty("ServerSettingsLogic", uiLogic->serverSettingsLogic()); - engine.rootContext()->setContextProperty("ServerContainersLogic", uiLogic->serverVpnProtocolsLogic()); - 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()); + engine->rootContext()->setContextProperty("AppSettingsLogic", uiLogic->appSettingsLogic()); + engine->rootContext()->setContextProperty("GeneralSettingsLogic", uiLogic->generalSettingsLogic()); + engine->rootContext()->setContextProperty("NetworkSettingsLogic", uiLogic->networkSettingsLogic()); + engine->rootContext()->setContextProperty("ServerConfiguringProgressLogic", uiLogic->serverConfiguringProgressLogic()); + engine->rootContext()->setContextProperty("NewServerProtocolsLogic", uiLogic->newServerProtocolsLogic()); + engine->rootContext()->setContextProperty("ServerListLogic", uiLogic->serverListLogic()); + engine->rootContext()->setContextProperty("ServerSettingsLogic", uiLogic->serverSettingsLogic()); + engine->rootContext()->setContextProperty("ServerContainersLogic", uiLogic->serverVpnProtocolsLogic()); + 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()); - engine.load(url); + 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)); + } // TODO - fix //#ifdef Q_OS_WIN diff --git a/client/protocols/ikev2_vpn_protocol.cpp b/client/protocols/ikev2_vpn_protocol.cpp index dc60be39..ccb3c7ad 100644 --- a/client/protocols/ikev2_vpn_protocol.cpp +++ b/client/protocols/ikev2_vpn_protocol.cpp @@ -1,18 +1,29 @@ #include #include #include -#include -#include +//#include +//#include #include +#include + #include "debug.h" #include "ikev2_vpn_protocol.h" #include "utils.h" +static Ikev2Protocol* self = nullptr; +static std::mutex rasDialFuncMutex; + +extern "C" { +static void WINAPI RasDialFuncCallback(UINT unMsg, + RASCONNSTATE rasconnstate, + DWORD dwError ); +} Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent) : VpnProtocol(configuration, parent) { + self = this; //m_configFile.setFileTemplate(QDir::tempPath() + QDir::separator() + serviceName() + ".conf"); readIkev2Configuration(configuration); } @@ -20,34 +31,214 @@ Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent) Ikev2Protocol::~Ikev2Protocol() { qDebug() << "IpsecProtocol::~IpsecProtocol()"; +#ifdef Q_OS_WIN + disconnect_vpn(); +#endif Ikev2Protocol::stop(); - QThread::msleep(200); } void Ikev2Protocol::stop() { + setConnectionState(VpnProtocol::Disconnecting); #ifdef Q_OS_WINDOWS { - setConnectionState(Disconnecting); - - auto disconnectProcess = new QProcess; - - disconnectProcess->setProgram("rasdial"); - QString arguments = QString("\"%1\" /disconnect") - .arg(tunnelName()); - disconnectProcess->setNativeArguments(arguments); - -// connect(connectProcess, &QProcess::readyRead, [connectProcess]() { -// qDebug().noquote() << "connectProcess readyRead" << connectProcess->readAll(); -// }); - - disconnectProcess->start(); - disconnectProcess->waitForFinished(5000); - setConnectionState(Disconnected); + if (! disconnect_vpn() ){ + qDebug()<<"We don't disconnect"; + setConnectionState(VpnProtocol::Error); + } + else { + setConnectionState(VpnProtocol::Disconnected); + } } #endif } +void Ikev2Protocol::newConnectionStateEventReceived(UINT unMsg, tagRASCONNSTATE rasconnstate, DWORD dwError) +{ + Q_UNUSED(unMsg); + qDebug()<<"Recive the new event "<(rasconnstate); + switch (rasconnstate) + { + case RASCS_OpenPort: + qDebug()<<__FUNCTION__ << __LINE__; + setConnectionState(Preparing); + //printf ("RASCS_OpenPort = %d\n", _connection_state); + //printf ("Opening port...\n"); + break; + case RASCS_PortOpened: + qDebug()<<__FUNCTION__ << __LINE__; + setConnectionState(Preparing); + //printf ("RASCS_PortOpened = %d\n", _connection_state); + //printf ("Port opened.\n"); + break; + case RASCS_ConnectDevice: + qDebug()<<__FUNCTION__ << __LINE__; + setConnectionState(Preparing); + //printf ("RASCS_ConnectDevice = %d\n", _connection_state); + //printf ("Connecting device...\n"); + break; + case RASCS_DeviceConnected: + qDebug()<<__FUNCTION__ << __LINE__; + setConnectionState(Preparing); + //printf ("RASCS_DeviceConnected = %d\n", _connection_state); + //printf ("Device connected.\n"); + break; + case RASCS_AllDevicesConnected: + qDebug()<<__FUNCTION__ << __LINE__; + setConnectionState(Preparing); + //printf ("RASCS_AllDevicesConnected = %d\n", _connection_state); + //printf ("All devices connected.\n"); + break; + case RASCS_Authenticate: + qDebug()<<__FUNCTION__ << __LINE__; + setConnectionState(Preparing); + //printf ("RASCS_Authenticate = %d\n", _connection_state); + // printf ("Authenticating...\n"); + break; + case RASCS_AuthNotify: + qDebug()<<__FUNCTION__ << __LINE__; + if (dwError != 0) { + qDebug() << "have error" << dwError; + setConnectionState(Disconnected); + } else { + qDebug() << "RASCS_AuthNotify but no error" << dwError; + } + //printf ("RASCS_AuthNotify = %d\n", _connection_state); + // printf ("Authentication notify.\n"); + break; + case RASCS_AuthRetry: + qDebug()<<__FUNCTION__ << __LINE__; + setConnectionState(Preparing); + //printf ("RASCS_AuthRetry = %d\n", _connection_state); + //printf ("Retrying authentication...\n"); + break; + case RASCS_AuthCallback: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_AuthCallback = %d\n", _connection_state); + //printf ("Authentication callback...\n"); + break; + case RASCS_AuthChangePassword: + qDebug()<<__FUNCTION__ << __LINE__; + // printf ("RASCS_AuthChangePassword = %d\n", _connection_state); + //printf ("Change password...\n"); + break; + case RASCS_AuthProject: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_AuthProject = %d\n", _connection_state); + //printf ("Projection phase started...\n"); + break; + case RASCS_AuthLinkSpeed: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_AuthLinkSpeed = %d\n", _connection_state); + //printf ("Negoting speed...\n"); + break; + case RASCS_AuthAck: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_AuthAck = %d\n", _connection_state); + //printf ("Authentication acknowledge...\n"); + break; + case RASCS_ReAuthenticate: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_ReAuthenticate = %d\n", _connection_state); + //printf ("Retrying Authentication...\n"); + break; + case RASCS_Authenticated: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_Authenticated = %d\n", _connection_state); + //printf ("Authentication complete.\n"); + break; + case RASCS_PrepareForCallback: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_PrepareForCallback = %d\n", _connection_state); + //printf ("Preparing for callback...\n"); + break; + case RASCS_WaitForModemReset: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_WaitForModemReset = %d\n", _connection_state); + // printf ("Waiting for modem reset...\n"); + break; + case RASCS_WaitForCallback: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_WaitForCallback = %d\n", _connection_state); + //printf ("Waiting for callback...\n"); + break; + case RASCS_Projected: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_Projected = %d\n", _connection_state); + //printf ("Projection completed.\n"); + break; +#if (WINVER >= 0x400) + case RASCS_StartAuthentication: // Windows 95 only + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_StartAuthentication = %d\n", _connection_state); + //printf ("Starting authentication...\n"); + + break; + case RASCS_CallbackComplete: // Windows 95 only + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_CallbackComplete = %d\n", rasconnstate); + //printf ("Callback complete.\n"); + break; + case RASCS_LogonNetwork: // Windows 95 only + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_LogonNetwork = %d\n", _connection_state); + //printf ("Login to the network.\n"); + break; +#endif + case RASCS_SubEntryConnected: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_SubEntryConnected = %d\n", _connection_state); + //printf ("Subentry connected.\n"); + break; + case RASCS_SubEntryDisconnected: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_SubEntryDisconnected = %d\n", _connection_state); + //printf ("Subentry disconnected.\n"); + break; + //PAUSED STATES: + case RASCS_Interactive: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_Interactive = %d\n", _connection_state); + //printf ("In Paused state: Interactive mode.\n"); + break; + case RASCS_RetryAuthentication: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_RetryAuthentication = %d\n", _connection_state); + //printf ("In Paused state: Retry Authentication...\n"); + break; + case RASCS_CallbackSetByCaller: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_CallbackSetByCaller = %d\n", _connection_state); + //printf ("In Paused state: Callback set by Caller.\n"); + break; + case RASCS_PasswordExpired: + setConnectionState(Error); + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_PasswordExpired = %d\n", _connection_state); + //printf ("In Paused state: Password has expired...\n"); + break; + + case RASCS_Connected: // = RASCS_DONE: + setConnectionState(Connected); + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_Connected = %d\n", _connection_state); + //printf ("Connection completed.\n"); + //SetEvent(gEvent_handle); + break; + case RASCS_Disconnected: + setConnectionState(Disconnected); + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("RASCS_Disconnected = %d\n", _connection_state); + //printf ("Disconnecting...\n"); + break; + default: + qDebug()<<__FUNCTION__ << __LINE__; + //printf ("Unknown Status = %d\n", _connection_state); + //printf ("What are you going to do about it?\n"); + break; + } +} + void Ikev2Protocol::readIkev2Configuration(const QJsonObject &configuration) { m_config = configuration.value(ProtocolProps::key_proto_config_data(Protocol::Ikev2)).toObject(); @@ -57,7 +248,7 @@ ErrorCode Ikev2Protocol::start() { #ifdef Q_OS_WINDOWS QByteArray cert = QByteArray::fromBase64(m_config[config_key::cert].toString().toUtf8()); - setConnectionState(ConnectionState::Connecting); + setConnectionState(Connecting); QTemporaryFile certFile; certFile.setAutoRemove(false); @@ -65,7 +256,6 @@ ErrorCode Ikev2Protocol::start() certFile.write(cert); certFile.close(); - { auto certInstallProcess = IpcClient::CreatePrivilegedProcess(); @@ -87,64 +277,71 @@ ErrorCode Ikev2Protocol::start() }); certInstallProcess->setArguments(arguments); -// qDebug() << arguments.join(" "); -// connect(certInstallProcess.data(), &IpcProcessInterfaceReplica::errorOccurred, [certInstallProcess](QProcess::ProcessError error) { -// qDebug() << "IpcProcessInterfaceReplica errorOccurred" << error; -// }); + // qDebug() << arguments.join(" "); + // connect(certInstallProcess.data(), &IpcProcessInterfaceReplica::errorOccurred, [certInstallProcess](QProcess::ProcessError error) { + // qDebug() << "IpcProcessInterfaceReplica errorOccurred" << error; + // }); -// connect(certInstallProcess.data(), &IpcProcessInterfaceReplica::stateChanged, [certInstallProcess](QProcess::ProcessState newState) { -// qDebug() << "IpcProcessInterfaceReplica stateChanged" << newState; -// }); + // connect(certInstallProcess.data(), &IpcProcessInterfaceReplica::stateChanged, [certInstallProcess](QProcess::ProcessState newState) { + // qDebug() << "IpcProcessInterfaceReplica stateChanged" << newState; + // }); -// connect(certInstallProcess.data(), &IpcProcessInterfaceReplica::readyRead, [certInstallProcess]() { -// auto req = certInstallProcess->readAll(); -// req.waitForFinished(); -// qDebug() << "IpcProcessInterfaceReplica readyRead" << req.returnValue(); -// }); + // connect(certInstallProcess.data(), &IpcProcessInterfaceReplica::readyRead, [certInstallProcess]() { + // auto req = certInstallProcess->readAll(); + // req.waitForFinished(); + // qDebug() << "IpcProcessInterfaceReplica readyRead" << req.returnValue(); + // }); certInstallProcess->start(); } - + // /* { - auto adapterRemoveProcess = new QProcess; + // auto adapterRemoveProcess = new QProcess; - adapterRemoveProcess->setProgram("powershell"); - QString arguments = QString("-command \"Remove-VpnConnection -Name '%1' -Force\"").arg(tunnelName()); - adapterRemoveProcess->setNativeArguments(arguments); + // adapterRemoveProcess->setProgram("powershell"); + // QString arguments = QString("-command \"Remove-VpnConnection -Name '%1' -Force\"").arg(tunnelName()); + // adapterRemoveProcess->setNativeArguments(arguments); - adapterRemoveProcess->start(); - adapterRemoveProcess->waitForFinished(5000); + // adapterRemoveProcess->start(); + // adapterRemoveProcess->waitForFinished(5000); + if ( disconnect_vpn()){ + qDebug()<<"VPN was disconnected"; + } + if ( delete_vpn_connection (tunnelName())){ + qDebug()<<"VPN was deleted"; + } } { - auto adapterInstallProcess = new QProcess; + { + if ( !create_new_vpn(tunnelName(), m_config[config_key::hostName].toString())){ + qDebug() <<"Can't create the VPN connect"; + } + } + // auto adapterInstallProcess = new QProcess; - adapterInstallProcess->setProgram("powershell"); - QString arguments = QString("-command \"Add-VpnConnection " - "-ServerAddress '%1' " - "-Name '%2' " - "-TunnelType IKEv2 " - "-AuthenticationMethod MachineCertificate " - "-EncryptionLevel Required " - "-PassThru\"") - .arg(m_config[config_key::hostName].toString()) - .arg(tunnelName()); - adapterInstallProcess->setNativeArguments(arguments); -// connect(adapterInstallProcess, &QProcess::readyRead, [adapterInstallProcess]() { -// qDebug().noquote() << "adapterInstallProcess readyRead" << adapterInstallProcess->readAll(); -// }); - - adapterInstallProcess->start(); - adapterInstallProcess->waitForFinished(5000); + // adapterInstallProcess->setProgram("powershell"); + // QString arguments = QString("-command \"Add-VpnConnection " + // "-ServerAddress '%1' " + // "-Name '%2' " + // "-TunnelType IKEv2 " + // "-AuthenticationMethod MachineCertificate " + // "-EncryptionLevel Required " + // "-PassThru\"") + // .arg(m_config[config_key::hostName].toString()) + // .arg(tunnelName()); + // adapterInstallProcess->setNativeArguments(arguments); + // adapterInstallProcess->start(); + // adapterInstallProcess->waitForFinished(5000); } { auto adapterConfigProcess = new QProcess; adapterConfigProcess->setProgram("powershell"); - QString arguments = QString("-command \"Set-VpnConnectionIPsecConfiguration\ " - "-ConnectionName '%1'\ " + QString arguments = QString("-command \"Set-VpnConnectionIPsecConfiguration\" " + "-ConnectionName '%1' " "-AuthenticationTransformConstants GCMAES128 " "-CipherTransformConstants GCMAES128 " "-EncryptionMethod AES256 " @@ -152,67 +349,98 @@ ErrorCode Ikev2Protocol::start() "-PfsGroup None " "-DHGroup Group14 " "-PassThru -Force\"") - .arg(tunnelName()); + .arg(tunnelName()); adapterConfigProcess->setNativeArguments(arguments); -// connect(adapterConfigProcess, &QProcess::readyRead, [adapterConfigProcess]() { -// qDebug().noquote() << "adapterConfigProcess readyRead" << adapterConfigProcess->readAll(); -// }); + // connect(adapterConfigProcess, &QProcess::readyRead, [adapterConfigProcess]() { + // qDebug().noquote() << "adapterConfigProcess readyRead" << adapterConfigProcess->readAll(); + // }); adapterConfigProcess->start(); adapterConfigProcess->waitForFinished(5000); } - + //*/ { -// char buf[RASBUFFER]= {0}; -// DWORD err = 0; -// RASDIALPARAMSA *param = (RASDIALPARAMSA *)buf; -// param->dwSize = 1064; -// strcpy(param->szEntryName, tunnelName().toStdString().c_str()); -// err = RasDialA(NULL, NULL, param, 0, (LPVOID)rasCallback, &g_h); -// qDebug() << "Ikev2Protocol::start() ret" << err; - - - auto connectProcess = new QProcess; - - connectProcess->setProgram("rasdial"); - QString arguments = QString("\"%1\"") - .arg(tunnelName()); - connectProcess->setNativeArguments(arguments); - - connect(connectProcess, &QProcess::readyRead, [connectProcess]() { - qDebug().noquote() << "connectProcess readyRead" << connectProcess->readAll(); - }); - - connectProcess->start(); - connectProcess->waitForFinished(5000); - } - - setConnectionState(Connected); - return ErrorCode::NoError; - -#endif - - return ErrorCode::NoError; -} - -#ifdef Q_OS_WINDOWS -DWORD CALLBACK rasCallback(UINT msg, RASCONNSTATE rascs, DWORD err) -{ - if(err != 0) { - printf("Error: %d\n", err); - fflush(stdout); - //g_done = 1; - return 0; // stop the connection. - } else { - //printf("%s\n", rasState(rascs)); - fflush(stdout); - if(rascs == RASCS_Connected) { - printf("Success: Connected\n"); - fflush(stdout); - //g_done = 1; + if (!connect_to_vpn(tunnelName())) { + qDebug()<<"We can't connect to VPN"; } - return 1; + } + //setConnectionState(Connecting); + return ErrorCode::NoError; +#else + return ErrorCode::NoError; +#endif + +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#ifdef Q_OS_WINDOWS +bool Ikev2Protocol::create_new_vpn(const QString & vpn_name, + const QString & serv_addr){ + + if ( RasValidateEntryName(nullptr, vpn_name.toStdWString().c_str()) != ERROR_SUCCESS) + return false; + DWORD size = 0; + ::RasGetEntryProperties(nullptr, L"", nullptr, &size, nullptr, nullptr); + LPRASENTRY pras = static_cast(malloc(size)); + memset(pras, 0, size); + pras->dwSize = size; + pras->dwType = RASET_Vpn; + pras->dwRedialCount = 1; + pras->dwRedialPause = 60; + pras->dwfNetProtocols = RASNP_Ip|RASNP_Ipv6; + pras->dwEncryptionType = ET_RequireMax; + wcscpy_s(pras->szLocalPhoneNumber, serv_addr.toStdWString().c_str()); + wcscpy_s(pras->szDeviceType, RASDT_Vpn); + pras->dwfOptions = RASEO_RemoteDefaultGateway; + pras->dwfOptions |= RASEO_RequireDataEncryption; + pras->dwfOptions2 |= RASEO2_RequireMachineCertificates; + pras->dwVpnStrategy = VS_Ikev2Only; + const auto nRet = ::RasSetEntryProperties(nullptr, vpn_name.toStdWString().c_str(), pras, pras->dwSize, NULL, 0); + free(pras); + if (nRet == ERROR_SUCCESS) + return true; + return false; +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool Ikev2Protocol::delete_vpn_connection(const QString &vpn_name){ + + if ( RasDeleteEntry(nullptr, vpn_name.toStdWString().c_str()) == ERROR_SUCCESS){ + return true; + } + return false; +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool Ikev2Protocol::connect_to_vpn(const QString & vpn_name){ + RASDIALPARAMS RasDialParams; + memset(&RasDialParams, 0x0, sizeof(RASDIALPARAMS)); + RasDialParams.dwSize = sizeof(RASDIALPARAMS); + wcscpy_s(RasDialParams.szEntryName, vpn_name.toStdWString().c_str()); + auto ret = RasDial(NULL, NULL, &RasDialParams, 0, + &RasDialFuncCallback, + &hRasConn); + if (ret == ERROR_SUCCESS){ + return true; + } + return false; +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool Ikev2Protocol::disconnect_vpn(){ + if ( hRasConn != nullptr ){ + if ( RasHangUp(hRasConn) != ERROR_SUCCESS) + return false; + } + QThread::msleep(3000); + + return true; +} + +void WINAPI RasDialFuncCallback(UINT unMsg, + RASCONNSTATE rasconnstate, + DWORD dwError ){ + std::lock_guard guard(rasDialFuncMutex); + if (self) { + self->newConnectionStateEventReceived(unMsg, rasconnstate, dwError); } } + #endif diff --git a/client/protocols/ikev2_vpn_protocol.h b/client/protocols/ikev2_vpn_protocol.h index 7cbae06a..05a23643 100644 --- a/client/protocols/ikev2_vpn_protocol.h +++ b/client/protocols/ikev2_vpn_protocol.h @@ -11,20 +11,26 @@ #include "core/ipcclient.h" #ifdef Q_OS_WIN -#include -#include -#include +#include +#include +#include +#include +#include +#include + +#include #include -#include +#include #include #include +#include + #pragma comment(lib, "shlwapi.lib") #pragma comment(lib, "rasapi32.lib") +#pragma comment(lib, "Crypt32.lib") -#define RASBUFFER 0x1000 -#define RASMAXENUM 0x100 #endif class Ikev2Protocol : public VpnProtocol @@ -40,17 +46,32 @@ public: static QString tunnelName() { return "AmneziaVPN IKEv2"; } +public: + void newConnectionStateEventReceived(UINT unMsg, + RASCONNSTATE rasconnstate, + DWORD dwError); + private: void readIkev2Configuration(const QJsonObject &configuration); +#ifdef Q_OS_WIN + //certificates variables +#endif private: QJsonObject m_config; #ifdef Q_OS_WIN - HRASCONN g_h; - int g_done = 0; + //RAS functions and parametrs + HRASCONN hRasConn{nullptr}; + bool create_new_vpn(const QString & vpn_name, + const QString & serv_addr); + bool delete_vpn_connection(const QString &vpn_name); + + bool connect_to_vpn(const QString & vpn_name); + bool disconnect_vpn(); #endif + }; #ifdef Q_OS_WIN diff --git a/client/protocols/protocols_defs.cpp b/client/protocols/protocols_defs.cpp index de54424a..30f1a2f1 100644 --- a/client/protocols/protocols_defs.cpp +++ b/client/protocols/protocols_defs.cpp @@ -73,6 +73,9 @@ QMap ProtocolProps::protocolHumanNames() {Protocol::ShadowSocks, "ShadowSocks"}, {Protocol::Cloak, "Cloak"}, {Protocol::WireGuard, "WireGuard"}, + {Protocol::Ikev2, "IKEv2"}, + {Protocol::L2tp, "L2TP"}, + {Protocol::TorWebSite, "Web site in TOR network"}, {Protocol::Dns, "DNS Service"}, {Protocol::FileShare, "File Sharing Service"}, diff --git a/client/protocols/wireguardprotocol.cpp b/client/protocols/wireguardprotocol.cpp index 16aa93ac..a0f11618 100644 --- a/client/protocols/wireguardprotocol.cpp +++ b/client/protocols/wireguardprotocol.cpp @@ -51,7 +51,7 @@ void WireguardProtocol::stop() m_wireguardStopProcess->setProgram(wireguardExecPath()); - QStringList arguments({"/uninstalltunnelservice", serviceName(), }); + QStringList arguments({"--remove", configPath()}); m_wireguardStopProcess->setArguments(arguments); qDebug() << arguments.join(" "); @@ -114,7 +114,7 @@ void WireguardProtocol::updateRouteGateway(QString line) QString WireguardProtocol::wireguardExecPath() const { #ifdef Q_OS_WIN - return Utils::executable("wireguard/wireguard", true); + return Utils::executable("wireguard/wireguard-service", true); #elif defined Q_OS_LINUX return Utils::usrExecutable("wg"); #else @@ -163,7 +163,7 @@ ErrorCode WireguardProtocol::start() m_wireguardStartProcess->setProgram(wireguardExecPath()); - QStringList arguments({"/installtunnelservice", configPath(), }); + QStringList arguments({"--add", configPath()}); m_wireguardStartProcess->setArguments(arguments); qDebug() << arguments.join(" "); diff --git a/client/resources.qrc b/client/resources.qrc index e16f82c6..26dd1e60 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -16,8 +16,6 @@ images/tray/default.png images/tray/error.png images/arrow_left.png - images/connect_button_connected.png - images/connect_button_disconnected.png fonts/Lato-Black.ttf fonts/Lato-BlackItalic.ttf fonts/Lato-Bold.ttf @@ -126,5 +124,22 @@ server_scripts/ipsec/Dockerfile server_scripts/ipsec/run_container.sh server_scripts/ipsec/start.sh + ui/qml/Pages/Share/PageShareProtoCloak.qml + ui/qml/Pages/Share/PageShareProtocolBase.qml + ui/qml/Pages/Share/PageShareProtoOpenVPN.qml + ui/qml/Pages/Share/PageShareProtoSftp.qml + ui/qml/Pages/Share/PageShareProtoShadowSocks.qml + ui/qml/Pages/Share/PageShareProtoTorWebSite.qml + ui/qml/Controls/TextAreaType.qml + ui/qml/Controls/ContextMenu.qml + ui/qml/Pages/Share/PageShareProtoAmnezia.qml + ui/qml/Controls/ShareConnectionButtonCopyType.qml + ui/qml/Pages/Share/PageShareProtoWireGuard.qml + server_scripts/ipsec/mobileconfig.plist + ui/qml/Pages/Share/PageShareProtoIkev2.qml + server_scripts/ipsec/strongswan.profile + images/animation.gif + images/connected.png + images/disconnected.png diff --git a/client/server_scripts/build_container.sh b/client/server_scripts/build_container.sh index 791d164b..2cda240c 100644 --- a/client/server_scripts/build_container.sh +++ b/client/server_scripts/build_container.sh @@ -1 +1 @@ -sudo docker build -t $CONTAINER_NAME $DOCKERFILE_FOLDER +sudo docker build -t $CONTAINER_NAME $DOCKERFILE_FOLDER --build-arg SERVER_ARCH=$(uname -m) diff --git a/client/server_scripts/ipsec/configure_container.sh b/client/server_scripts/ipsec/configure_container.sh index 0d22a7da..76c4dfaf 100644 --- a/client/server_scripts/ipsec/configure_container.sh +++ b/client/server_scripts/ipsec/configure_container.sh @@ -120,8 +120,8 @@ proxyarp lcp-echo-failure 4 lcp-echo-interval 30 connect-delay 5000 -ms-dns $PRIMARY_DNS -ms-dns $SECONDARY_DNS +ms-dns $PRIMARY_SERVER_DNS +ms-dns $SECONDARY_SERVER_DNS EOF @@ -222,6 +222,8 @@ certutil -z <(head -c 1024 /dev/urandom) \ --extKeyUsage serverAuth \ --extSAN "ip:$SERVER_IP_ADDRESS,dns:$SERVER_IP_ADDRESS" +certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" -a | grep -v CERTIFICATE > /etc/ipsec.d/ca_cert_base64.p12 + cat > /etc/ipsec.d/ikev2.conf < + + + + PayloadContent + + + IKEv2 + + AuthenticationMethod + Certificate + ChildSecurityAssociationParameters + + DiffieHellmanGroup + 14 + EncryptionAlgorithm + AES-128-GCM + LifeTimeInMinutes + 1410 + + DeadPeerDetectionRate + Medium + DisableRedirect + + EnableCertificateRevocationCheck + 0 + EnablePFS + 0 + IKESecurityAssociationParameters + + DiffieHellmanGroup + 14 + EncryptionAlgorithm + AES-256 + IntegrityAlgorithm + SHA2-256 + LifeTimeInMinutes + 1410 + + LocalIdentifier + $CLIENT_NAME + PayloadCertificateUUID + $UUID1 + OnDemandEnabled + 0 + OnDemandRules + + + Action + Connect + + + RemoteAddress + $SERVER_ADDR + RemoteIdentifier + $SERVER_ADDR + UseConfigurationAttributeInternalIPSubnet + 0 + + IPv4 + + OverridePrimary + 1 + + PayloadDescription + Configures VPN settings + PayloadDisplayName + VPN + PayloadOrganization + IKEv2 VPN + PayloadIdentifier + com.apple.vpn.managed.$(UUID_GEN) + PayloadType + com.apple.vpn.managed + PayloadUUID + $(UUID_GEN) + PayloadVersion + 1 + Proxies + + HTTPEnable + 0 + HTTPSEnable + 0 + + UserDefinedName + $SERVER_ADDR + VPNType + IKEv2 + + + PayloadCertificateFileName + $CLIENT_NAME + PayloadContent + +$P12_BASE64 + + PayloadDescription + Adds a PKCS#12-formatted certificate + PayloadDisplayName + $CLIENT_NAME + PayloadIdentifier + com.apple.security.pkcs12.$(UUID_GEN) + PayloadType + com.apple.security.pkcs12 + PayloadUUID + $UUID1 + PayloadVersion + 1 + + + PayloadContent + +$CA_BASE64 + + PayloadCertificateFileName + ikev2vpnca + PayloadDescription + Adds a CA root certificate + PayloadDisplayName + Certificate Authority (CA) + PayloadIdentifier + com.apple.security.root.$(UUID_GEN) + PayloadType + com.apple.security.root + PayloadUUID + $(UUID_GEN) + PayloadVersion + 1 + + + PayloadDisplayName + IKEv2 VPN ($SERVER_ADDR) + PayloadIdentifier + com.apple.vpn.managed.$(UUID_GEN) + PayloadRemovalDisallowed + + PayloadType + Configuration + PayloadUUID + $(UUID_GEN) + PayloadVersion + 1 + + diff --git a/client/server_scripts/ipsec/strongswan.profile b/client/server_scripts/ipsec/strongswan.profile new file mode 100644 index 00000000..007db142 --- /dev/null +++ b/client/server_scripts/ipsec/strongswan.profile @@ -0,0 +1,14 @@ +{ + "uuid": "$UUID", + "name": "IKEv2 VPN ($SERVER_ADDR)", + "type": "ikev2-cert", + "remote": { + "addr": "$SERVER_ADDR" + }, + "local": { + "p12": "$P12_BASE64", + "rsa-pss": "true" + }, + "ike-proposal": "aes256-sha256-modp2048", + "esp-proposal": "aes128gcm16" +} \ No newline at end of file diff --git a/client/server_scripts/openvpn_shadowsocks/Dockerfile b/client/server_scripts/openvpn_shadowsocks/Dockerfile index e5cdea08..b0246730 100644 --- a/client/server_scripts/openvpn_shadowsocks/Dockerfile +++ b/client/server_scripts/openvpn_shadowsocks/Dockerfile @@ -1,7 +1,9 @@ FROM alpine:latest - LABEL maintainer="AmneziaVPN" +ARG SS_RELEASE="v1.11.2" +ARG SERVER_ARCH + #Install required packages RUN apk add --no-cache curl openvpn easy-rsa bash netcat-openbsd dumb-init rng-tools xz RUN apk --update upgrade --no-cache @@ -13,9 +15,9 @@ RUN mkdir -p /opt/amnezia RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh RUN chmod a+x /opt/amnezia/start.sh -RUN curl -L https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.10.9/shadowsocks-v1.10.9.x86_64-unknown-linux-musl.tar.xz > /usr/bin/ss.tar.xz -RUN tar -Jxvf /usr/bin/ss.tar.xz -C /usr/bin/ -RUN chmod a+x /usr/bin/ssserver +RUN curl -L https://github.com/shadowsocks/shadowsocks-rust/releases/download/${SS_RELEASE}/shadowsocks-${SS_RELEASE}.${SERVER_ARCH}-unknown-linux-musl.tar.xz > /usr/bin/ss.tar.xz;\ + tar -Jxvf /usr/bin/ss.tar.xz -C /usr/bin/;\ + chmod a+x /usr/bin/ssserver; # Tune network RUN echo -e " \n\ diff --git a/client/server_scripts/website_tor/run_container.sh b/client/server_scripts/website_tor/run_container.sh index 11adb569..e5dfce98 100644 --- a/client/server_scripts/website_tor/run_container.sh +++ b/client/server_scripts/website_tor/run_container.sh @@ -1,3 +1,6 @@ # Run container +sudo docker stop amnezia-tor +sudo docker rm amnezia-tor sudo docker run -d -p 80:80 --restart always --name $CONTAINER_NAME tutum/wordpress sudo docker run -d --link $CONTAINER_NAME --name amnezia-tor goldy/tor-hidden-service +sudo docker exec -i amnezia-tor apk add bash diff --git a/client/ui/models/protocols_model.cpp b/client/ui/models/protocols_model.cpp index cc333f40..7fe66dd0 100644 --- a/client/ui/models/protocols_model.cpp +++ b/client/ui/models/protocols_model.cpp @@ -24,7 +24,7 @@ QHash ProtocolsModel::roleNames() const { QVariant ProtocolsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 - || index.row() >= ContainerProps::allContainers().size()) { + || index.row() >= ProtocolProps::allProtocols().size()) { return QVariant(); } diff --git a/client/ui/pages.h b/client/ui/pages.h index 64537472..235b912f 100644 --- a/client/ui/pages.h +++ b/client/ui/pages.h @@ -4,6 +4,19 @@ #include #include +class PageType : public QObject +{ + Q_GADGET + +public: + enum Type { + Basic, + Proto, + ShareProto + }; + Q_ENUM(Type) +}; + namespace PageEnumNS { Q_NAMESPACE @@ -11,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}; + ProtocolSettings, ProtocolShare}; Q_ENUM_NS(Page) static void declareQmlPageEnum() { diff --git a/client/ui/pages_logic/GeneralSettingsLogic.cpp b/client/ui/pages_logic/GeneralSettingsLogic.cpp index 33d28185..92696afa 100644 --- a/client/ui/pages_logic/GeneralSettingsLogic.cpp +++ b/client/ui/pages_logic/GeneralSettingsLogic.cpp @@ -2,6 +2,7 @@ #include "ShareConnectionLogic.h" #include "../uilogic.h" +#include "../models/protocols_model.h" GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent) @@ -11,13 +12,18 @@ GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent): void GeneralSettingsLogic::onUpdatePage() { + uiLogic()->selectedServerIndex = m_settings.defaultServerIndex(); + uiLogic()->selectedDockerContainer = m_settings.defaultContainer(m_settings.defaultServerIndex()); + set_pushButtonGeneralSettingsShareConnectionEnable(m_settings.haveAuthData(m_settings.defaultServerIndex())); } void GeneralSettingsLogic::onPushButtonGeneralSettingsServerSettingsClicked() { uiLogic()->selectedServerIndex = m_settings.defaultServerIndex(); - uiLogic()->goToPage(Page::ServerSettings); + uiLogic()->selectedDockerContainer = m_settings.defaultContainer(m_settings.defaultServerIndex()); + + emit uiLogic()->goToPage(Page::ServerSettings); } void GeneralSettingsLogic::onPushButtonGeneralSettingsShareConnectionClicked() @@ -25,6 +31,9 @@ void GeneralSettingsLogic::onPushButtonGeneralSettingsShareConnectionClicked() uiLogic()->selectedServerIndex = m_settings.defaultServerIndex(); uiLogic()->selectedDockerContainer = m_settings.defaultContainer(uiLogic()->selectedServerIndex); - uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, m_settings.serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer); - uiLogic()->goToPage(Page::ShareConnection); + qobject_cast(uiLogic()->protocolsModel())->setSelectedServerIndex(uiLogic()->selectedServerIndex); + qobject_cast(uiLogic()->protocolsModel())->setSelectedDockerContainer(uiLogic()->selectedDockerContainer); + + uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + emit uiLogic()->goToPage(Page::ShareConnection); } diff --git a/client/ui/pages_logic/ServerConfiguringProgressLogic.cpp b/client/ui/pages_logic/ServerConfiguringProgressLogic.cpp index 8909a9f5..32ae366d 100644 --- a/client/ui/pages_logic/ServerConfiguringProgressLogic.cpp +++ b/client/ui/pages_logic/ServerConfiguringProgressLogic.cpp @@ -18,6 +18,11 @@ ServerConfiguringProgressLogic::ServerConfiguringProgressLogic(UiLogic *logic, Q } +void ServerConfiguringProgressLogic::onUpdatePage() +{ + set_progressBarValue(0); +} + ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function &action) { diff --git a/client/ui/pages_logic/ServerConfiguringProgressLogic.h b/client/ui/pages_logic/ServerConfiguringProgressLogic.h index edd2f4e9..9620d4c3 100644 --- a/client/ui/pages_logic/ServerConfiguringProgressLogic.h +++ b/client/ui/pages_logic/ServerConfiguringProgressLogic.h @@ -22,6 +22,7 @@ public: explicit ServerConfiguringProgressLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~ServerConfiguringProgressLogic() = default; + void onUpdatePage() override; ErrorCode doInstallAction(const std::function &action); private: diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index 23fd1880..22afaa30 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -43,12 +43,12 @@ void ServerContainersLogic::onPushButtonProtoSettingsClicked(DockerContainer c, void ServerContainersLogic::onPushButtonDefaultClicked(DockerContainer c) { m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, c); - onUpdatePage(); + uiLogic()->onUpdateAllPages(); } void ServerContainersLogic::onPushButtonShareClicked(DockerContainer c) { - uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, m_settings.serverCredentials(uiLogic()->selectedServerIndex), c); + uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, c); emit uiLogic()->goToPage(Page::ShareConnection); } @@ -64,7 +64,7 @@ void ServerContainersLogic::onPushButtonRemoveClicked(DockerContainer container) if (c.isEmpty()) m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, DockerContainer::None); else m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, c.keys().first()); } - onUpdatePage(); + uiLogic()->onUpdateAllPages(); } void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int port, TransportProto tp) @@ -85,6 +85,6 @@ void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int p } } - onUpdatePage(); + uiLogic()->onUpdateAllPages(); emit uiLogic()->closePage(); } diff --git a/client/ui/pages_logic/ServerListLogic.cpp b/client/ui/pages_logic/ServerListLogic.cpp index 4fac4271..05cac928 100644 --- a/client/ui/pages_logic/ServerListLogic.cpp +++ b/client/ui/pages_logic/ServerListLogic.cpp @@ -14,7 +14,7 @@ ServerListLogic::ServerListLogic(UiLogic *logic, QObject *parent): void ServerListLogic::onServerListPushbuttonDefaultClicked(int index) { m_settings.setDefaultServer(index); - onUpdatePage(); + uiLogic()->onUpdateAllPages(); } void ServerListLogic::onServerListPushbuttonSettingsClicked(int index) diff --git a/client/ui/pages_logic/ServerSettingsLogic.cpp b/client/ui/pages_logic/ServerSettingsLogic.cpp index bfb1a075..27ce64fb 100644 --- a/client/ui/pages_logic/ServerSettingsLogic.cpp +++ b/client/ui/pages_logic/ServerSettingsLogic.cpp @@ -37,7 +37,7 @@ void ServerSettingsLogic::onUpdatePage() .arg(port)); set_lineEditDescriptionText(server.value(config_key::description).toString()); QString selectedContainerName = m_settings.defaultContainerName(uiLogic()->selectedServerIndex); - set_labelCurrentVpnProtocolText(tr("Protocol: ") + selectedContainerName); + set_labelCurrentVpnProtocolText(tr("Service: ") + selectedContainerName); } void ServerSettingsLogic::onPushButtonClearServer() @@ -90,8 +90,7 @@ void ServerSettingsLogic::onPushButtonForgetServer() uiLogic()->selectedServerIndex = -1; - - uiLogic()->serverListLogic()->onUpdatePage(); + uiLogic()->onUpdateAllPages(); if (m_settings.serversCount() == 0) { uiLogic()->setStartPage(Page::Start); @@ -121,11 +120,11 @@ void ServerSettingsLogic::onLineEditDescriptionEditingFinished() QJsonObject server = m_settings.server(uiLogic()->selectedServerIndex); server.insert(config_key::description, newText); m_settings.editServer(uiLogic()->selectedServerIndex, server); - uiLogic()->serverListLogic()->onUpdatePage(); + uiLogic()->onUpdateAllPages(); } void ServerSettingsLogic::onPushButtonShareFullClicked() { - uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, m_settings.serverCredentials(uiLogic()->selectedServerIndex), DockerContainer::None); - uiLogic()->goToPage(Page::ShareConnection); + uiLogic()->shareConnectionLogic()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None); + emit uiLogic()->goToShareProtocolPage(Protocol::Any); } diff --git a/client/ui/pages_logic/ShareConnectionLogic.cpp b/client/ui/pages_logic/ShareConnectionLogic.cpp index a75edd71..ef7aec91 100644 --- a/client/ui/pages_logic/ShareConnectionLogic.cpp +++ b/client/ui/pages_logic/ShareConnectionLogic.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "ShareConnectionLogic.h" @@ -12,6 +13,8 @@ #include "configurators/vpn_configurator.h" #include "configurators/openvpn_configurator.h" #include "configurators/shadowsocks_configurator.h" +#include "configurators/wireguard_configurator.h" +#include "configurators/ikev2_configurator.h" #include "configurators/ssh_configurator.h" #include "defines.h" @@ -21,333 +24,226 @@ ShareConnectionLogic::ShareConnectionLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), - m_pageShareAmneziaVisible{true}, - m_pageShareOpenVpnVisible{true}, - m_pageShareShadowSocksVisible{true}, - m_pageShareCloakVisible{true}, - m_pageShareFullAccessVisible{true}, m_textEditShareOpenVpnCodeText{}, - m_pushButtonShareOpenVpnCopyEnabled{false}, - m_pushButtonShareOpenVpnSaveEnabled{false}, - m_toolBoxShareConnectionCurrentIndex{-1}, - m_pushButtonShareShadowSocksCopyEnabled{false}, m_lineEditShareShadowSocksStringText{}, - m_labelShareShadowSocksQrCodeText{}, - m_labelShareShadowSocksServerText{}, - m_labelShareShadowSocksPortText{}, - m_labelShareShadowSocksMethodText{}, - m_labelShareShadowSocksPasswordText{}, - m_plainTextEditShareCloakText{}, - m_pushButtonShareCloakCopyEnabled{false}, - m_textEditShareFullCodeText{}, - m_textEditShareAmneziaCodeText{}, - m_pushButtonShareFullCopyText{tr("Copy")}, - m_pushButtonShareAmneziaCopyText{tr("Copy")}, - m_pushButtonShareOpenVpnCopyText{tr("Copy")}, - m_pushButtonShareShadowSocksCopyText{tr("Copy")}, - m_pushButtonShareCloakCopyText{tr("Copy")}, - m_pushButtonShareAmneziaGenerateEnabled{true}, - m_pushButtonShareAmneziaCopyEnabled{true}, - m_pushButtonShareAmneziaGenerateText{tr("Generate config")}, - m_pushButtonShareOpenVpnGenerateEnabled{true}, - m_pushButtonShareOpenVpnGenerateText{tr("Generate config")} + m_shareShadowSocksQrCodeText{}, + m_textEditShareCloakText{}, + m_textEditShareAmneziaCodeText{} { - // TODO consider move to Component.onCompleted - updateSharingPage(uiLogic()->selectedServerIndex, m_settings.serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer); } - -void ShareConnectionLogic::onPushButtonShareFullCopyClicked() +void ShareConnectionLogic::onUpdatePage() { - QGuiApplication::clipboard()->setText(textEditShareFullCodeText()); - set_pushButtonShareFullCopyText(tr("Copied")); + set_textEditShareAmneziaCodeText(tr("")); + set_shareAmneziaQrCodeText(""); - QTimer::singleShot(3000, this, [this]() { - set_pushButtonShareFullCopyText(tr("Copy")); - }); -} + set_textEditShareOpenVpnCodeText(""); -void ShareConnectionLogic::onPushButtonShareFullSaveClicked() -{ - if (textEditShareFullCodeText().isEmpty()) return; + set_shareShadowSocksQrCodeText(""); + set_textEditShareShadowSocksText(""); + set_lineEditShareShadowSocksStringText(""); - QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save AmneziaVPN config"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.amnezia"); - QSaveFile save(fileName); - save.open(QIODevice::WriteOnly); - save.write(textEditShareFullCodeText().toUtf8()); - save.commit(); -} + set_textEditShareCloakText(""); -void ShareConnectionLogic::onPushButtonShareAmneziaCopyClicked() -{ - if (textEditShareAmneziaCodeText().isEmpty()) return; + set_textEditShareWireGuardCodeText(""); + set_shareWireGuardQrCodeText(""); - QGuiApplication::clipboard()->setText(textEditShareAmneziaCodeText()); - set_pushButtonShareAmneziaCopyText(tr("Copied")); - - QTimer::singleShot(3000, this, [this]() { - set_pushButtonShareAmneziaCopyText(tr("Copy")); - }); -} - -void ShareConnectionLogic::onPushButtonShareAmneziaSaveClicked() -{ - if (textEditShareAmneziaCodeText().isEmpty()) return; - - QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save AmneziaVPN config"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.amnezia"); - QSaveFile save(fileName); - save.open(QIODevice::WriteOnly); - save.write(textEditShareAmneziaCodeText().toUtf8()); - save.commit(); -} - -void ShareConnectionLogic::onPushButtonShareOpenVpnCopyClicked() -{ - QGuiApplication::clipboard()->setText(textEditShareOpenVpnCodeText()); - set_pushButtonShareOpenVpnCopyText(tr("Copied")); - - QTimer::singleShot(3000, this, [this]() { - set_pushButtonShareOpenVpnCopyText(tr("Copy")); - }); -} - -void ShareConnectionLogic::onPushButtonShareShadowSocksCopyClicked() -{ - QGuiApplication::clipboard()->setText(lineEditShareShadowSocksStringText()); - set_pushButtonShareShadowSocksCopyText(tr("Copied")); - - QTimer::singleShot(3000, this, [this]() { - set_pushButtonShareShadowSocksCopyText(tr("Copy")); - }); -} - -void ShareConnectionLogic::onPushButtonShareCloakCopyClicked() -{ - QGuiApplication::clipboard()->setText(plainTextEditShareCloakText()); - set_pushButtonShareCloakCopyText(tr("Copied")); - - QTimer::singleShot(3000, this, [this]() { - set_pushButtonShareCloakCopyText(tr("Copy")); - }); + set_textEditShareIkev2CertText(""); + set_textEditShareIkev2MobileConfigText(""); + set_textEditShareIkev2StrongSwanConfigText(""); } void ShareConnectionLogic::onPushButtonShareAmneziaGenerateClicked() { - set_pushButtonShareAmneziaGenerateEnabled(false); - set_pushButtonShareAmneziaCopyEnabled(false); - set_pushButtonShareAmneziaGenerateText(tr("Generating...")); - qApp->processEvents(); + set_textEditShareAmneziaCodeText(""); + set_shareAmneziaQrCodeText(""); - ServerCredentials credentials = m_settings.serverCredentials(uiLogic()->selectedServerIndex); - QJsonObject containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); - containerConfig.insert(config_key::container, ContainerProps::containerToString(uiLogic()->selectedDockerContainer)); - - ErrorCode e = ErrorCode::NoError; - for (Protocol p: ContainerProps::protocolsForContainer(uiLogic()->selectedDockerContainer)) { - QJsonObject protoConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, p); - - QString cfg = VpnConfigurator::genVpnProtocolConfig(credentials, uiLogic()->selectedDockerContainer, containerConfig, p, &e); - if (e) { - cfg = "Error generating config"; - break; - } - protoConfig.insert(config_key::last_config, cfg); - - containerConfig.insert(ProtocolProps::protoToString(p), protoConfig); - } - - QByteArray ba; - if (!e) { - QJsonObject serverConfig = m_settings.server(uiLogic()->selectedServerIndex); - 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(uiLogic()->selectedDockerContainer)); - - - ba = QJsonDocument(serverConfig).toJson().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - set_textEditShareAmneziaCodeText(QString("vpn://%1").arg(QString(ba))); + QJsonObject serverConfig; + // Full access + if (shareFullAccess()) { + serverConfig = m_settings.server(uiLogic()->selectedServerIndex); } + // Container share else { - set_textEditShareAmneziaCodeText(tr("Error while generating connection profile")); + ServerCredentials credentials = m_settings.serverCredentials(uiLogic()->selectedServerIndex); + QJsonObject containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); + containerConfig.insert(config_key::container, ContainerProps::containerToString(uiLogic()->selectedDockerContainer)); + + ErrorCode e = ErrorCode::NoError; + for (Protocol p: ContainerProps::protocolsForContainer(uiLogic()->selectedDockerContainer)) { + QJsonObject protoConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, p); + + QString cfg = VpnConfigurator::genVpnProtocolConfig(credentials, uiLogic()->selectedDockerContainer, containerConfig, p, &e); + if (e) { + cfg = "Error generating config"; + break; + } + protoConfig.insert(config_key::last_config, cfg); + containerConfig.insert(ProtocolProps::protoToString(p), protoConfig); + } + + QByteArray ba; + if (!e) { + serverConfig = m_settings.server(uiLogic()->selectedServerIndex); + 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(uiLogic()->selectedDockerContainer)); + } + else { + set_textEditShareAmneziaCodeText(tr("Error while generating connection profile")); + return; + } } - set_pushButtonShareAmneziaGenerateEnabled(true); - set_pushButtonShareAmneziaCopyEnabled(true); - set_pushButtonShareAmneziaGenerateText(tr("Generate config")); + QByteArray ba = QJsonDocument(serverConfig).toBinaryData(); + ba = qCompress(ba, 8); + QString code = QString("vpn://%1").arg(QString(ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals))); + set_textEditShareAmneziaCodeText(code); + + if (ba.size() < 2900) { + QImage qr = updateQRCodeImage(ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)); + set_shareAmneziaQrCodeText(imageToBase64(qr)); + } } void ShareConnectionLogic::onPushButtonShareOpenVpnGenerateClicked() { - set_pushButtonShareOpenVpnGenerateEnabled(false); - set_pushButtonShareOpenVpnCopyEnabled(false); - set_pushButtonShareOpenVpnSaveEnabled(false); - set_pushButtonShareOpenVpnGenerateText(tr("Generating...")); - ServerCredentials credentials = m_settings.serverCredentials(uiLogic()->selectedServerIndex); const QJsonObject &containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer); ErrorCode e = ErrorCode::NoError; QString cfg = OpenVpnConfigurator::genOpenVpnConfig(credentials, uiLogic()->selectedDockerContainer, containerConfig, &e); - cfg = OpenVpnConfigurator::processConfigWithExportSettings(cfg); + cfg = VpnConfigurator::processConfigWithExportSettings(uiLogic()->selectedDockerContainer, Protocol::OpenVpn, cfg); set_textEditShareOpenVpnCodeText(QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString()); - - set_pushButtonShareOpenVpnGenerateEnabled(true); - set_pushButtonShareOpenVpnCopyEnabled(true); - set_pushButtonShareOpenVpnSaveEnabled(true); - set_pushButtonShareOpenVpnGenerateText(tr("Generate config")); } -void ShareConnectionLogic::onPushButtonShareOpenVpnSaveClicked() +void ShareConnectionLogic::onPushButtonShareShadowSocksGenerateClicked() { - QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save OpenVPN config"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.ovpn"); + int serverIndex = uiLogic()->selectedServerIndex; + DockerContainer container = uiLogic()->selectedDockerContainer; + ServerCredentials credentials = m_settings.serverCredentials(serverIndex); - QSaveFile save(fileName); - save.open(QIODevice::WriteOnly); - save.write(textEditShareOpenVpnCodeText().toUtf8()); - save.commit(); + QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, Protocol::ShadowSocks); + QString cfg = protoConfig.value(config_key::last_config).toString(); + + if (cfg.isEmpty()) { + const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + + ErrorCode e = ErrorCode::NoError; + cfg = ShadowSocksConfigurator::genShadowSocksConfig(credentials, container, containerConfig, &e); + } + + QJsonObject ssConfig = QJsonDocument::fromJson(cfg.toUtf8()).object(); + + QString ssString = QString("%1:%2@%3:%4") + .arg(ssConfig.value("method").toString()) + .arg(ssConfig.value("password").toString()) + .arg(ssConfig.value("server").toString()) + .arg(ssConfig.value("server_port").toString()); + + ssString = "ss://" + ssString.toUtf8().toBase64(); + set_lineEditShareShadowSocksStringText(ssString); + + QImage qr = updateQRCodeImage(ssString.toUtf8()); + set_shareShadowSocksQrCodeText(imageToBase64(qr)); + + QString humanString = QString("Server: %3\n" + "Port: %4\n" + "Encryption: %1\n" + "Password: %2") + .arg(ssConfig.value("method").toString()) + .arg(ssConfig.value("password").toString()) + .arg(ssConfig.value("server").toString()) + .arg(ssConfig.value("server_port").toString()); + + set_textEditShareShadowSocksText(humanString); } - -void ShareConnectionLogic::updateSharingPage(int serverIndex, const ServerCredentials &credentials, - DockerContainer container) +void ShareConnectionLogic::onPushButtonShareCloakGenerateClicked() { - uiLogic()->selectedDockerContainer = container; - uiLogic()->selectedServerIndex = serverIndex; + int serverIndex = uiLogic()->selectedServerIndex; + DockerContainer container = uiLogic()->selectedDockerContainer; + ServerCredentials credentials = m_settings.serverCredentials(serverIndex); + + QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, Protocol::Cloak); + QString cfg = protoConfig.value(config_key::last_config).toString(); + + if (cfg.isEmpty()) { + const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + + ErrorCode e = ErrorCode::NoError; + cfg = CloakConfigurator::genCloakConfig(credentials, container, containerConfig, &e); + } + + QJsonObject cloakConfig = QJsonDocument::fromJson(cfg.toUtf8()).object(); + cloakConfig.remove(config_key::transport_proto); + cloakConfig.insert("ProxyMethod", "shadowsocks"); + + set_textEditShareCloakText(QJsonDocument(cloakConfig).toJson()); +} + +void ShareConnectionLogic::onPushButtonShareWireGuardGenerateClicked() +{ + int serverIndex = uiLogic()->selectedServerIndex; + DockerContainer container = uiLogic()->selectedDockerContainer; + ServerCredentials credentials = m_settings.serverCredentials(serverIndex); + + const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); + + ErrorCode e = ErrorCode::NoError; + QString cfg = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, &e); + cfg = VpnConfigurator::processConfigWithExportSettings(container, Protocol::WireGuard, cfg); + cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString(); + + set_textEditShareWireGuardCodeText(cfg); + + QImage qr = updateQRCodeImage(cfg.toUtf8()); + set_shareWireGuardQrCodeText(imageToBase64(qr)); +} + +void ShareConnectionLogic::onPushButtonShareIkev2GenerateClicked() +{ + int serverIndex = uiLogic()->selectedServerIndex; + DockerContainer container = uiLogic()->selectedDockerContainer; + ServerCredentials credentials = m_settings.serverCredentials(serverIndex); //const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); - set_pageShareAmneziaVisible(false); - set_pageShareOpenVpnVisible(false); - set_pageShareShadowSocksVisible(false); - set_pageShareCloakVisible(false); - set_pageShareFullAccessVisible(false); + Ikev2Configurator::ConnectionData connData = Ikev2Configurator::prepareIkev2Config(credentials, container); - enum currentWidget { - full_access = 0, - share_amezia, - share_openvpn, - share_shadowshock, - share_cloak - }; + QString cfg = Ikev2Configurator::genIkev2Config(connData); + cfg = VpnConfigurator::processConfigWithExportSettings(container, Protocol::Ikev2, cfg); + cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::cert].toString(); - if (container == DockerContainer::OpenVpn) { - set_pageShareAmneziaVisible(true); - set_pageShareOpenVpnVisible(true); + set_textEditShareIkev2CertText(cfg); - QString cfg = tr("Press Generate config"); - set_textEditShareOpenVpnCodeText(cfg); - set_pushButtonShareOpenVpnCopyEnabled(false); - set_pushButtonShareOpenVpnSaveEnabled(false); + QString mobileCfg = Ikev2Configurator::genMobileConfig(connData); + set_textEditShareIkev2MobileConfigText(mobileCfg); - set_toolBoxShareConnectionCurrentIndex(share_openvpn); - } + QString strongSwanCfg = Ikev2Configurator::genStrongSwanConfig(connData); + set_textEditShareIkev2StrongSwanConfigText(strongSwanCfg); - if (container == DockerContainer::ShadowSocks || - container == DockerContainer::Cloak) { - set_pageShareAmneziaVisible(true); - set_pageShareShadowSocksVisible(true); - - QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, Protocol::ShadowSocks); - QString cfg = protoConfig.value(config_key::last_config).toString(); - - if (cfg.isEmpty()) { - const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); - - ErrorCode e = ErrorCode::NoError; - cfg = ShadowSocksConfigurator::genShadowSocksConfig(credentials, container, containerConfig, &e); - - set_pushButtonShareShadowSocksCopyEnabled(true); - } - - QJsonObject ssConfig = QJsonDocument::fromJson(cfg.toUtf8()).object(); - - QString ssString = QString("%1:%2@%3:%4") - .arg(ssConfig.value("method").toString()) - .arg(ssConfig.value("password").toString()) - .arg(ssConfig.value("server").toString()) - .arg(ssConfig.value("server_port").toString()); - - ssString = "ss://" + ssString.toUtf8().toBase64(); - set_lineEditShareShadowSocksStringText(ssString); - updateQRCodeImage(ssString, [this](const QString& labelText) ->void { - set_labelShareShadowSocksQrCodeText(labelText); - }); - - set_labelShareShadowSocksServerText(ssConfig.value("server").toString()); - set_labelShareShadowSocksPortText(ssConfig.value("server_port").toString()); - set_labelShareShadowSocksMethodText(ssConfig.value("method").toString()); - set_labelShareShadowSocksPasswordText(ssConfig.value("password").toString()); - - set_toolBoxShareConnectionCurrentIndex(share_shadowshock); - } - - if (container == DockerContainer::Cloak) { - //ui->toolBox_share_connection->addItem(ui->page_share_amnezia, tr(" Share for Amnezia client")); - set_pageShareCloakVisible(true); - set_plainTextEditShareCloakText(QString("")); - - QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, Protocol::Cloak); - QString cfg = protoConfig.value(config_key::last_config).toString(); - - if (cfg.isEmpty()) { - const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container); - - ErrorCode e = ErrorCode::NoError; - cfg = CloakConfigurator::genCloakConfig(credentials, container, containerConfig, &e); - - set_pushButtonShareCloakCopyEnabled(true); - } - - QJsonObject cloakConfig = QJsonDocument::fromJson(cfg.toUtf8()).object(); - cloakConfig.remove(config_key::transport_proto); - cloakConfig.insert("ProxyMethod", "shadowsocks"); - - set_plainTextEditShareCloakText(QJsonDocument(cloakConfig).toJson()); - } - - // Full access - if (container == DockerContainer::None) { - set_pageShareFullAccessVisible(true); - - const QJsonObject &server = m_settings.server(uiLogic()->selectedServerIndex); - - QByteArray ba = QJsonDocument(server).toJson().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - - set_textEditShareFullCodeText(QString("vpn://%1").arg(QString(ba))); - set_toolBoxShareConnectionCurrentIndex(full_access); - } - - //ui->toolBox_share_connection->addItem(ui->page_share_amnezia, tr(" Share for Amnezia client")); - - // Amnezia sharing - // QJsonObject exportContainer; - // for (Protocol p: protocolsForContainer(container)) { - // QJsonObject protocolConfig = containerConfig.value(ProtocolProps::protoToString(p)).toObject(); - // protocolConfig.remove(config_key::last_config); - // exportContainer.insert(ProtocolProps::protoToString(p), protocolConfig); - // } - // exportContainer.insert(config_key::container, containerToString(container)); - - // ui->textEdit_share_amnezia_code->setPlainText(QJsonDocument(exportContainer).toJson()); - - set_textEditShareAmneziaCodeText(tr("")); } -void ShareConnectionLogic::updateQRCodeImage(const QString &text, const std::function& set_labelFunc) +void ShareConnectionLogic::updateSharingPage(int serverIndex, DockerContainer container) +{ + uiLogic()->selectedDockerContainer = container; + uiLogic()->selectedServerIndex = serverIndex; + set_shareFullAccess(container == DockerContainer::None); +} + +QImage ShareConnectionLogic::updateQRCodeImage(const QByteArray &data) { int levelIndex = 1; int versionIndex = 0; bool bExtent = true; int maskIndex = -1; - m_qrEncode.EncodeData( levelIndex, versionIndex, bExtent, maskIndex, text.toUtf8().data() ); + m_qrEncode.EncodeData( levelIndex, versionIndex, bExtent, maskIndex, data.data() ); int qrImageSize = m_qrEncode.m_nSymbleSize; @@ -361,10 +257,14 @@ void ShareConnectionLogic::updateQRCodeImage(const QString &text, const std::fun if ( m_qrEncode.m_byModuleData[i][j] ) encodeImage.setPixel( i + QR_MARGIN, j + QR_MARGIN, 0 ); - QByteArray byteArray; - QBuffer buffer(&byteArray); - encodeImage.save(&buffer, "PNG"); // writes the image in PNG format inside the buffer - QString iconBase64 = QString::fromLatin1(byteArray.toBase64().data()); - - set_labelFunc(iconBase64); + return encodeImage; +} + +QString ShareConnectionLogic::imageToBase64(const QImage &image) +{ + QByteArray ba; + QBuffer bu(&ba); + bu.open(QIODevice::WriteOnly); + image.save(&bu, "PNG"); + return "data:image/png;base64," + QString::fromLatin1(ba.toBase64().data()); } diff --git a/client/ui/pages_logic/ShareConnectionLogic.h b/client/ui/pages_logic/ShareConnectionLogic.h index cdf8961b..bc99fe1b 100644 --- a/client/ui/pages_logic/ShareConnectionLogic.h +++ b/client/ui/pages_logic/ShareConnectionLogic.h @@ -11,56 +11,43 @@ class ShareConnectionLogic: public PageLogicBase Q_OBJECT public: - AUTO_PROPERTY(bool, pageShareAmneziaVisible) - AUTO_PROPERTY(bool, pageShareOpenVpnVisible) - AUTO_PROPERTY(bool, pageShareShadowSocksVisible) - AUTO_PROPERTY(bool, pageShareCloakVisible) - AUTO_PROPERTY(bool, pageShareFullAccessVisible) - AUTO_PROPERTY(QString, textEditShareOpenVpnCodeText) - AUTO_PROPERTY(bool, pushButtonShareOpenVpnCopyEnabled) - AUTO_PROPERTY(bool, pushButtonShareOpenVpnSaveEnabled) - AUTO_PROPERTY(int, toolBoxShareConnectionCurrentIndex) - AUTO_PROPERTY(bool, pushButtonShareShadowSocksCopyEnabled) - AUTO_PROPERTY(QString, lineEditShareShadowSocksStringText) - AUTO_PROPERTY(QString, labelShareShadowSocksQrCodeText) - AUTO_PROPERTY(QString, labelShareShadowSocksServerText) - AUTO_PROPERTY(QString, labelShareShadowSocksPortText) - AUTO_PROPERTY(QString, labelShareShadowSocksMethodText) - AUTO_PROPERTY(QString, labelShareShadowSocksPasswordText) - AUTO_PROPERTY(QString, plainTextEditShareCloakText) - AUTO_PROPERTY(bool, pushButtonShareCloakCopyEnabled) - AUTO_PROPERTY(QString, textEditShareFullCodeText) + AUTO_PROPERTY(bool, shareFullAccess) + AUTO_PROPERTY(QString, textEditShareAmneziaCodeText) - AUTO_PROPERTY(QString, pushButtonShareFullCopyText) - AUTO_PROPERTY(QString, pushButtonShareAmneziaCopyText) - AUTO_PROPERTY(QString, pushButtonShareOpenVpnCopyText) - AUTO_PROPERTY(QString, pushButtonShareShadowSocksCopyText) - AUTO_PROPERTY(QString, pushButtonShareCloakCopyText) - AUTO_PROPERTY(bool, pushButtonShareAmneziaGenerateEnabled) - AUTO_PROPERTY(bool, pushButtonShareAmneziaCopyEnabled) - AUTO_PROPERTY(QString, pushButtonShareAmneziaGenerateText) - AUTO_PROPERTY(bool, pushButtonShareOpenVpnGenerateEnabled) - AUTO_PROPERTY(QString, pushButtonShareOpenVpnGenerateText) + AUTO_PROPERTY(QString, shareAmneziaQrCodeText) + + AUTO_PROPERTY(QString, textEditShareOpenVpnCodeText) + + AUTO_PROPERTY(QString, textEditShareShadowSocksText) + AUTO_PROPERTY(QString, lineEditShareShadowSocksStringText) + AUTO_PROPERTY(QString, shareShadowSocksQrCodeText) + + AUTO_PROPERTY(QString, textEditShareCloakText) + + AUTO_PROPERTY(QString, textEditShareWireGuardCodeText) + AUTO_PROPERTY(QString, shareWireGuardQrCodeText) + + AUTO_PROPERTY(QString, textEditShareIkev2CertText) + AUTO_PROPERTY(QString, textEditShareIkev2MobileConfigText) + AUTO_PROPERTY(QString, textEditShareIkev2StrongSwanConfigText) public: - Q_INVOKABLE void onPushButtonShareFullCopyClicked(); - Q_INVOKABLE void onPushButtonShareFullSaveClicked(); - Q_INVOKABLE void onPushButtonShareAmneziaCopyClicked(); - Q_INVOKABLE void onPushButtonShareAmneziaSaveClicked(); - Q_INVOKABLE void onPushButtonShareOpenVpnCopyClicked(); - Q_INVOKABLE void onPushButtonShareShadowSocksCopyClicked(); - Q_INVOKABLE void onPushButtonShareCloakCopyClicked(); Q_INVOKABLE void onPushButtonShareAmneziaGenerateClicked(); Q_INVOKABLE void onPushButtonShareOpenVpnGenerateClicked(); - Q_INVOKABLE void onPushButtonShareOpenVpnSaveClicked(); + Q_INVOKABLE void onPushButtonShareShadowSocksGenerateClicked(); + Q_INVOKABLE void onPushButtonShareCloakGenerateClicked(); + Q_INVOKABLE void onPushButtonShareWireGuardGenerateClicked(); + Q_INVOKABLE void onPushButtonShareIkev2GenerateClicked(); + + Q_INVOKABLE virtual void onUpdatePage() override; public: explicit ShareConnectionLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~ShareConnectionLogic() = default; - void updateSharingPage(int serverIndex, const ServerCredentials &credentials, - DockerContainer container); - void updateQRCodeImage(const QString &text, const std::function& setLabelFunc); + void updateSharingPage(int serverIndex, DockerContainer container); + QImage updateQRCodeImage(const QByteArray &data); + QString imageToBase64(const QImage &image); private: CQR_Encode m_qrEncode; diff --git a/client/ui/pages_logic/SitesLogic.cpp b/client/ui/pages_logic/SitesLogic.cpp index 9607aee0..a035e065 100644 --- a/client/ui/pages_logic/SitesLogic.cpp +++ b/client/ui/pages_logic/SitesLogic.cpp @@ -22,7 +22,7 @@ SitesLogic::SitesLogic(UiLogic *logic, QObject *parent): sitesModels.insert(Settings::VpnAllExceptSites, new SitesModel(Settings::VpnAllExceptSites)); } -void SitesLogic::updateSitesPage() +void SitesLogic::onUpdatePage() { Settings::RouteMode m = m_settings.routeMode(); if (m == Settings::VpnAllSites) return; @@ -71,7 +71,7 @@ void SitesLogic::onPushButtonAddCustomSitesClicked() uiLogic()->m_vpnConnection->flushDns(); } - updateSitesPage(); + onUpdatePage(); }; const auto &cbResolv = [this, cbProcess](const QHostInfo &hostInfo){ @@ -93,7 +93,7 @@ void SitesLogic::onPushButtonAddCustomSitesClicked() } else { cbProcess(newSite, ""); - updateSitesPage(); + onUpdatePage(); QHostInfo::lookupHost(newSite, this, cbResolv); } } @@ -123,7 +123,7 @@ void SitesLogic::onPushButtonSitesDeleteClicked(int row) uiLogic()->m_vpnConnection->flushDns(); } - updateSitesPage(); + onUpdatePage(); } void SitesLogic::onPushButtonSitesImportClicked(const QString& fileName) @@ -153,6 +153,6 @@ void SitesLogic::onPushButtonSitesImportClicked(const QString& fileName) uiLogic()->m_vpnConnection->addRoutes(QStringList() << ips); uiLogic()->m_vpnConnection->flushDns(); - updateSitesPage(); + onUpdatePage(); } diff --git a/client/ui/pages_logic/SitesLogic.h b/client/ui/pages_logic/SitesLogic.h index a3e19984..fdc5ec82 100644 --- a/client/ui/pages_logic/SitesLogic.h +++ b/client/ui/pages_logic/SitesLogic.h @@ -15,7 +15,7 @@ class SitesLogic : public PageLogicBase AUTO_PROPERTY(QString, lineEditSitesAddCustomText) public: - Q_INVOKABLE void updateSitesPage(); + Q_INVOKABLE void onUpdatePage() override; Q_INVOKABLE void onPushButtonAddCustomSitesClicked(); Q_INVOKABLE void onPushButtonSitesDeleteClicked(int row); diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 394f70c7..a7ec7e49 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -35,6 +35,8 @@ void StartPageLogic::onUpdatePage() set_pushButtonConnectVisible(true); set_pushButtonConnectKeyChecked(false); + + set_pushButtonBackFromStartVisible(uiLogic()->pagesStackDepth() > 0); } void StartPageLogic::onPushButtonConnect() diff --git a/client/ui/pages_logic/VpnLogic.cpp b/client/ui/pages_logic/VpnLogic.cpp index cc7884fc..dd0b10f3 100644 --- a/client/ui/pages_logic/VpnLogic.cpp +++ b/client/ui/pages_logic/VpnLogic.cpp @@ -24,6 +24,9 @@ VpnLogic::VpnLogic(UiLogic *logic, QObject *parent): connect(uiLogic()->m_vpnConnection, &VpnConnection::connectionStateChanged, this, &VpnLogic::onConnectionStateChanged); connect(uiLogic()->m_vpnConnection, &VpnConnection::vpnProtocolError, this, &VpnLogic::onVpnProtocolError); + 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) { QTimer::singleShot(1000, this, [this](){ set_pushButtonConnectEnabled(false); @@ -33,13 +36,22 @@ VpnLogic::VpnLogic(UiLogic *logic, QObject *parent): } -void VpnLogic::updateVpnPage() +void VpnLogic::onUpdatePage() { Settings::RouteMode mode = m_settings.routeMode(); set_radioButtonVpnModeAllSitesChecked(mode == Settings::VpnAllSites); set_radioButtonVpnModeForwardSitesChecked(mode == Settings::VpnOnlyForwardSites); set_radioButtonVpnModeExceptSitesChecked(mode == Settings::VpnAllExceptSites); set_pushButtonVpnAddSiteEnabled(mode != Settings::VpnAllSites); + + 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()); + set_labelCurrentServer(serverString); + + QString selectedContainerName = m_settings.defaultContainerName(m_settings.defaultServerIndex()); + set_labelCurrentService(selectedContainerName); } @@ -76,6 +88,7 @@ void VpnLogic::onConnectionStateChanged(VpnProtocol::ConnectionState state) bool pbConnectEnabled = false; bool rbModeEnabled = false; + bool pbConnectVisible = false; set_labelStateText(VpnProtocol::textConnectionState(state)); uiLogic()->setTrayState(state); @@ -85,39 +98,48 @@ void VpnLogic::onConnectionStateChanged(VpnProtocol::ConnectionState state) onBytesChanged(0,0); set_pushButtonConnectChecked(false); pbConnectEnabled = true; + pbConnectVisible = true; rbModeEnabled = true; break; case VpnProtocol::Preparing: pbConnectEnabled = false; + pbConnectVisible = false; rbModeEnabled = false; break; case VpnProtocol::Connecting: pbConnectEnabled = false; + pbConnectVisible = false; rbModeEnabled = false; break; case VpnProtocol::Connected: pbConnectEnabled = true; + pbConnectVisible = true; rbModeEnabled = false; break; case VpnProtocol::Disconnecting: pbConnectEnabled = false; + pbConnectVisible = false; rbModeEnabled = false; break; case VpnProtocol::Reconnecting: pbConnectEnabled = true; + pbConnectVisible = false; rbModeEnabled = false; break; case VpnProtocol::Error: set_pushButtonConnectEnabled(false); pbConnectEnabled = true; + pbConnectVisible = true; rbModeEnabled = true; break; case VpnProtocol::Unknown: pbConnectEnabled = true; + pbConnectVisible = true; rbModeEnabled = true; } set_pushButtonConnectEnabled(pbConnectEnabled); + set_pushButtonConnectVisible(pbConnectVisible); set_widgetVpnModeEnabled(rbModeEnabled); } @@ -166,20 +188,19 @@ void VpnLogic::onConnectWorker(int serverIndex, const ServerCredentials &credent qApp->processEvents(); - ErrorCode errorCode = uiLogic()->m_vpnConnection->connectToVpn( - serverIndex, credentials, container, containerConfig); + emit connectToVpn(serverIndex, credentials, container, containerConfig); - if (errorCode) { - //ui->pushButton_connect->setChecked(false); - uiLogic()->setDialogConnectErrorText(errorString(errorCode)); - emit uiLogic()->showConnectErrorDialog(); - return; - } +// if (errorCode) { +// //ui->pushButton_connect->setChecked(false); +// uiLogic()->setDialogConnectErrorText(errorString(errorCode)); +// emit uiLogic()->showConnectErrorDialog(); +// return; +// } } void VpnLogic::onDisconnect() { set_pushButtonConnectChecked(false); - uiLogic()->m_vpnConnection->disconnectFromVpn(); + emit disconnectFromVpn(); } diff --git a/client/ui/pages_logic/VpnLogic.h b/client/ui/pages_logic/VpnLogic.h index cda57f6d..e3cce6a4 100644 --- a/client/ui/pages_logic/VpnLogic.h +++ b/client/ui/pages_logic/VpnLogic.h @@ -14,7 +14,10 @@ class VpnLogic : public PageLogicBase AUTO_PROPERTY(QString, labelSpeedReceivedText) AUTO_PROPERTY(QString, labelSpeedSentText) AUTO_PROPERTY(QString, labelStateText) + AUTO_PROPERTY(QString, labelCurrentServer) + AUTO_PROPERTY(QString, labelCurrentService) AUTO_PROPERTY(bool, pushButtonConnectEnabled) + AUTO_PROPERTY(bool, pushButtonConnectVisible) AUTO_PROPERTY(bool, widgetVpnModeEnabled) AUTO_PROPERTY(QString, labelErrorText) AUTO_PROPERTY(bool, pushButtonVpnAddSiteEnabled) @@ -24,7 +27,7 @@ class VpnLogic : public PageLogicBase AUTO_PROPERTY(bool, radioButtonVpnModeExceptSitesChecked) public: - Q_INVOKABLE void updateVpnPage(); + Q_INVOKABLE void onUpdatePage() override; Q_INVOKABLE void onRadioButtonVpnModeAllSitesToggled(bool checked); Q_INVOKABLE void onRadioButtonVpnModeForwardSitesToggled(bool checked); @@ -48,5 +51,10 @@ public slots: void onConnectionStateChanged(VpnProtocol::ConnectionState state); void onVpnProtocolError(amnezia::ErrorCode errorCode); +signals: + void connectToVpn(int serverIndex, + const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig); + + void disconnectFromVpn(); }; #endif // VPN_LOGIC_H diff --git a/client/ui/pages_logic/WizardLogic.cpp b/client/ui/pages_logic/WizardLogic.cpp index aeffc21b..af245c1e 100644 --- a/client/ui/pages_logic/WizardLogic.cpp +++ b/client/ui/pages_logic/WizardLogic.cpp @@ -15,6 +15,7 @@ WizardLogic::WizardLogic(UiLogic *logic, QObject *parent): void WizardLogic::onUpdatePage() { set_lineEditHighWebsiteMaskingText(protocols::cloak::defaultRedirSite); + set_radioButtonMediumChecked(true); } QMap WizardLogic::getInstallConfigsFromWizardPage() const diff --git a/client/ui/pages_logic/protocols/CloakLogic.cpp b/client/ui/pages_logic/protocols/CloakLogic.cpp index 73632892..22d86efb 100644 --- a/client/ui/pages_logic/protocols/CloakLogic.cpp +++ b/client/ui/pages_logic/protocols/CloakLogic.cpp @@ -8,17 +8,17 @@ using namespace PageEnumNS; CloakLogic::CloakLogic(UiLogic *logic, QObject *parent): PageProtocolLogicBase(logic, parent), - m_comboBoxProtoCloakCipherText{"chacha20-poly1305"}, - m_lineEditProtoCloakSiteText{"tile.openstreetmap.org"}, - m_lineEditProtoCloakPortText{}, - m_pushButtonCloakSaveVisible{false}, - m_progressBarProtoCloakResetVisible{false}, - m_lineEditProtoCloakPortEnabled{false}, - m_pageProtoCloakEnabled{true}, - m_labelProtoCloakInfoVisible{true}, - m_labelProtoCloakInfoText{}, - m_progressBarProtoCloakResetValue{0}, - m_progressBarProtoCloakResetMaximium{100} + m_comboBoxCipherText{"chacha20-poly1305"}, + m_lineEditSiteText{"tile.openstreetmap.org"}, + m_lineEditPortText{}, + m_pushButtonSaveVisible{false}, + m_progressBarResetVisible{false}, + m_lineEditPortEnabled{false}, + m_pageEnabled{true}, + m_labelInfoVisible{true}, + m_labelInfoText{}, + m_progressBarResetValue{0}, + m_progressBarResetMaximium{100} { } @@ -26,31 +26,31 @@ CloakLogic::CloakLogic(UiLogic *logic, QObject *parent): void CloakLogic::updateProtocolPage(const QJsonObject &ckConfig, DockerContainer container, bool haveAuthData) { set_pageEnabled(haveAuthData); - set_pushButtonCloakSaveVisible(haveAuthData); - set_progressBarProtoCloakResetVisible(haveAuthData); + set_pushButtonSaveVisible(haveAuthData); + set_progressBarResetVisible(haveAuthData); - set_comboBoxProtoCloakCipherText(ckConfig.value(config_key::cipher). + set_comboBoxCipherText(ckConfig.value(config_key::cipher). toString(protocols::cloak::defaultCipher)); - set_lineEditProtoCloakSiteText(ckConfig.value(config_key::site). + set_lineEditSiteText(ckConfig.value(config_key::site). toString(protocols::cloak::defaultRedirSite)); - set_lineEditProtoCloakPortText(ckConfig.value(config_key::port). + set_lineEditPortText(ckConfig.value(config_key::port). toString(protocols::cloak::defaultPort)); - set_lineEditProtoCloakPortEnabled(container == DockerContainer::Cloak); + set_lineEditPortEnabled(container == DockerContainer::Cloak); } QJsonObject CloakLogic::getProtocolConfigFromPage(QJsonObject oldConfig) { - oldConfig.insert(config_key::cipher, comboBoxProtoCloakCipherText()); - oldConfig.insert(config_key::site, lineEditProtoCloakSiteText()); - oldConfig.insert(config_key::port, lineEditProtoCloakPortText()); + oldConfig.insert(config_key::cipher, comboBoxCipherText()); + oldConfig.insert(config_key::site, lineEditSiteText()); + oldConfig.insert(config_key::port, lineEditPortText()); return oldConfig; } -void CloakLogic::onPushButtonProtoCloakSaveClicked() +void CloakLogic::onPushButtonSaveClicked() { QJsonObject protocolConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Protocol::Cloak); protocolConfig = getProtocolConfigFromPage(protocolConfig); @@ -59,40 +59,40 @@ void CloakLogic::onPushButtonProtoCloakSaveClicked() QJsonObject newContainerConfig = containerConfig; newContainerConfig.insert(ProtocolProps::protoToString(Protocol::Cloak), protocolConfig); - UiLogic::PageFunc page_proto_cloak; - page_proto_cloak.setEnabledFunc = [this] (bool enabled) -> void { - set_pageProtoCloakEnabled(enabled); + UiLogic::PageFunc page_func; + page_func.setEnabledFunc = [this] (bool enabled) -> void { + set_pageEnabled(enabled); }; - UiLogic::ButtonFunc pushButton_proto_cloak_save; - pushButton_proto_cloak_save.setVisibleFunc = [this] (bool visible) ->void { - set_pushButtonCloakSaveVisible(visible); + UiLogic::ButtonFunc pushButton_save_func; + pushButton_save_func.setVisibleFunc = [this] (bool visible) ->void { + set_pushButtonSaveVisible(visible); }; - UiLogic::LabelFunc label_proto_cloak_info; - label_proto_cloak_info.setVisibleFunc = [this] (bool visible) ->void { - set_labelProtoCloakInfoVisible(visible); + UiLogic::LabelFunc label_info_func; + label_info_func.setVisibleFunc = [this] (bool visible) ->void { + set_labelInfoVisible(visible); }; - label_proto_cloak_info.setTextFunc = [this] (const QString& text) ->void { - set_labelProtoCloakInfoText(text); + label_info_func.setTextFunc = [this] (const QString& text) ->void { + set_labelInfoText(text); }; - UiLogic::ProgressFunc progressBar_proto_cloak_reset; - progressBar_proto_cloak_reset.setVisibleFunc = [this] (bool visible) ->void { - set_progressBarProtoCloakResetVisible(visible); + UiLogic::ProgressFunc progressBar_reset; + progressBar_reset.setVisibleFunc = [this] (bool visible) ->void { + set_progressBarResetVisible(visible); }; - progressBar_proto_cloak_reset.setValueFunc = [this] (int value) ->void { - set_progressBarProtoCloakResetValue(value); + progressBar_reset.setValueFunc = [this] (int value) ->void { + set_progressBarResetValue(value); }; - progressBar_proto_cloak_reset.getValueFunc = [this] (void) -> int { - return progressBarProtoCloakResetValue(); + progressBar_reset.getValueFunc = [this] (void) -> int { + return progressBarResetValue(); }; - progressBar_proto_cloak_reset.getMaximiumFunc = [this] (void) -> int { - return progressBarProtoCloakResetMaximium(); + progressBar_reset.getMaximiumFunc = [this] (void) -> int { + return progressBarResetMaximium(); }; ErrorCode e = uiLogic()->doInstallAction([this, containerConfig, &newContainerConfig](){ return ServerController::updateContainer(m_settings.serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); }, - page_proto_cloak, progressBar_proto_cloak_reset, - pushButton_proto_cloak_save, label_proto_cloak_info); + page_func, progressBar_reset, + pushButton_save_func, label_info_func); if (!e) { m_settings.setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig); diff --git a/client/ui/pages_logic/protocols/CloakLogic.h b/client/ui/pages_logic/protocols/CloakLogic.h index b5c752a4..e110c465 100644 --- a/client/ui/pages_logic/protocols/CloakLogic.h +++ b/client/ui/pages_logic/protocols/CloakLogic.h @@ -9,20 +9,20 @@ class CloakLogic : public PageProtocolLogicBase { Q_OBJECT - AUTO_PROPERTY(QString, comboBoxProtoCloakCipherText) - AUTO_PROPERTY(QString, lineEditProtoCloakSiteText) - AUTO_PROPERTY(QString, lineEditProtoCloakPortText) - AUTO_PROPERTY(bool, pushButtonCloakSaveVisible) - AUTO_PROPERTY(bool, progressBarProtoCloakResetVisible) - AUTO_PROPERTY(bool, lineEditProtoCloakPortEnabled) - AUTO_PROPERTY(bool, pageProtoCloakEnabled) - AUTO_PROPERTY(bool, labelProtoCloakInfoVisible) - AUTO_PROPERTY(QString, labelProtoCloakInfoText) - AUTO_PROPERTY(int, progressBarProtoCloakResetValue) - AUTO_PROPERTY(int, progressBarProtoCloakResetMaximium) + AUTO_PROPERTY(QString, comboBoxCipherText) + AUTO_PROPERTY(QString, lineEditSiteText) + AUTO_PROPERTY(QString, lineEditPortText) + AUTO_PROPERTY(bool, pushButtonSaveVisible) + AUTO_PROPERTY(bool, progressBarResetVisible) + AUTO_PROPERTY(bool, lineEditPortEnabled) + AUTO_PROPERTY(bool, pageEnabled) + AUTO_PROPERTY(bool, labelInfoVisible) + AUTO_PROPERTY(QString, labelInfoText) + AUTO_PROPERTY(int, progressBarResetValue) + AUTO_PROPERTY(int, progressBarResetMaximium) public: - Q_INVOKABLE void onPushButtonProtoCloakSaveClicked(); + Q_INVOKABLE void onPushButtonSaveClicked(); public: explicit CloakLogic(UiLogic *uiLogic, QObject *parent = nullptr); diff --git a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp index c00d8ae9..a19e8a2f 100644 --- a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp +++ b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp @@ -8,16 +8,15 @@ using namespace PageEnumNS; ShadowSocksLogic::ShadowSocksLogic(UiLogic *logic, QObject *parent): PageProtocolLogicBase(logic, parent), - m_comboBoxProtoShadowSocksCipherText{"chacha20-poly1305"}, - m_lineEditProtoShadowSocksPortText{}, - m_pushButtonShadowSocksSaveVisible{false}, - m_progressBarProtoShadowSocksResetVisible{false}, - m_lineEditProtoShadowSocksPortEnabled{false}, - m_pageProtoShadowSocksEnabled{true}, - m_labelProtoShadowSocksInfoVisible{true}, - m_labelProtoShadowSocksInfoText{}, - m_progressBarProtoShadowSocksResetValue{0}, - m_progressBarProtoShadowSocksResetMaximium{100} + m_comboBoxCipherText{"chacha20-poly1305"}, + m_lineEditPortText{}, + m_pushButtonSaveVisible{false}, + m_progressBaResetVisible{false}, + m_lineEditPortEnabled{false}, + m_labelInfoVisible{true}, + m_labelInfoText{}, + m_progressBaResetValue{0}, + m_progressBaResetMaximium{100} { } @@ -25,27 +24,27 @@ ShadowSocksLogic::ShadowSocksLogic(UiLogic *logic, QObject *parent): void ShadowSocksLogic::updateProtocolPage(const QJsonObject &ssConfig, DockerContainer container, bool haveAuthData) { set_pageEnabled(haveAuthData); - set_pushButtonShadowSocksSaveVisible(haveAuthData); - set_progressBarProtoShadowSocksResetVisible(haveAuthData); + set_pushButtonSaveVisible(haveAuthData); + set_progressBaResetVisible(haveAuthData); - set_comboBoxProtoShadowSocksCipherText(ssConfig.value(config_key::cipher). + set_comboBoxCipherText(ssConfig.value(config_key::cipher). toString(protocols::shadowsocks::defaultCipher)); - set_lineEditProtoShadowSocksPortText(ssConfig.value(config_key::port). + set_lineEditPortText(ssConfig.value(config_key::port). toString(protocols::shadowsocks::defaultPort)); - set_lineEditProtoShadowSocksPortEnabled(container == DockerContainer::ShadowSocks); + set_lineEditPortEnabled(container == DockerContainer::ShadowSocks); } QJsonObject ShadowSocksLogic::getProtocolConfigFromPage(QJsonObject oldConfig) { - oldConfig.insert(config_key::cipher, comboBoxProtoShadowSocksCipherText()); - oldConfig.insert(config_key::port, lineEditProtoShadowSocksPortText()); + oldConfig.insert(config_key::cipher, comboBoxCipherText()); + oldConfig.insert(config_key::port, lineEditPortText()); return oldConfig; } -void ShadowSocksLogic::onPushButtonProtoShadowSocksSaveClicked() +void ShadowSocksLogic::onPushButtonSaveClicked() { QJsonObject protocolConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Protocol::ShadowSocks); //protocolConfig = getShadowSocksConfigFromPage(protocolConfig); @@ -55,37 +54,37 @@ void ShadowSocksLogic::onPushButtonProtoShadowSocksSaveClicked() newContainerConfig.insert(ProtocolProps::protoToString(Protocol::ShadowSocks), protocolConfig); UiLogic::PageFunc page_proto_shadowsocks; page_proto_shadowsocks.setEnabledFunc = [this] (bool enabled) -> void { - set_pageProtoShadowSocksEnabled(enabled); + set_pageEnabled(enabled); }; UiLogic::ButtonFunc pushButton_proto_shadowsocks_save; pushButton_proto_shadowsocks_save.setVisibleFunc = [this] (bool visible) ->void { - set_pushButtonShadowSocksSaveVisible(visible); + set_pushButtonSaveVisible(visible); }; UiLogic::LabelFunc label_proto_shadowsocks_info; label_proto_shadowsocks_info.setVisibleFunc = [this] (bool visible) ->void { - set_labelProtoShadowSocksInfoVisible(visible); + set_labelInfoVisible(visible); }; label_proto_shadowsocks_info.setTextFunc = [this] (const QString& text) ->void { - set_labelProtoShadowSocksInfoText(text); + set_labelInfoText(text); }; - UiLogic::ProgressFunc progressBar_proto_shadowsocks_reset; - progressBar_proto_shadowsocks_reset.setVisibleFunc = [this] (bool visible) ->void { - set_progressBarProtoShadowSocksResetVisible(visible); + UiLogic::ProgressFunc progressBar_reset; + progressBar_reset.setVisibleFunc = [this] (bool visible) ->void { + set_progressBaResetVisible(visible); }; - progressBar_proto_shadowsocks_reset.setValueFunc = [this] (int value) ->void { - set_progressBarProtoShadowSocksResetValue(value); + progressBar_reset.setValueFunc = [this] (int value) ->void { + set_progressBaResetValue(value); }; - progressBar_proto_shadowsocks_reset.getValueFunc = [this] (void) -> int { - return progressBarProtoShadowSocksResetValue(); + progressBar_reset.getValueFunc = [this] (void) -> int { + return progressBaResetValue(); }; - progressBar_proto_shadowsocks_reset.getMaximiumFunc = [this] (void) -> int { - return progressBarProtoShadowSocksResetMaximium(); + progressBar_reset.getMaximiumFunc = [this] (void) -> int { + return progressBaResetMaximium(); }; ErrorCode e = uiLogic()->doInstallAction([this, containerConfig, &newContainerConfig](){ return ServerController::updateContainer(m_settings.serverCredentials(uiLogic()->selectedServerIndex), uiLogic()->selectedDockerContainer, containerConfig, newContainerConfig); }, - page_proto_shadowsocks, progressBar_proto_shadowsocks_reset, + page_proto_shadowsocks, progressBar_reset, pushButton_proto_shadowsocks_save, label_proto_shadowsocks_info); if (!e) { diff --git a/client/ui/pages_logic/protocols/ShadowSocksLogic.h b/client/ui/pages_logic/protocols/ShadowSocksLogic.h index 392831ab..b7c2c9ee 100644 --- a/client/ui/pages_logic/protocols/ShadowSocksLogic.h +++ b/client/ui/pages_logic/protocols/ShadowSocksLogic.h @@ -9,19 +9,18 @@ class ShadowSocksLogic : public PageProtocolLogicBase { Q_OBJECT - AUTO_PROPERTY(QString, comboBoxProtoShadowSocksCipherText) - AUTO_PROPERTY(QString, lineEditProtoShadowSocksPortText) - AUTO_PROPERTY(bool, pushButtonShadowSocksSaveVisible) - AUTO_PROPERTY(bool, progressBarProtoShadowSocksResetVisible) - AUTO_PROPERTY(bool, lineEditProtoShadowSocksPortEnabled) - AUTO_PROPERTY(bool, pageProtoShadowSocksEnabled) - AUTO_PROPERTY(bool, labelProtoShadowSocksInfoVisible) - AUTO_PROPERTY(QString, labelProtoShadowSocksInfoText) - AUTO_PROPERTY(int, progressBarProtoShadowSocksResetValue) - AUTO_PROPERTY(int, progressBarProtoShadowSocksResetMaximium) + AUTO_PROPERTY(QString, comboBoxCipherText) + AUTO_PROPERTY(QString, lineEditPortText) + AUTO_PROPERTY(bool, pushButtonSaveVisible) + AUTO_PROPERTY(bool, progressBaResetVisible) + AUTO_PROPERTY(bool, lineEditPortEnabled) + AUTO_PROPERTY(bool, labelInfoVisible) + AUTO_PROPERTY(QString, labelInfoText) + AUTO_PROPERTY(int, progressBaResetValue) + AUTO_PROPERTY(int, progressBaResetMaximium) public: - Q_INVOKABLE void onPushButtonProtoShadowSocksSaveClicked(); + Q_INVOKABLE void onPushButtonSaveClicked(); public: explicit ShadowSocksLogic(UiLogic *uiLogic, QObject *parent = nullptr); diff --git a/client/ui/qml/Controls/BlueButtonType.qml b/client/ui/qml/Controls/BlueButtonType.qml index 147a6d8b..c19f1256 100644 --- a/client/ui/qml/Controls/BlueButtonType.qml +++ b/client/ui/qml/Controls/BlueButtonType.qml @@ -6,8 +6,6 @@ BasicButtonType { id: root width: parent.width - 80 height: 40 - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: 20 background: Rectangle { anchors.fill: parent diff --git a/client/ui/qml/Controls/ContextMenu.qml b/client/ui/qml/Controls/ContextMenu.qml new file mode 100644 index 00000000..a0d0202d --- /dev/null +++ b/client/ui/qml/Controls/ContextMenu.qml @@ -0,0 +1,33 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import Qt.labs.platform 1.0 + +Menu { + property var textObj + + MenuItem { + text: qsTr("C&ut") + shortcut: StandardKey.Cut + enabled: textObj.selectedText + onTriggered: textObj.cut() + } + MenuItem { + text: qsTr("&Copy") + shortcut: StandardKey.Copy + enabled: textObj.selectedText + onTriggered: textObj.copy() + } + MenuItem { + text: qsTr("&Paste") + shortcut: StandardKey.Paste + enabled: textObj.canPaste + onTriggered: textObj.paste() + } + + MenuItem { + text: qsTr("&SelectAll") + shortcut: StandardKey.SelectAll + enabled: textObj.length > 0 + onTriggered: textObj.selectAll() + } +} diff --git a/client/ui/qml/Controls/ShareConnectionButtonCopyType.qml b/client/ui/qml/Controls/ShareConnectionButtonCopyType.qml new file mode 100644 index 00000000..eccf1bb5 --- /dev/null +++ b/client/ui/qml/Controls/ShareConnectionButtonCopyType.qml @@ -0,0 +1,26 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +ShareConnectionButtonType { + property string start_text: qsTr("Copy") + property string end_text: qsTr("Copied") + + property string copyText + + enabled: copyText.length > 0 + visible: copyText.length > 0 + + Timer { + id: timer + interval: 1000; running: false; repeat: false + onTriggered: text = start_text + } + + text: start_text + + onClicked: { + text = end_text + timer.running = true + UiLogic.copyToClipboard(copyText) + } +} diff --git a/client/ui/qml/Controls/ShareConnectionButtonType.qml b/client/ui/qml/Controls/ShareConnectionButtonType.qml index a45e5fee..5609a29d 100644 --- a/client/ui/qml/Controls/ShareConnectionButtonType.qml +++ b/client/ui/qml/Controls/ShareConnectionButtonType.qml @@ -4,10 +4,13 @@ import QtQuick.Controls 2.12 BasicButtonType { id: root + height: 40 background: Rectangle { anchors.fill: parent radius: 4 - color: root.containsMouse ? "#282932" : "#181922" + color: root.enabled + ? (root.containsMouse ? "#282932" : "#181922") + : "#484952" } font.pixelSize: 16 contentItem: Text { diff --git a/client/ui/qml/Controls/ShareConnectionContent.qml b/client/ui/qml/Controls/ShareConnectionContent.qml index df126db1..22b274b0 100644 --- a/client/ui/qml/Controls/ShareConnectionContent.qml +++ b/client/ui/qml/Controls/ShareConnectionContent.qml @@ -5,9 +5,7 @@ import QtGraphicalEffects 1.12 Item { id: root property bool active: false - property Component content: undefined property string text: "" - width: 360 height: active ? contentLoader.item.height + 40 + 5 * 2 : 40 signal clicked() @@ -64,12 +62,5 @@ Item { onClicked: root.clicked() } } - Loader { - x: 0 - y: 40 + 5 - id: contentLoader - sourceComponent: root.content - visible: root.active - } } diff --git a/client/ui/qml/Controls/TextAreaType.qml b/client/ui/qml/Controls/TextAreaType.qml new file mode 100644 index 00000000..5e83e787 --- /dev/null +++ b/client/ui/qml/Controls/TextAreaType.qml @@ -0,0 +1,63 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import Qt.labs.platform 1.0 + +import "../Config" + +Flickable +{ + property alias textArea: root + id: flickable + flickableDirection: Flickable.VerticalFlick + clip: true + TextArea.flickable: + + TextArea { + id: root + property bool error: false + + height: 40 + anchors.topMargin: 5 + selectByMouse: false + + selectionColor: "darkgray" + font.pixelSize: 16 + color: "#333333" + background: Rectangle { + implicitWidth: 200 + implicitHeight: 40 + border.width: 1 + color: { + if (root.error) { + return Qt.rgba(213, 40, 60, 255) + } + return root.enabled ? "#F4F4F4" : Qt.rgba(127, 127, 127, 255) + } + border.color: { + if (!root.enabled) { + return Qt.rgba(127, 127, 127, 255) + } + if (root.error) { + return Qt.rgba(213, 40, 60, 255) + } + if (root.focus) { + return "#A7A7A7" + } + return "#A7A7A7" + } + } + +// MouseArea { +// anchors.fill: root +// enabled: GC.isDesktop() +// acceptedButtons: Qt.RightButton +// onClicked: contextMenu.open() +// } + +// ContextMenu { +// id: contextMenu +// textObj: root +// } + } + +} diff --git a/client/ui/qml/Controls/TextFieldType.qml b/client/ui/qml/Controls/TextFieldType.qml index a809a3d5..836a233c 100644 --- a/client/ui/qml/Controls/TextFieldType.qml +++ b/client/ui/qml/Controls/TextFieldType.qml @@ -44,29 +44,8 @@ TextField { onClicked: contextMenu.open() } - Menu { + ContextMenu { id: contextMenu - - onAboutToShow: console.log("aboutToShow") - onAboutToHide: console.log("aboutToHide") - - MenuItem { - text: qsTr("C&ut") - shortcut: StandardKey.Cut - enabled: root.selectedText - onTriggered: root.cut() - } - MenuItem { - text: qsTr("&Copy") - shortcut: StandardKey.Copy - enabled: root.selectedText - onTriggered: root.copy() - } - MenuItem { - text: qsTr("&Paste") - shortcut: StandardKey.Paste - enabled: root.canPaste - onTriggered: root.paste() - } + textObj: root } } diff --git a/client/ui/qml/Pages/InstallSettings/SelectContainer.qml b/client/ui/qml/Pages/InstallSettings/SelectContainer.qml index a004ade1..83efaec1 100644 --- a/client/ui/qml/Pages/InstallSettings/SelectContainer.qml +++ b/client/ui/qml/Pages/InstallSettings/SelectContainer.qml @@ -20,7 +20,7 @@ Drawer { height: parent.height modal: true - interactive: true + interactive: activeFocus SortFilterProxyModel { id: proxyModel diff --git a/client/ui/qml/Pages/PageGeneralSettings.qml b/client/ui/qml/Pages/PageGeneralSettings.qml index 4b54ba9c..0da29d00 100644 --- a/client/ui/qml/Pages/PageGeneralSettings.qml +++ b/client/ui/qml/Pages/PageGeneralSettings.qml @@ -13,82 +13,49 @@ PageBase { BackButton { id: back } + + // ---------- App settings ------------ Rectangle { - y: 40 + id: l1 + visible: !GC.isMobile() + anchors.top: back.bottom x: 20 width: parent.width - 40 - height: 1 - color: "#DDDDDD" - } - Rectangle { - y: 100 - x: 20 - width: parent.width - 40 - height: 1 - color: "#DDDDDD" - } - Rectangle { - y: 160 - x: 20 - width: parent.width - 40 - height: 1 - color: "#DDDDDD" - } - Rectangle { - y: 220 - x: 20 - width: parent.width - 40 - height: 1 - color: "#DDDDDD" - } - Rectangle { - y: 280 - x: 20 - width: parent.width - 40 - height: 1 - color: "#DDDDDD" - } - Rectangle { - y: 340 - x: 20 - width: parent.width - 40 - height: 1 - color: "#DDDDDD" - } - Rectangle { - y: 400 - x: 20 - width: parent.width - 40 - height: 1 + height: GC.isMobile() ? 0: 1 color: "#DDDDDD" } SettingButtonType { + id: b1 + visible: !GC.isMobile() + anchors.top: l1.bottom + anchors.topMargin: GC.isMobile() ? 0: 15 x: 30 - y: 355 - width: 330 - height: 30 - icon.source: "qrc:/images/plus.png" - text: qsTr("Add server") - onClicked: { - UiLogic.goToPage(PageEnum.Start) - } - } - SettingButtonType { - x: 30 - y: 55 - width: 330 - height: 30 + width: parent.width - 40 + height: GC.isMobile() ? 0: 30 icon.source: "qrc:/images/settings.png" text: qsTr("App settings") onClicked: { UiLogic.goToPage(PageEnum.AppSettings) } } + + // ---------- Network settings ------------ + Rectangle { + id: l2 + anchors.top: b1.bottom + anchors.topMargin: 15 + x: 20 + width: parent.width - 40 + height: 1 + color: "#DDDDDD" + } SettingButtonType { + id: b2 x: 30 - y: 115 - width: 330 + anchors.top: l2.bottom + anchors.topMargin: 15 + width: parent.width - 40 height: 30 icon.source: "qrc:/images/settings.png" text: qsTr("Network settings") @@ -96,31 +63,46 @@ PageBase { UiLogic.goToPage(PageEnum.NetworkSettings) } } + + // ---------- Server settings ------------ + Rectangle { + id: l3 + anchors.top: b2.bottom + anchors.topMargin: 15 + x: 20 + width: parent.width - 40 + height: 1 + color: "#DDDDDD" + } SettingButtonType { + id: b3 x: 30 - y: 175 + anchors.top: l3.bottom + anchors.topMargin: 15 width: 330 height: 30 icon.source: "qrc:/images/server_settings.png" - text: qsTr("Server management") + text: qsTr("Server Settings") onClicked: { GeneralSettingsLogic.onPushButtonGeneralSettingsServerSettingsClicked() } } - SettingButtonType { - x: 30 - y: 295 - width: 330 - height: 30 - icon.source: "qrc:/images/server_settings.png" - text: qsTr("Servers") - onClicked: { - UiLogic.goToPage(PageEnum.ServersList) - } + + // ---------- Share connection ------------ + Rectangle { + id: l4 + anchors.top: b3.bottom + anchors.topMargin: 15 + x: 20 + width: parent.width - 40 + height: 1 + color: "#DDDDDD" } SettingButtonType { + id: b4 x: 30 - y: 235 + anchors.top: l4.bottom + anchors.topMargin: 15 width: 330 height: 30 icon.source: "qrc:/images/share.png" @@ -130,6 +112,66 @@ PageBase { GeneralSettingsLogic.onPushButtonGeneralSettingsShareConnectionClicked() } } + + // ---------- Servers ------------ + Rectangle { + id: l5 + anchors.top: b4.bottom + anchors.topMargin: 15 + x: 20 + width: parent.width - 40 + height: 1 + color: "#DDDDDD" + } + SettingButtonType { + id: b5 + x: 30 + anchors.top: l5.bottom + anchors.topMargin: 15 + width: 330 + height: 30 + icon.source: "qrc:/images/server_settings.png" + text: qsTr("Servers") + onClicked: { + UiLogic.goToPage(PageEnum.ServersList) + } + } + + // ---------- Add server ------------ + Rectangle { + id: l6 + anchors.top: b5.bottom + anchors.topMargin: 15 + x: 20 + width: parent.width - 40 + height: 1 + color: "#DDDDDD" + } + SettingButtonType { + id: b6 + x: 30 + anchors.top: l6.bottom + anchors.topMargin: 15 + width: 330 + height: 30 + icon.source: "qrc:/images/plus.png" + text: qsTr("Add server") + onClicked: { + UiLogic.goToPage(PageEnum.Start) + } + } + + Rectangle { + id: l7 + anchors.top: b6.bottom + anchors.topMargin: 15 + x: 20 + width: parent.width - 40 + height: 1 + color: "#DDDDDD" + } + + SettingButtonType { x: 30 anchors.bottom: parent.bottom diff --git a/client/ui/qml/Pages/PageNewServer.qml b/client/ui/qml/Pages/PageNewServer.qml index 32e38b43..fad2d14d 100644 --- a/client/ui/qml/Pages/PageNewServer.qml +++ b/client/ui/qml/Pages/PageNewServer.qml @@ -30,6 +30,7 @@ PageBase { id: pushButtonWizard text: qsTr("Run Setup Wizard") anchors.top: labelWizard.bottom + anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: 10 onClicked: { UiLogic.goToPage(PageEnum.Wizard); diff --git a/client/ui/qml/Pages/PageNewServerProtocols.qml b/client/ui/qml/Pages/PageNewServerProtocols.qml index e50206f7..828d05ce 100644 --- a/client/ui/qml/Pages/PageNewServerProtocols.qml +++ b/client/ui/qml/Pages/PageNewServerProtocols.qml @@ -147,172 +147,4 @@ PageBase { } } } - - - - - - - - - -// ScrollView { -// id: scrollView -// width: parent.width - 40 -// anchors.horizontalCenter: parent.horizontalCenter - -// anchors.top: pb_add_container.bottom -// anchors.topMargin: 10 - -// anchors.bottom: pushButtonConfigure.top -// anchors.bottomMargin: 10 - -// clip: true -// Column { -// width: scrollView.width -// anchors.horizontalCenter: parent.horizontalCenter - -// spacing: 5 -// InstallSettingsBase { -// containerDescription: qsTr("OpenVPN and ShadowSocks\n with masking using Cloak plugin") -// onContainerChecked: NewServerProtocolsLogic.checkBoxCloakChecked = checked - -// LabelType { -// width: 130 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: qsTr("Port (TCP)") -// } -// TextFieldType { -// width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: NewServerProtocolsLogic.lineEditCloakPortText -// onEditingFinished: { -// NewServerProtocolsLogic.lineEditCloakPortText = text -// } -// } -// LabelType { -// width: 130 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: qsTr("Fake Web Site") -// } -// TextFieldType { -// width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: NewServerProtocolsLogic.lineEditCloakSiteText -// onEditingFinished: { -// NewServerProtocolsLogic.lineEditCloakSiteText = text -// } -// } - -// } - -// InstallSettingsBase { -// containerDescription: qsTr("ShadowSocks") -// onContainerChecked: NewServerProtocolsLogic.checkBoxSsChecked = checked - -// LabelType { -// width: 130 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: qsTr("Port (TCP)") -// } -// TextFieldType { -// width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: NewServerProtocolsLogic.lineEditSsPortText -// onEditingFinished: { -// NewServerProtocolsLogic.lineEditSsPortText = text -// } -// } -// LabelType { -// width: 130 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: qsTr("Encryption") -// } -// ComboBoxType { -// width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// model: [ -// qsTr("chacha20-ietf-poly1305"), -// qsTr("xchacha20-ietf-poly1305"), -// qsTr("aes-256-gcm"), -// qsTr("aes-192-gcm"), -// qsTr("aes-128-gcm") -// ] -// currentIndex: { -// for (let i = 0; i < model.length; ++i) { -// if (NewServerProtocolsLogic.comboBoxSsCipherText === model[i]) { -// return i -// } -// } -// return -1 -// } -// onCurrentTextChanged: { -// NewServerProtocolsLogic.comboBoxSsCipherText = currentText -// } -// } - -// } - -// InstallSettingsBase { -// containerDescription: qsTr("OpenVPN") -// onContainerChecked: NewServerProtocolsLogic.checkBoxOpenVpnChecked = checked - -// LabelType { -// width: 130 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: qsTr("Port (TCP/UDP)") -// } -// TextFieldType { -// width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: NewServerProtocolsLogic.lineEditOpenvpnPortText -// onEditingFinished: { -// NewServerProtocolsLogic.lineEditOpenvpnPortText = text -// } -// } -// LabelType { -// width: 130 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// text: qsTr("Protocol") -// } -// ComboBoxType { -// width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 -// height: (parent.height - parent.spacing - parent.topPadding * 2) / 2 -// model: [ -// qsTr("udp"), -// qsTr("tcp"), -// ] -// currentIndex: { -// for (let i = 0; i < model.length; ++i) { -// if (NewServerProtocolsLogic.comboBoxOpenvpnProtoText === model[i]) { -// return i -// } -// } -// return -1 -// } -// onCurrentTextChanged: { -// NewServerProtocolsLogic.comboBoxOpenvpnProtoText = currentText -// } -// } - -// } - -// InstallSettingsBase { -// visible: false -// containerDescription: qsTr("WireGuard") -// onContainerChecked: NewServerProtocolsLogic.checkBoxWireGuardChecked = checked - -// LabelType { -// width: 130 -// height: (parent.height - parent.spacing - parent.topPadding * 2) -// text: qsTr("Port (UDP)") -// } -// TextFieldType { -// width: parent.width - 130 - parent.spacing - parent.leftPadding * 2 -// height: (parent.height - parent.spacing - parent.topPadding * 2) -// text: "32767" -// } -// } -// } -// } } diff --git a/client/ui/qml/Pages/PageServerContainers.qml b/client/ui/qml/Pages/PageServerContainers.qml index fee61a9f..565a2488 100644 --- a/client/ui/qml/Pages/PageServerContainers.qml +++ b/client/ui/qml/Pages/PageServerContainers.qml @@ -1,5 +1,6 @@ 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 @@ -292,19 +293,30 @@ PageBase { } ImageButtonType { - id: button_default - visible: service_type_role == ProtocolEnum.Vpn - + id: button_remove + visible: index === tb_c.currentIndex Layout.alignment: Qt.AlignRight checkable: true - img.source: checked ? "qrc:/images/check.png" : "qrc:/images/uncheck.png" + icon.source: "qrc:/images/delete.png" implicitWidth: 30 implicitHeight: 30 checked: default_role - onClicked: { - ServerContainersLogic.onPushButtonDefaultClicked(proxyContainersModel.mapToSource(index)) + + 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 { @@ -322,23 +334,20 @@ PageBase { } ImageButtonType { - id: button_remove - visible: index === tb_c.currentIndex + id: button_default + visible: service_type_role == ProtocolEnum.Vpn + Layout.alignment: Qt.AlignRight checkable: true - icon.source: "qrc:/images/delete.png" + img.source: checked ? "qrc:/images/check.png" : "qrc:/images/uncheck.png" implicitWidth: 30 implicitHeight: 30 checked: default_role onClicked: { - tb_c.currentIndex = -1 - ServerContainersLogic.onPushButtonRemoveClicked(proxyContainersModel.mapToSource(index)) + ServerContainersLogic.onPushButtonDefaultClicked(proxyContainersModel.mapToSource(index)) } - - VisibleBehavior on visible { } } - } diff --git a/client/ui/qml/Pages/PageServerList.qml b/client/ui/qml/Pages/PageServerList.qml index f4ba9bd2..5a333069 100644 --- a/client/ui/qml/Pages/PageServerList.qml +++ b/client/ui/qml/Pages/PageServerList.qml @@ -49,7 +49,13 @@ PageBase { anchors.fill: parent hoverEnabled: true onClicked: { - listWidget_servers.currentIndex = index + if (GC.isMobile()) { + ServerListLogic.onServerListPushbuttonSettingsClicked(index) + } + else { + listWidget_servers.currentIndex = index + } + mouse.accepted = false } onEntered: { @@ -104,22 +110,11 @@ PageBase { wrapMode: Text.Wrap text: desc } - ImageButtonType { - x: 212 - y: 25 - width: 32 - height: 24 - checkable: true - iconMargin: 0 - icon.source: checked ? "qrc:/images/connect_button_connected.png" - : "qrc:/images/connect_button_disconnected.png" - visible: false - } ImageButtonType { x: parent.width - 30 - y: 25 - width: 24 - height: 24 + y: 15 + width: 30 + height: 30 checkable: true icon.source: checked ? "qrc:/images/check.png" : "qrc:/images/uncheck.png" @@ -131,10 +126,10 @@ PageBase { } ImageButtonType { id: pushButtonSetting - x: parent.width - 60 - y: 25 - width: 24 - height: 24 + x: parent.width - 70 + y: 15 + width: 30 + height: 30 icon.source: "qrc:/images/settings.png" opacity: 0 diff --git a/client/ui/qml/Pages/PageShareConnection.qml b/client/ui/qml/Pages/PageShareConnection.qml index f7b0c639..c124c969 100644 --- a/client/ui/qml/Pages/PageShareConnection.qml +++ b/client/ui/qml/Pages/PageShareConnection.qml @@ -1,6 +1,13 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.15 +import QtGraphicalEffects 1.12 +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" @@ -13,400 +20,69 @@ PageBase { BackButton { id: back } - ScrollView { - x: 10 - y: 40 - width: 360 - height: 580 - Item { - id: ct - width: parent.width - height: childrenRect.height + 10 - property var contentList: [ - full_access, - share_amezia, - share_openvpn, - share_shadowshock, - share_cloak - ] - property int currentIndex: ShareConnectionLogic.toolBoxShareConnectionCurrentIndex - onCurrentIndexChanged: { - ShareConnectionLogic.toolBoxShareConnectionCurrentIndex = currentIndex - for (let i = 0; i < contentList.length; ++i) { - if (i == currentIndex) { - contentList[i].active = true - } else { - contentList[i].active = false - } + + Caption { + id: caption + text: qsTr("Share protocol config") + width: undefined + } + + + Flickable { + clip: true + width: parent.width + anchors.top: caption.bottom + anchors.bottom: root.bottom + contentHeight: col.height + + Column { + id: col + anchors { + left: parent.left; + right: parent.right; + } + topPadding: 20 + spacing: 10 + + SortFilterProxyModel { + id: proxyProtocolsModel + sourceModel: UiLogic.protocolsModel + filters: ValueFilter { + roleName: "is_installed_role" + value: true } } - function clearActive() { - for (let i = 0; i < contentList.length; ++i) { - contentList[i].active = false - } - currentIndex = -1; + + ShareConnectionContent { + x: 10 + text: qsTr("Share for Amnezia") + height: 40 + width: tb_c.width - 10 + onClicked: UiLogic.onGotoShareProtocolPage(ProtocolEnum.Any) } - Column { - spacing: 5 - ShareConnectionContent { - id: full_access - x: 0 - text: qsTr("Full access") - visible: ShareConnectionLogic.pageShareFullAccessVisible - content: Component { - Item { - width: 360 - height: 380 - Text { - x: 10 - y: 250 - width: 341 - height: 111 - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 16 - color: "#181922" - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - wrapMode: Text.Wrap - text: qsTr("Anyone who logs in with this code will have the same permissions to use VPN and your server as you. \nThis code includes your server credentials!\nProvide this code only to TRUSTED users.") - } - ShareConnectionButtonType { - x: 10 - y: 130 - width: 341 - height: 40 - text: ShareConnectionLogic.pushButtonShareFullCopyText - onClicked: { - ShareConnectionLogic.onPushButtonShareFullCopyClicked() - } - } - ShareConnectionButtonType { - x: 10 - y: 180 - width: 341 - height: 40 - text: qsTr("Save file") - onClicked: { - ShareConnectionLogic.onPushButtonShareFullSaveClicked() - } - } - TextFieldType { - x: 10 - y: 10 - width: 341 - height: 100 - verticalAlignment: Text.AlignTop - text: ShareConnectionLogic.textEditShareFullCodeText - onEditingFinished: { - ShareConnectionLogic.textEditShareFullCodeText = text - } - } - } - } - onClicked: { - if (active) { - ct.currentIndex = -1 - } else { - ct.clearActive() - ct.currentIndex = 0 - } - } - } - ShareConnectionContent { - id: share_amezia - x: 0 - text: qsTr("Share for Amnezia client") - visible: ShareConnectionLogic.pageShareAmneziaVisible - content: Component { - Item { - width: 360 - height: 380 - Text { - x: 10 - y: 280 - width: 341 - height: 111 - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 16 - color: "#181922" - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - wrapMode: Text.Wrap - text: qsTr("Anyone who logs in with this code will be able to connect to this VPN server. \nThis code does not include server credentials.") - } - ShareConnectionButtonType { - x: 10 - y: 180 - width: 341 - height: 40 - text: ShareConnectionLogic.pushButtonShareAmneziaCopyText - onClicked: { - ShareConnectionLogic.onPushButtonShareAmneziaCopyClicked() - } - enabled: ShareConnectionLogic.pushButtonShareAmneziaCopyEnabled - } - ShareConnectionButtonType { - x: 10 - y: 130 - width: 341 - height: 40 - text: ShareConnectionLogic.pushButtonShareAmneziaGenerateText - enabled: ShareConnectionLogic.pushButtonShareAmneziaGenerateEnabled - onClicked: { - ShareConnectionLogic.onPushButtonShareAmneziaGenerateClicked() - } - } - ShareConnectionButtonType { - x: 10 - y: 230 - width: 341 - height: 40 - text: qsTr("Save file") - onClicked: { - ShareConnectionLogic.onPushButtonShareAmneziaSaveClicked() - } - } - TextFieldType { - x: 10 - y: 10 - width: 341 - height: 100 - verticalAlignment: Text.AlignTop - text: ShareConnectionLogic.textEditShareAmneziaCodeText - onEditingFinished: { - ShareConnectionLogic.textEditShareAmneziaCodeText = text - } - } - } - } - onClicked: { - if (active) { - ct.currentIndex = -1 - } else { - ct.clearActive() - ct.currentIndex = 1 - } - } - } - ShareConnectionContent { - id: share_openvpn - x: 0 - text: qsTr("Share for OpenVPN client") - visible: ShareConnectionLogic.pageShareOpenVpnVisible - content: Component { - Item { - width: 360 - height: 380 - ShareConnectionButtonType { - x: 10 - y: 180 - width: 341 - height: 40 - text: ShareConnectionLogic.pushButtonShareOpenVpnCopyText - enabled: ShareConnectionLogic.pushButtonShareOpenVpnCopyEnabled - onClicked: { - ShareConnectionLogic.onPushButtonShareOpenVpnCopyClicked() - } - } - ShareConnectionButtonType { - x: 10 - y: 130 - width: 341 - height: 40 - text: ShareConnectionLogic.pushButtonShareOpenVpnGenerateText - onClicked: { - ShareConnectionLogic.onPushButtonShareOpenVpnGenerateClicked() - } - enabled: ShareConnectionLogic.pushButtonShareOpenVpnGenerateEnabled - } - ShareConnectionButtonType { - x: 10 - y: 230 - width: 341 - height: 40 - text: qsTr("Save file") - enabled: ShareConnectionLogic.pushButtonShareOpenVpnSaveEnabled - onClicked: { - ShareConnectionLogic.onPushButtonShareOpenVpnSaveClicked() - } - } - TextFieldType { - x: 10 - y: 10 - width: 341 - height: 100 - verticalAlignment: Text.AlignTop - text: ShareConnectionLogic.textEditShareOpenVpnCodeText - onEditingFinished: { - ShareConnectionLogic.textEditShareOpenVpnCodeText = text - } - } - } - } - onClicked: { - if (active) { - ct.currentIndex = -1 - } else { - ct.clearActive() - ct.currentIndex = 2 - } - } - } - ShareConnectionContent { - id: share_shadowshock - x: 0 - text: qsTr("Share for ShadowSocks client") - visible: ShareConnectionLogic.pageShareShadowSocksVisible - content: Component { - Item { - width: 360 - height: 380 - LabelType { - x: 10 - y: 70 - width: 100 - height: 20 - text: qsTr("Password") - } - LabelType { - x: 10 - y: 10 - width: 100 - height: 20 - text: qsTr("Server:") - } - LabelType { - x: 10 - y: 50 - width: 100 - height: 20 - text: qsTr("Encryption:") - } - LabelType { - x: 10 - y: 30 - width: 100 - height: 20 - text: qsTr("Port:") - } - LabelType { - x: 10 - y: 100 - width: 191 - height: 20 - text: qsTr("Connection string") - } - LabelType { - x: 130 - y: 70 - width: 100 - height: 20 - text: ShareConnectionLogic.labelShareShadowSocksPasswordText - } - LabelType { - x: 130 - y: 10 - width: 100 - height: 20 - text: ShareConnectionLogic.labelShareShadowSocksServerText - } - LabelType { - x: 130 - y: 50 - width: 100 - height: 20 - text: ShareConnectionLogic.labelShareShadowSocksMethodText - } - LabelType { - x: 130 - y: 30 - width: 100 - height: 20 - text: ShareConnectionLogic.labelShareShadowSocksPortText - } - Image { - id: label_share_ss_qr_code - x: 85 - y: 235 - width: 200 - height: 200 - source: ShareConnectionLogic.labelShareShadowSocksQrCodeText === "" ? "" : "data:image/png;base64," + UiLogic.labelShareShadowSocksQrCodeText - } - ShareConnectionButtonType { - x: 10 - y: 180 - width: 331 - height: 40 - text: ShareConnectionLogic.pushButtonShareShadowSocksCopyText - enabled: ShareConnectionLogic.pushButtonShareShadowSocksCopyEnabled - onClicked: { - ShareConnectionLogic.onPushButtonShareShadowSocksCopyClicked() - } - } - TextFieldType { - x: 10 - y: 130 - width: 331 - height: 100 - horizontalAlignment: Text.AlignHCenter - text: ShareConnectionLogic.lineEditShareShadowSocksStringText - onEditingFinished: { - ShareConnectionLogic.lineEditShareShadowSocksStringText = text - } - } - } - } - onClicked: { - if (active) { - ct.currentIndex = -1 - } else { - ct.clearActive() - ct.currentIndex = 3 - } - } - } - ShareConnectionContent { - id: share_cloak - x: 0 - text: qsTr("Share for Cloak client") - visible: ShareConnectionLogic.pageShareCloakVisible - content: Component { - Item { - width: 360 - height: 380 - ShareConnectionButtonType { - x: 10 - y: 290 - width: 331 - height: 40 - text: ShareConnectionLogic.pushButtonShareCloakCopyText - enabled: ShareConnectionLogic.pushButtonShareCloakCopyEnabled - onClicked: { - ShareConnectionLogic.onPushButtonShareCloakCopyClicked() - } - } - TextInput { - x: 10 - y: 30 - width: 331 - height: 100 - text: ShareConnectionLogic.plainTextEditShareCloakText - onEditingFinished: { - ShareConnectionLogic.plainTextEditShareCloakText = text - } - } - } - } - onClicked: { - if (active) { - ct.currentIndex = -1 - } else { - ct.clearActive() - ct.currentIndex = 4 - } + + ListView { + id: tb_c + x: 10 + width: parent.width - 10 + height: tb_c.contentItem.height + currentIndex: -1 + spacing: 10 + clip: true + interactive: false + model: proxyProtocolsModel + + delegate: Item { + implicitWidth: tb_c.width - 10 + implicitHeight: c_item.height + + ShareConnectionContent { + id: c_item + text: qsTr("Share for ") + name_role + height: 40 + width: tb_c.width - 10 + onClicked: UiLogic.onGotoShareProtocolPage(proxyProtocolsModel.mapToSource(index)) } } } diff --git a/client/ui/qml/Pages/PageSites.qml b/client/ui/qml/Pages/PageSites.qml index a75b7b72..0a90166f 100644 --- a/client/ui/qml/Pages/PageSites.qml +++ b/client/ui/qml/Pages/PageSites.qml @@ -15,36 +15,30 @@ PageBase { BackButton { id: back } - Text { - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 16 + + Caption { + id: caption + text: SitesLogic.labelSitesAddCustomText + } + + LabelType { + id: lb_addr color: "#333333" - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter text: qsTr("Web site/Hostname/IP address/Subnet") x: 20 - y: 110 - width: 311 + anchors.top: caption.bottom + anchors.topMargin: 10 + width: parent.width height: 21 } - Text { - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 20 - color: "#100A44" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignTop - text: SitesLogic.labelSitesAddCustomText - x: 20 - y: 40 - width: 340 - height: 60 - } + TextFieldType { - x: 20 - y: 140 - width: 231 + anchors.top: lb_addr.bottom + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: 20 + anchors.right: sites_add.left + anchors.rightMargin: 10 height: 31 placeholderText: qsTr("yousite.com or IP address") text: SitesLogic.lineEditSitesAddCustomText @@ -55,10 +49,13 @@ PageBase { SitesLogic.onPushButtonAddCustomSitesClicked() } } + BlueButtonType { id: sites_add - x: 260 - y: 140 + anchors.right: sites_import.left + anchors.rightMargin: 10 + anchors.top: lb_addr.bottom + anchors.topMargin: 10 width: 51 height: 31 font.pixelSize: 24 @@ -67,23 +64,13 @@ PageBase { SitesLogic.onPushButtonAddCustomSitesClicked() } } - BlueButtonType { - id: sites_delete - x: 80 - y: 589 - width: 231 - height: 31 - font.pixelSize: 16 - text: qsTr("Delete selected") - onClicked: { - SitesLogic.onPushButtonSitesDeleteClicked(tb.currentRow) - } - } BasicButtonType { id: sites_import - x: 320 - y: 140 + anchors.right: parent.right + anchors.rightMargin: 20 + anchors.top: lb_addr.bottom + anchors.topMargin: 10 width: 51 height: 31 background: Rectangle { @@ -116,12 +103,15 @@ PageBase { SitesLogic.onPushButtonSitesImportClicked(fileUrl) } } + ListView { id: tb x: 20 - y: 200 - width: 341 - height: 371 + anchors.top: sites_add.bottom + anchors.topMargin: 10 + width: parent.width - 40 + anchors.bottom: sites_delete.top + anchors.bottomMargin: 10 spacing: 1 clip: true property int currentRow: -1 @@ -187,4 +177,17 @@ PageBase { } } } + + BlueButtonType { + id: sites_delete + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + height: 31 + font.pixelSize: 16 + text: qsTr("Delete selected") + onClicked: { + SitesLogic.onPushButtonSitesDeleteClicked(tb.currentRow) + } + } } diff --git a/client/ui/qml/Pages/PageStart.qml b/client/ui/qml/Pages/PageStart.qml index b96767e8..b172a15c 100644 --- a/client/ui/qml/Pages/PageStart.qml +++ b/client/ui/qml/Pages/PageStart.qml @@ -128,7 +128,7 @@ PageBase { anchors.top: parent.top anchors.topMargin: 5 - text: qsTr("Where to get connection data →") + text: qsTr("How to get own server? →") background: Item { anchors.fill: parent } @@ -147,7 +147,7 @@ PageBase { checkable: true checked: true onClicked: { - Qt.openUrlExternally("https://amnezia.org") + Qt.openUrlExternally("https://amnezia.org/instruction.html") } } LabelType { diff --git a/client/ui/qml/Pages/PageVPN.qml b/client/ui/qml/Pages/PageVPN.qml index 7e20824a..20fae41c 100644 --- a/client/ui/qml/Pages/PageVPN.qml +++ b/client/ui/qml/Pages/PageVPN.qml @@ -1,5 +1,6 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 import PageEnum 1.0 import "./" import "../Controls" @@ -11,12 +12,11 @@ PageBase { logic: VpnLogic Image { + id: bg_top anchors.horizontalCenter: parent.horizontalCenter y: 0 width: parent.width - -// width: 380 -// height: 325 + height: parent.height * 0.28 source: "qrc:/images/background_connected.png" } @@ -31,38 +31,26 @@ PageBase { } } - LabelType { - id: error_text - x: 0 - y: 280 - width: 381 - height: 61 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.Wrap - text: VpnLogic.labelErrorText - } - Text { - anchors.horizontalCenter: parent.horizontalCenter - y: 250 - width: 380 - height: 31 - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 15 - color: "#181922" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.Wrap - text: VpnLogic.labelStateText + AnimatedImage { + id: connect_anim + source: "qrc:/images/animation.gif" + anchors.top: bg_top.bottom + anchors.topMargin: 10 + anchors.horizontalCenter: root.horizontalCenter + width: Math.min(parent.width, parent.height) / 4 + height: width + + visible: !VpnLogic.pushButtonConnectVisible + paused: VpnLogic.pushButtonConnectVisible + //VisibleBehavior on visible { } } BasicButtonType { id: button_connect - anchors.horizontalCenter: parent.horizontalCenter - y: 200 - width: 80 - height: 40 + anchors.horizontalCenter: connect_anim.horizontalCenter + anchors.verticalCenter: connect_anim.verticalCenter + width: connect_anim.width + height: width checkable: true checked: VpnLogic.pushButtonConnectChecked onCheckedChanged: { @@ -71,18 +59,102 @@ PageBase { } background: Image { anchors.fill: parent - source: button_connect.checked ? "qrc:/images/connect_button_connected.png" - : "qrc:/images/connect_button_disconnected.png" + source: button_connect.checked ? "qrc:/images/connected.png" + : "qrc:/images/disconnected.png" } contentItem: Item {} antialiasing: true enabled: VpnLogic.pushButtonConnectEnabled + opacity: VpnLogic.pushButtonConnectVisible ? 1 : 0 + +// transitions: Transition { +// NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad; duration: 500 } +// } + } + + + LabelType { + id: lb_state + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: button_connect.bottom + width: parent.width + height: 21 + horizontalAlignment: Text.AlignHCenter + text: VpnLogic.labelStateText + } + + RowLayout { + id: layout1 + anchors.top: lb_state.bottom + //anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + height: 21 + + + LabelType { + Layout.alignment: Qt.AlignRight + height: 21 + text: qsTr("Server") + ": " + } + + BasicButtonType { + Layout.alignment: Qt.AlignLeft + height: 21 + background: Item {} + text: VpnLogic.labelCurrentServer + font.family: "Lato" + font.styleName: "normal" + font.pixelSize: 16 + onClicked: { + UiLogic.goToPage(PageEnum.ServersList) + } + } + } + + RowLayout { + id: layout2 + anchors.top: layout1.bottom + anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + height: 21 + + + LabelType { + Layout.alignment: Qt.AlignRight + height: 21 + text: qsTr("Service") + ": " + } + + BasicButtonType { + Layout.alignment: Qt.AlignLeft + height: 21 + background: Item {} + text: VpnLogic.labelCurrentService + font.family: "Lato" + font.styleName: "normal" + font.pixelSize: 16 + onClicked: { + UiLogic.onGotoCurrentProtocolsPage() + } + } + } + + + LabelType { + id: error_text + anchors.top: layout2.bottom + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: 21 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + text: VpnLogic.labelErrorText } Item { x: 0 anchors.bottom: line.top - anchors.bottomMargin: 10 + anchors.bottomMargin: GC.isMobile() ? 0 :10 width: parent.width height: 51 Image { @@ -136,7 +208,7 @@ PageBase { x: 20 width: parent.width - 40 height: 1 - anchors.bottom: conn_type_label.top + anchors.bottom: GC.isMobile() ? root.bottom : conn_type_label.top anchors.bottomMargin: 10 color: "#DDDDDD" } @@ -146,7 +218,7 @@ PageBase { visible: !GC.isMobile() x: 20 anchors.bottom: conn_type_group.top - anchors.bottomMargin: 10 + anchors.bottomMargin: GC.isMobile() ? 0 :10 width: 281 height: GC.isMobile() ? 0: 21 font.family: "Lato" @@ -208,11 +280,11 @@ PageBase { BasicButtonType { id: button_add_site + visible: !GC.isMobile() anchors.horizontalCenter: parent.horizontalCenter y: parent.height - 60 - //anchors.bottom: parent.bottom width: parent.width - 40 - height: 40 + height: GC.isMobile() ? 0: 40 text: qsTr("+ Add site") enabled: VpnLogic.pushButtonVpnAddSiteEnabled background: Rectangle { diff --git a/client/ui/qml/Pages/Protocols/PageProtoCloak.qml b/client/ui/qml/Pages/Protocols/PageProtoCloak.qml index 0e7baffb..c98aa6e7 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoCloak.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoCloak.qml @@ -1,5 +1,6 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 import ProtocolEnum 1.0 import "../" import "../../Controls" @@ -10,144 +11,155 @@ PageProtocolBase { protocol: ProtocolEnum.Cloak logic: UiLogic.protocolLogic(protocol) - enabled: logic.pageProtoCloakEnabled + enabled: logic.pageEnabled BackButton { id: back } - Item { - x: 0 - y: 40 - width: 380 - height: 600 + + Caption { + id: caption + text: qsTr("Cloak Settings") + } + + ColumnLayout { + id: content enabled: logic.pageEnabled - ComboBoxType { - x: 190 - y: 60 - width: 151 - height: 31 - model: [ - qsTr("chacha20-poly1305"), - qsTr("aes-256-gcm"), - qsTr("aes-192-gcm"), - qsTr("aes-128-gcm") - ] - currentIndex: { - for (let i = 0; i < model.length; ++i) { - if (logic.comboBoxProtoCloakCipherText === model[i]) { - return i + anchors.top: caption.bottom + anchors.left: root.left + anchors.right: root.right + anchors.bottom: pb_save.top + anchors.margins: 20 + anchors.topMargin: 10 + + + RowLayout { + Layout.fillWidth: true + + LabelType { + height: 31 + text: qsTr("Cipher") + Layout.preferredWidth: 0.3 * root.width - 10 + } + + ComboBoxType { + Layout.fillWidth: true + height: 31 + model: [ + qsTr("chacha20-poly1305"), + qsTr("aes-256-gcm"), + qsTr("aes-192-gcm"), + qsTr("aes-128-gcm") + ] + currentIndex: { + for (let i = 0; i < model.length; ++i) { + if (logic.comboBoxCipherText === model[i]) { + return i + } } + return -1 } - return -1 - } - onCurrentTextChanged: { - logic.comboBoxProtoCloakCipherText = currentText - } - } - LabelType { - x: 30 - y: 60 - width: 151 - height: 31 - text: qsTr("Cipher") - } - LabelType { - x: 30 - y: 160 - width: 151 - height: 31 - text: qsTr("Port") - } - Text { - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 24 - color: "#100A44" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - text: qsTr("Cloak Settings") - x: 20 - y: 0 - width: 340 - height: 30 - } - LabelType { - x: 30 - y: 110 - width: 151 - height: 31 - text: qsTr("Fake Web Site") - } - - LabelType { - id: label_proto_cloak_info - x: 30 - y: 550 - width: 321 - height: 41 - visible: logic.labelProtoCloakInfoVisible - text: logic.labelProtoCloakInfoText - } - TextFieldType { - id: lineEdit_proto_cloak_port - x: 190 - y: 160 - width: 151 - height: 31 - text: logic.lineEditProtoCloakPortText - onEditingFinished: { - logic.lineEditProtoCloakPortText = text - } - enabled: logic.lineEditProtoCloakPortEnabled - } - TextFieldType { - id: lineEdit_proto_cloak_site - x: 190 - y: 110 - width: 151 - height: 31 - text: logic.lineEditProtoCloakSiteText - onEditingFinished: { - logic.lineEditProtoCloakSiteText = text - } - } - ProgressBar { - id: progressBar_proto_cloak_reset - anchors.horizontalCenter: parent.horizontalCenter - y: 500 - width: 321 - height: 40 - from: 0 - to: logic.progressBarProtoCloakResetMaximium - value: logic.progressBarProtoCloakResetValue - background: Rectangle { - implicitWidth: parent.width - implicitHeight: parent.height - color: "#100A44" - radius: 4 - } - - contentItem: Item { - implicitWidth: parent.width - implicitHeight: parent.height - Rectangle { - width: progressBar_proto_cloak_reset.visualPosition * parent.width - height: parent.height - radius: 4 - color: Qt.rgba(255, 255, 255, 0.15); + onCurrentTextChanged: { + logic.comboBoxCipherText = currentText } } - visible: logic.progressBarProtoCloakResetVisible } - BlueButtonType { - anchors.horizontalCenter: parent.horizontalCenter - y: 500 - width: 321 - height: 40 - text: qsTr("Save and restart VPN") - visible: logic.pushButtonCloakSaveVisible - onClicked: { - logic.onPushButtonProtoCloakSaveClicked() + + RowLayout { + Layout.fillWidth: true + + LabelType { + Layout.preferredWidth: 0.3 * root.width - 10 + height: 31 + text: qsTr("Fake Web Site") } + + TextFieldType { + id: lineEdit_proto_cloak_site + Layout.fillWidth: true + height: 31 + text: logic.lineEditSiteText + onEditingFinished: { + logic.lineEditSiteText = text + } + } + } + + RowLayout { + Layout.fillWidth: true + + LabelType { + Layout.preferredWidth: 0.3 * root.width - 10 + height: 31 + text: qsTr("Port") + } + + TextFieldType { + id: lineEdit_proto_cloak_port + Layout.fillWidth: true + height: 31 + text: logic.lineEditPortText + onEditingFinished: { + logic.lineEditPortText = text + } + enabled: logic.lineEditPortEnabled + } + } + + Item { + Layout.fillHeight: true } } + + LabelType { + id: label_proto_cloak_info + x: 30 + anchors.bottom: pb_save.top + anchors.bottomMargin: 10 + width: parent.width - 40 + visible: logic.labelInfoVisible + text: logic.labelInfoText + } + + + ProgressBar { + id: progressBar_proto_cloak_reset + anchors.horizontalCenter: parent.horizontalCenter + anchors.fill: pb_save + from: 0 + to: logic.progressBarResetMaximium + value: logic.progressBarResetValue + background: Rectangle { + implicitWidth: parent.width + implicitHeight: parent.height + color: "#100A44" + radius: 4 + } + + contentItem: Item { + implicitWidth: parent.width + implicitHeight: parent.height + Rectangle { + width: progressBar_proto_cloak_reset.visualPosition * parent.width + height: parent.height + radius: 4 + color: Qt.rgba(255, 255, 255, 0.15); + } + } + visible: logic.progressBarResetVisible + } + BlueButtonType { + id: pb_save + anchors.horizontalCenter: parent.horizontalCenter + enabled: logic.pageEnabled + anchors.bottom: root.bottom + anchors.bottomMargin: 20 + width: root.width - 60 + height: 40 + text: qsTr("Save and restart VPN") + visible: logic.pushButtonSaveVisible + onClicked: { + logic.onPushButtonSaveClicked() + } + } + } diff --git a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml index c1545a19..42ef1e59 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml @@ -338,9 +338,6 @@ PageProtocolBase { } - - - LabelType { id: label_proto_openvpn_info @@ -349,8 +346,6 @@ PageProtocolBase { text: logic.labelProtoOpenVpnInfoText } - - Rectangle { id: it_save implicitWidth: parent.width diff --git a/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml b/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml index 28d433fa..88239fb5 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml @@ -1,5 +1,6 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 import ProtocolEnum 1.0 import "../" import "../../Controls" @@ -10,123 +11,136 @@ PageProtocolBase { protocol: ProtocolEnum.ShadowSocks logic: UiLogic.protocolLogic(protocol) - enabled: logic.pageProtoShadowSocksEnabled + enabled: logic.pageEnabled BackButton { id: back } - Item { - x: 0 - y: 40 - width: 380 - height: 600 + + Caption { + id: caption + text: qsTr("ShadowSocks Settings") + } + + + ColumnLayout { + id: content enabled: logic.pageEnabled - ComboBoxType { - x: 190 - y: 60 - width: 151 - height: 31 - model: [ - qsTr("chacha20-ietf-poly1305"), - qsTr("xchacha20-ietf-poly1305"), - qsTr("aes-256-gcm"), - qsTr("aes-192-gcm"), - qsTr("aes-128-gcm") - ] - currentIndex: { - for (let i = 0; i < model.length; ++i) { - if (logic.comboBoxProtoShadowSocksCipherText === model[i]) { - return i - } - } - return -1 - } - } - LabelType { - x: 30 - y: 60 - width: 151 - height: 31 - text: qsTr("Cipher") - } - LabelType { - x: 30 - y: 110 - width: 151 - height: 31 - text: qsTr("Port") - } - Text { - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 24 - color: "#100A44" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - text: qsTr("ShadowSocks Settings") - x: 30 - y: 0 - width: 340 - height: 30 - } - LabelType { - id: label_proto_shadowsocks_info - x: 30 - y: 550 - width: 321 - height: 41 - visible: logic.labelProtoShadowSocksInfoVisible - text: logic.labelProtoShadowSocksInfoText - } - TextFieldType { - id: lineEdit_proto_shadowsocks_port - x: 190 - y: 110 - width: 151 - height: 31 - text: logic.lineEditProtoShadowSocksPortText - onEditingFinished: { - logic.lineEditProtoShadowSocksPortText = text - } - enabled: logic.lineEditProtoShadowSocksPortEnabled - } - ProgressBar { - id: progressBar_proto_shadowsocks_reset - anchors.horizontalCenter: parent.horizontalCenter - y: 500 - width: 321 - height: 40 - from: 0 - to: logic.progressBarProtoShadowSocksResetMaximium - value: logic.progressBarProtoShadowSocksResetValue - visible: logic.progressBarProtoShadowSocksResetVisible - background: Rectangle { - implicitWidth: parent.width - implicitHeight: parent.height - color: "#100A44" - radius: 4 + anchors.top: caption.bottom + anchors.left: root.left + anchors.right: root.right + anchors.bottom: pb_save.top + anchors.margins: 20 + anchors.topMargin: 10 + + + + RowLayout { + Layout.fillWidth: true + + LabelType { + height: 31 + text: qsTr("Cipher") + Layout.preferredWidth: 0.3 * root.width - 10 } - contentItem: Item { - implicitWidth: parent.width - implicitHeight: parent.height - Rectangle { - width: progressBar_proto_shadowsocks_reset.visualPosition * parent.width - height: parent.height - radius: 4 - color: Qt.rgba(255, 255, 255, 0.15); + ComboBoxType { + height: 31 + Layout.fillWidth: true + + model: [ + qsTr("chacha20-ietf-poly1305"), + qsTr("xchacha20-ietf-poly1305"), + qsTr("aes-256-gcm"), + qsTr("aes-192-gcm"), + qsTr("aes-128-gcm") + ] + currentIndex: { + for (let i = 0; i < model.length; ++i) { + if (logic.comboBoxCipherText === model[i]) { + return i + } + } + return -1 } } } - BlueButtonType { - anchors.horizontalCenter: parent.horizontalCenter - y: 500 - width: 321 - height: 40 - text: qsTr("Save and restart VPN") - visible: logic.pushButtonShadowSocksSaveVisible - onClicked: { - logic.onPushButtonProtoShadowSocksSaveClicked() + + RowLayout { + Layout.fillWidth: true + + LabelType { + Layout.preferredWidth: 0.3 * root.width - 10 + height: 31 + text: qsTr("Port") + } + + TextFieldType { + id: lineEdit_proto_shadowsocks_port + Layout.fillWidth: true + height: 31 + text: logic.lineEditPortText + onEditingFinished: { + logic.lineEditPortText = text + } + enabled: logic.lineEditPortEnabled + } + } + + Item { + Layout.fillHeight: true + } + } + + + LabelType { + id: label_proto_shadowsocks_info + x: 30 + anchors.bottom: pb_save.top + anchors.bottomMargin: 10 + width: parent.width - 40 + height: 41 + visible: logic.labelInfoVisible + text: logic.labelInfoText + } + + ProgressBar { + id: progressBar_reset + anchors.fill: pb_save + from: 0 + to: logic.progressBaResetMaximium + value: logic.progressBaResetValue + visible: logic.progressBaResetVisible + background: Rectangle { + implicitWidth: parent.width + implicitHeight: parent.height + color: "#100A44" + radius: 4 + } + + contentItem: Item { + implicitWidth: parent.width + implicitHeight: parent.height + Rectangle { + width: progressBar_reset.visualPosition * parent.width + height: parent.height + radius: 4 + color: Qt.rgba(255, 255, 255, 0.15); } } } + + BlueButtonType { + id: pb_save + enabled: logic.pageEnabled + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: root.bottom + anchors.bottomMargin: 20 + width: root.width - 60 + height: 40 + text: qsTr("Save and restart VPN") + visible: logic.pushButtonSaveVisible + onClicked: { + logic.onPushButtonSaveClicked() + } + } } diff --git a/client/ui/qml/Pages/Share/PageShareProtoAmnezia.qml b/client/ui/qml/Pages/Share/PageShareProtoAmnezia.qml new file mode 100644 index 00000000..eb2b827e --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoAmnezia.qml @@ -0,0 +1,133 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.Any + + BackButton { + id: back + } + Caption { + id: caption + text: qsTr("Share for Amnezia") + } + + 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 + 20 + clip: true + + ColumnLayout { + id: content + enabled: logic.pageEnabled + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Text { + id: lb_desc + Layout.fillWidth: true + font.family: "Lato" + font.styleName: "normal" + font.pixelSize: 16 + color: "#181922" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + text: ShareConnectionLogic.shareFullAccess + ? qsTr("Anyone who logs in with this code will have the same permissions to use VPN and YOUR SERVER as you. \n +This code includes your server credentials!\n +Provide this code only to TRUSTED users.") + : qsTr("Anyone who logs in with this code will be able to connect to this VPN server. \n +This code does not include server credentials.\n +New encryption keys pair will be generated.") + } + + ShareConnectionButtonType { + Layout.topMargin: 20 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + text: ShareConnectionLogic.shareFullAccess + ? showConfigText + : (genConfigProcess ? generatingConfigText : generateConfigText) + onClicked: { + enabled = false + genConfigProcess = true + ShareConnectionLogic.onPushButtonShareAmneziaGenerateClicked() + enabled = true + genConfigProcess = false + } + } + + TextAreaType { + id: tfShareCode + + Layout.topMargin: 20 + Layout.bottomMargin: 20 + Layout.preferredHeight: 200 + + Layout.fillWidth: true + + textArea.readOnly: true + textArea.wrapMode: TextEdit.WrapAnywhere + textArea.verticalAlignment: Text.AlignTop + textArea.text: ShareConnectionLogic.textEditShareAmneziaCodeText + + visible: tfShareCode.textArea.length > 0 + } + + + ShareConnectionButtonCopyType { + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + copyText: tfShareCode.textArea.text + } + ShareConnectionButtonType { + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + text: qsTr("Save to file") + enabled: tfShareCode.textArea.length > 0 + visible: tfShareCode.textArea.length > 0 + + onClicked: { + UiLogic.saveTextFile(qsTr("Save AmneziaVPN config"), "*.vpn", tfShareCode.textArea.text) + } + } + + Image { + id: label_share_code + Layout.topMargin: 20 + Layout.fillWidth: true + Layout.preferredHeight: width + smooth: false + source: ShareConnectionLogic.shareAmneziaQrCodeText + visible: ShareConnectionLogic.shareAmneziaQrCodeText.length > 0 + } + + LabelType { + height: 20 + text: qsTr("Config too long to be displayed as QR code") + visible: ShareConnectionLogic.shareAmneziaQrCodeText.length == 0 && tfShareCode.textArea.length > 0 + } + } + } +} diff --git a/client/ui/qml/Pages/Share/PageShareProtoCloak.qml b/client/ui/qml/Pages/Share/PageShareProtoCloak.qml new file mode 100644 index 00000000..7bc888ad --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoCloak.qml @@ -0,0 +1,109 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.Cloak + + BackButton { + id: back + } + Caption { + id: caption + text: qsTr("Share Cloak Settings") + } + + 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 + + + LabelType { + id: lb_desc + Layout.fillWidth: true + Layout.topMargin: 10 + + horizontalAlignment: Text.AlignHCenter + + wrapMode: Text.Wrap + text: qsTr("Note: Cloak protocol using same password for all connections") + } + + ShareConnectionButtonType { + Layout.topMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + text: genConfigProcess ? generatingConfigText : generateConfigText + onClicked: { + enabled = false + genConfigProcess = true + ShareConnectionLogic.onPushButtonShareCloakGenerateClicked() + enabled = true + genConfigProcess = false + } + } + + TextAreaType { + id: tfShareCode + + Layout.topMargin: 20 + Layout.bottomMargin: 20 + Layout.fillWidth: true + Layout.preferredHeight: 200 + + textArea.readOnly: true + textArea.text: ShareConnectionLogic.textEditShareCloakText + + visible: tfShareCode.textArea.length > 0 + } + + ShareConnectionButtonCopyType { + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + copyText: tfShareCode.textArea.text + } + + ShareConnectionButtonType { + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + text: qsTr("Save to file") + enabled: tfShareCode.textArea.length > 0 + visible: tfShareCode.textArea.length > 0 + + onClicked: { + UiLogic.saveTextFile(qsTr("Save AmneziaVPN config"), "*.json", tfShareCode.textArea.text) + } + } + + } + } + +} diff --git a/client/ui/qml/Pages/Share/PageShareProtoIkev2.qml b/client/ui/qml/Pages/Share/PageShareProtoIkev2.qml new file mode 100644 index 00000000..0a0db4df --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoIkev2.qml @@ -0,0 +1,140 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.Ikev2 + + BackButton { + id: back + } + Caption { + id: caption + text: qsTr("Share IKEv2 Settings") + } + + TextAreaType { + id: tfCert + textArea.readOnly: true + textArea.text: ShareConnectionLogic.textEditShareIkev2CertText + + visible: false + } + + TextAreaType { + id: tfMobileConfig + textArea.readOnly: true + textArea.text: ShareConnectionLogic.textEditShareIkev2MobileConfigText + + visible: false + } + + TextAreaType { + id: tfStrongSwanConfig + textArea.readOnly: true + textArea.text: ShareConnectionLogic.textEditShareIkev2StrongSwanConfigText + + visible: false + } + + 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 + +// LabelType { +// id: lb_desc +// Layout.fillWidth: true +// Layout.topMargin: 10 + +// horizontalAlignment: Text.AlignHCenter + +// wrapMode: Text.Wrap +// text: qsTr("Note: ShadowSocks protocol using same password for all connections") +// } + + ShareConnectionButtonType { + Layout.topMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + text: genConfigProcess ? generatingConfigText : generateConfigText + onClicked: { + enabled = false + genConfigProcess = true + ShareConnectionLogic.onPushButtonShareIkev2GenerateClicked() + enabled = true + genConfigProcess = false + } + } + + ShareConnectionButtonType { + Layout.topMargin: 30 + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + width: parent.width - 60 + + text: qsTr("Export p12 certificate") + enabled: tfCert.textArea.length > 0 + visible: tfCert.textArea.length > 0 + + onClicked: { + UiLogic.saveTextFile(qsTr("Export p12 certificate"), "*.p12", tfCert.textArea.text) + } + } + + ShareConnectionButtonType { + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + width: parent.width - 60 + + text: qsTr("Export config for Apple") + enabled: tfMobileConfig.textArea.length > 0 + visible: tfMobileConfig.textArea.length > 0 + + onClicked: { + UiLogic.saveTextFile(qsTr("Export config for Apple"), "*.plist", tfMobileConfig.textArea.text) + } + } + + ShareConnectionButtonType { + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + width: parent.width - 60 + + text: qsTr("Export config for StrongSwan") + enabled: tfStrongSwanConfig.textArea.length > 0 + visible: tfStrongSwanConfig.textArea.length > 0 + + onClicked: { + UiLogic.saveTextFile(qsTr("Export config for StrongSwan"), "*.profile", tfStrongSwanConfig.textArea.text) + } + } + } + } +} diff --git a/client/ui/qml/Pages/Share/PageShareProtoOpenVPN.qml b/client/ui/qml/Pages/Share/PageShareProtoOpenVPN.qml new file mode 100644 index 00000000..c45bdb33 --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoOpenVPN.qml @@ -0,0 +1,106 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.OpenVpn + + BackButton { + id: back + } + Caption { + id: caption + text: qsTr("Share OpenVPN Settings") + } + + 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 + + + LabelType { + id: lb_desc + Layout.fillWidth: true + Layout.topMargin: 10 + + horizontalAlignment: Text.AlignHCenter + + wrapMode: Text.Wrap + text: qsTr("New encryption keys pair will be generated.") + } + + ShareConnectionButtonType { + Layout.topMargin: 20 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + text: genConfigProcess ? generatingConfigText : generateConfigText + onClicked: { + enabled = false + genConfigProcess = true + ShareConnectionLogic.onPushButtonShareOpenVpnGenerateClicked() + genConfigProcess = false + enabled = true + } + } + + TextAreaType { + id: tfShareCode + Layout.topMargin: 20 + Layout.preferredHeight: 200 + Layout.fillWidth: true + + textArea.readOnly: true + + textArea.verticalAlignment: Text.AlignTop + textArea.text: ShareConnectionLogic.textEditShareOpenVpnCodeText + + visible: tfShareCode.textArea.length > 0 + } + + ShareConnectionButtonCopyType { + Layout.preferredHeight: 40 + Layout.fillWidth: true + + copyText: tfShareCode.textArea.text + } + ShareConnectionButtonType { + Layout.bottomMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + width: parent.width - 60 + + text: qsTr("Save to file") + enabled: tfShareCode.textArea.length > 0 + visible: tfShareCode.textArea.length > 0 + + onClicked: { + UiLogic.saveTextFile(qsTr("Save OpenVPN config"), "*.ovpn", tfShareCode.textArea.text) + } + } + } + } +} diff --git a/client/ui/qml/Pages/Share/PageShareProtoSftp.qml b/client/ui/qml/Pages/Share/PageShareProtoSftp.qml new file mode 100644 index 00000000..5cb137f9 --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoSftp.qml @@ -0,0 +1,21 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.Sftp + + BackButton { + id: back + } + + Caption { + id: caption + text: qsTr("Share SFTF settings") + } + + } diff --git a/client/ui/qml/Pages/Share/PageShareProtoShadowSocks.qml b/client/ui/qml/Pages/Share/PageShareProtoShadowSocks.qml new file mode 100644 index 00000000..f8d83ae3 --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoShadowSocks.qml @@ -0,0 +1,124 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.ShadowSocks + + BackButton { + id: back + } + Caption { + id: caption + text: qsTr("Share ShadowSocks Settings") + } + + 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 + + LabelType { + id: lb_desc + Layout.fillWidth: true + Layout.topMargin: 10 + + horizontalAlignment: Text.AlignHCenter + + wrapMode: Text.Wrap + text: qsTr("Note: ShadowSocks protocol using same password for all connections") + } + + ShareConnectionButtonType { + Layout.topMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + text: genConfigProcess ? generatingConfigText : generateConfigText + onClicked: { + enabled = false + genConfigProcess = true + ShareConnectionLogic.onPushButtonShareShadowSocksGenerateClicked() + enabled = true + genConfigProcess = false + } + } + + TextAreaType { + id: tfShareCode + + Layout.topMargin: 20 + Layout.preferredHeight: 200 + Layout.fillWidth: true + + textArea.readOnly: true + textArea.wrapMode: TextEdit.WrapAnywhere + textArea.verticalAlignment: Text.AlignTop + textArea.text: ShareConnectionLogic.textEditShareShadowSocksText + + visible: tfShareCode.textArea.length > 0 + } + ShareConnectionButtonCopyType { + Layout.preferredHeight: 40 + Layout.fillWidth: true + Layout.bottomMargin: 20 + + start_text: qsTr("Copy config") + copyText: tfShareCode.textArea.text + } + + LabelType { + height: 20 + visible: tfConnString.length > 0 + text: qsTr("Connection string") + } + TextFieldType { + id: tfConnString + height: 100 + horizontalAlignment: Text.AlignHCenter + Layout.fillWidth: true + text: ShareConnectionLogic.lineEditShareShadowSocksStringText + visible: tfConnString.length > 0 + + readOnly: true + } + ShareConnectionButtonCopyType { + Layout.preferredHeight: 40 + Layout.fillWidth: true + start_text: qsTr("Copy string") + copyText: tfConnString.text + } + + Image { + id: label_share_ss_qr_code + Layout.topMargin: 20 + Layout.fillWidth: true + Layout.preferredHeight: width + smooth: false + source: ShareConnectionLogic.shareShadowSocksQrCodeText + } + } + } +} diff --git a/client/ui/qml/Pages/Share/PageShareProtoTorWebSite.qml b/client/ui/qml/Pages/Share/PageShareProtoTorWebSite.qml new file mode 100644 index 00000000..59a96345 --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoTorWebSite.qml @@ -0,0 +1,20 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.TorWebSite + + BackButton { + id: back + } + + Caption { + id: caption + text: qsTr("Share TOR Web site") + } +} diff --git a/client/ui/qml/Pages/Share/PageShareProtoWireGuard.qml b/client/ui/qml/Pages/Share/PageShareProtoWireGuard.qml new file mode 100644 index 00000000..f934ea4c --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtoWireGuard.qml @@ -0,0 +1,112 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageShareProtocolBase { + id: root + protocol: ProtocolEnum.WireGuard + + BackButton { + id: back + } + Caption { + id: caption + text: qsTr("Share WireGuard Settings") + } + + 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.bottom + anchors.left: parent.left + anchors.right: parent.right + + LabelType { + id: lb_desc + Layout.fillWidth: true + Layout.topMargin: 10 + + horizontalAlignment: Text.AlignHCenter + + wrapMode: Text.Wrap + text: qsTr("New encryption keys pair will be generated.") + } + + ShareConnectionButtonType { + Layout.topMargin: 10 + Layout.fillWidth: true + Layout.preferredHeight: 40 + + text: genConfigProcess ? generatingConfigText : generateConfigText + onClicked: { + enabled = false + genConfigProcess = true + ShareConnectionLogic.onPushButtonShareWireGuardGenerateClicked() + enabled = true + genConfigProcess = false + } + } + + TextAreaType { + id: tfShareCode + + Layout.topMargin: 20 + Layout.preferredHeight: 200 + Layout.fillWidth: true + + textArea.readOnly: true + textArea.wrapMode: TextEdit.WrapAnywhere + textArea.verticalAlignment: Text.AlignTop + textArea.text: ShareConnectionLogic.textEditShareWireGuardCodeText + + visible: tfShareCode.textArea.length > 0 + } + ShareConnectionButtonCopyType { + Layout.preferredHeight: 40 + Layout.fillWidth: true + + copyText: tfShareCode.textArea.text + } + + ShareConnectionButtonType { + Layout.preferredHeight: 40 + Layout.fillWidth: true + + text: qsTr("Save to file") + enabled: tfShareCode.textArea.length > 0 + visible: tfShareCode.textArea.length > 0 + + onClicked: { + UiLogic.saveTextFile(qsTr("Save OpenVPN config"), "*.conf", tfShareCode.textArea.text) + } + } + + Image { + Layout.topMargin: 20 + Layout.fillWidth: true + Layout.preferredHeight: width + smooth: false + source: ShareConnectionLogic.shareWireGuardQrCodeText + } + } + } +} diff --git a/client/ui/qml/Pages/Share/PageShareProtocolBase.qml b/client/ui/qml/Pages/Share/PageShareProtocolBase.qml new file mode 100644 index 00000000..92659b58 --- /dev/null +++ b/client/ui/qml/Pages/Share/PageShareProtocolBase.qml @@ -0,0 +1,19 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import PageEnum 1.0 +import ProtocolEnum 1.0 +import "./.." +import "../../Controls" +import "../../Config" + +PageBase { + id: root + property var protocol: ProtocolEnum.Any + page: PageEnum.ProtocolShare + logic: ShareConnectionLogic + + readonly property string generateConfigText: qsTr("Generate config") + readonly property string generatingConfigText: qsTr("Generating config...") + readonly property string showConfigText: qsTr("Show config") + property bool genConfigProcess: false +} diff --git a/client/ui/qml/main.qml b/client/ui/qml/main.qml index 1576b640..61e7380f 100644 --- a/client/ui/qml/main.qml +++ b/client/ui/qml/main.qml @@ -3,17 +3,20 @@ import QtQuick.Window 2.14 import QtQuick.Controls 2.12 import QtQuick.Controls.Material 2.12 import PageEnum 1.0 +import PageType 1.0 import Qt.labs.platform 1.1 import Qt.labs.folderlistmodel 2.12 import QtQuick.Dialogs 1.1 import "./" import "Pages" import "Pages/Protocols" +import "Pages/Share" import "Config" Window { property var pages: ({}) property var protocolPages: ({}) + property var sharePages: ({}) id: root visible: true @@ -28,73 +31,28 @@ Window { //flags: Qt.FramelessWindowHint title: "AmneziaVPN" - function gotoPage(page, reset, slide) { + function gotoPage(type, page, reset, slide) { + + let p_obj; + if (type === PageType.Basic) p_obj = pages[page] + else if (type === PageType.Proto) p_obj = protocolPages[page] + else if (type === PageType.ShareProto) p_obj = sharePages[page] + else return + + console.debug("QML gotoPage " + type + " " + page + " " + p_obj) + + + if (slide) { + pageLoader.push(p_obj, {}, StackView.PushTransition) + } else { + pageLoader.push(p_obj, {}, StackView.Immediate) + } + if (reset) { - if (page === PageEnum.ServerSettings) { - ServerSettingsLogic.onUpdatePage(); - } - if (page === PageEnum.ShareConnection) { - } - if (page === PageEnum.Wizard) { - WizardLogic.radioButtonMediumChecked = true - } - if (page === PageEnum.WizardHigh) { - WizardLogic.onUpdatePage(); - } - if (page === PageEnum.ServerConfiguringProgress) { - ServerConfiguringProgressLogic.progressBarValue = 0; - } - if (page === PageEnum.GeneralSettings) { - GeneralSettingsLogic.onUpdatePage(); - } - if (page === PageEnum.ServersList) { - ServerListLogic.onUpdatePage(); - } - if (page === PageEnum.Start) { - StartPageLogic.pushButtonBackFromStartVisible = !pageLoader.empty - StartPageLogic.onUpdatePage(); - } - if (page === PageEnum.NewServerProtocols) { - NewServerProtocolsLogic.onUpdatePage() - } - if (page === PageEnum.ServerContainers) { - ServerContainersLogic.onUpdatePage() - } - if (page === PageEnum.AppSettings) { - AppSettingsLogic.onUpdatePage() - } - if (page === PageEnum.NetworkSettings) { - NetworkSettingsLogic.onUpdatePage() - } - if (page === PageEnum.Sites) { - SitesLogic.updateSitesPage() - } - if (page === PageEnum.Vpn) { - VpnLogic.updateVpnPage() - } + p_obj.logic.onUpdatePage(); } - if (slide) { - pageLoader.push(pages[page], {}, StackView.PushTransition) - } else { - pageLoader.push(pages[page], {}, StackView.Immediate) - } - - pages[page].activated(reset) - } - - function gotoProtocolPage(protocol, reset, slide) { - if (reset && protocolPages[protocol] !== "undefined") { - protocolPages[protocol].logic.onUpdatePage(); - } - - if (slide) { - pageLoader.push(protocolPages[protocol], {}, StackView.PushTransition) - } else { - pageLoader.push(protocolPages[protocol], {}, StackView.Immediate) - } - - protocolPages[protocol].activated(reset) + p_obj.activated(reset) } function close_page() { @@ -146,6 +104,8 @@ Window { color: "white" } + //PageShareProtoAmnezia {} + StackView { id: pageLoader y: GC.isDesktop() ? titleBar.height : 0 @@ -157,6 +117,10 @@ Window { UiLogic.currentPageValue = currentItem.page } + onDepthChanged: { + UiLogic.pagesStackDepth = depth + } + Keys.onPressed: { UiLogic.keyPressEvent(event.key) event.accepted = true @@ -171,7 +135,7 @@ Window { onStatusChanged: if (status == FolderListModel.Ready) { for (var i=0; imoveToThread(&m_vpnConnectionThread); + m_vpnConnectionThread.start(); m_appSettingsLogic = new AppSettingsLogic(this); m_generalSettingsLogic = new GeneralSettingsLogic(this); @@ -104,6 +102,30 @@ UiLogic::UiLogic(QObject *parent) : } +UiLogic::~UiLogic() +{ + m_tray = nullptr; + + emit hide(); + + if (m_vpnConnection->connectionState() != VpnProtocol::ConnectionState::Disconnected) { + m_vpnConnection->disconnectFromVpn(); + for (int i = 0; i < 50; i++) { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + QThread::msleep(100); + if (m_vpnConnection->isDisconnected()) { + break; + } + } + } + + m_vpnConnectionThread.quit(); + m_vpnConnectionThread.wait(3000); + delete m_vpnConnection; + + qDebug() << "Application closed"; +} + void UiLogic::initalizeUiLogic() { qDebug() << "UiLogic::initalizeUiLogic()"; @@ -168,86 +190,8 @@ void UiLogic::initalizeUiLogic() // ui->lineEdit_proto_shadowsocks_port->setValidator(&m_ipPortValidator); // ui->lineEdit_proto_cloak_port->setValidator(&m_ipPortValidator); - - - } - - - - - - - - - - -int UiLogic::getCurrentPageValue() const -{ - return m_currentPageValue; -} - -void UiLogic::setCurrentPageValue(int currentPageValue) -{ - if (m_currentPageValue != currentPageValue) { - m_currentPageValue = currentPageValue; - emit currentPageValueChanged(); - } -} - -QString UiLogic::getTrayIconUrl() const -{ - return m_trayIconUrl; -} - -void UiLogic::setTrayIconUrl(const QString &trayIconUrl) -{ - if (m_trayIconUrl != trayIconUrl) { - m_trayIconUrl = trayIconUrl; - emit trayIconUrlChanged(); - } -} - -bool UiLogic::getTrayActionDisconnectEnabled() const -{ - return m_trayActionDisconnectEnabled; -} - -void UiLogic::setTrayActionDisconnectEnabled(bool trayActionDisconnectEnabled) -{ - if (m_trayActionDisconnectEnabled != trayActionDisconnectEnabled) { - m_trayActionDisconnectEnabled = trayActionDisconnectEnabled; - emit trayActionDisconnectEnabledChanged(); - } -} - -bool UiLogic::getTrayActionConnectEnabled() const -{ - return m_trayActionConnectEnabled; -} - -void UiLogic::setTrayActionConnectEnabled(bool trayActionConnectEnabled) -{ - if (m_trayActionConnectEnabled != trayActionConnectEnabled) { - m_trayActionConnectEnabled = trayActionConnectEnabled; - emit trayActionConnectEnabledChanged(); - } -} - - - - - - - - - - - - - - QString UiLogic::getDialogConnectErrorText() const { return m_dialogConnectErrorText; @@ -261,24 +205,6 @@ void UiLogic::setDialogConnectErrorText(const QString &dialogConnectErrorText) } } - -UiLogic::~UiLogic() -{ - hide(); - m_vpnConnection->disconnectFromVpn(); - for (int i = 0; i < 50; i++) { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - QThread::msleep(100); - if (m_vpnConnection->isDisconnected()) { - break; - } - } - - delete m_vpnConnection; - - qDebug() << "Application closed"; -} - void UiLogic::showOnStartup() { if (! m_settings.isStartMinimized()) { @@ -290,6 +216,26 @@ 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_serverVpnProtocolsLogic, + (PageLogicBase *) m_shareConnectionLogic, + (PageLogicBase *) m_sitesLogic, + (PageLogicBase *) m_startPageLogic, + (PageLogicBase *) m_vpnLogic, + (PageLogicBase *) m_wizardLogic + }) { + logic->onUpdatePage(); + } +} void UiLogic::keyPressEvent(Qt::Key key) { @@ -326,9 +272,7 @@ void UiLogic::keyPressEvent(Qt::Key key) emit goToPage(Page::ServerSettings); break; case Qt::Key_P: - selectedServerIndex = m_settings.defaultServerIndex(); - selectedDockerContainer = m_settings.defaultContainer(selectedServerIndex); - emit goToPage(Page::ServerContainers); + onGotoCurrentProtocolsPage(); break; case Qt::Key_T: SshConfigurator::openSshTerminal(m_settings.serverCredentials(m_settings.defaultServerIndex())); @@ -371,6 +315,13 @@ QString UiLogic::containerDesc(int container) } +void UiLogic::onGotoCurrentProtocolsPage() +{ + selectedServerIndex = m_settings.defaultServerIndex(); + selectedDockerContainer = m_settings.defaultContainer(selectedServerIndex); + emit goToPage(Page::ServerContainers); +} + //void UiLogic::showEvent(QShowEvent *event) @@ -650,12 +601,22 @@ ErrorCode UiLogic::doInstallAction(const std::function &action, void UiLogic::setupTray() { - setTrayState(VpnProtocol::Disconnected); + m_tray = new QSystemTrayIcon(qmlRoot()); + setTrayState(VpnProtocol::Disconnected); + + m_tray->show(); + + connect(m_tray, &QSystemTrayIcon::activated, this, &UiLogic::onTrayActivated); } void UiLogic::setTrayIcon(const QString &iconPath) { - setTrayIconUrl(iconPath); + if (m_tray) m_tray->setIcon(QIcon(QPixmap(iconPath).scaled(128,128))); +} + +void UiLogic::onTrayActivated(QSystemTrayIcon::ActivationReason reason) +{ + emit raise(); } PageProtocolLogicBase *UiLogic::protocolLogic(Protocol p) { @@ -667,17 +628,24 @@ PageProtocolLogicBase *UiLogic::protocolLogic(Protocol p) { } } +QObject *UiLogic::qmlRoot() const +{ + return m_qmlRoot; +} + +void UiLogic::setQmlRoot(QObject *newQmlRoot) +{ + m_qmlRoot = newQmlRoot; +} + PageEnumNS::Page UiLogic::currentPage() { - return static_cast(getCurrentPageValue()); + return static_cast(currentPageValue()); } void UiLogic::setTrayState(VpnProtocol::ConnectionState state) { - QString resourcesPath = "qrc:/images/tray/%1"; - - setTrayActionDisconnectEnabled(state == VpnProtocol::Connected); - setTrayActionConnectEnabled(state == VpnProtocol::Disconnected); + QString resourcesPath = ":/images/tray/%1"; switch (state) { case VpnProtocol::Disconnected: @@ -713,8 +681,40 @@ void UiLogic::setTrayState(VpnProtocol::ConnectionState state) // resourcesPath = ":/images_mac/tray_icon/%1"; // useIconName = useIconName.replace(".png", darkTaskBar ? "@2x.png" : " dark@2x.png"); //#endif - } +bool UiLogic::saveTextFile(const QString& desc, const QString& ext, const QString& data) +{ + QString fileName = QFileDialog::getSaveFileName(nullptr, desc, + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), ext); + QSaveFile save(fileName); + save.open(QIODevice::WriteOnly); + save.write(data.toUtf8()); + + QFileInfo fi(fileName); + QDesktopServices::openUrl(fi.absoluteDir().absolutePath()); + + return save.commit(); +} + +bool UiLogic::saveBinaryFile(const QString &desc, const QString &ext, const QString &data) +{ + QString fileName = QFileDialog::getSaveFileName(nullptr, desc, + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), ext); + + QSaveFile save(fileName); + save.open(QIODevice::WriteOnly); + save.write(QByteArray::fromBase64(data.toUtf8())); + + QFileInfo fi(fileName); + QDesktopServices::openUrl(fi.absoluteDir().absolutePath()); + + return save.commit(); +} + +void UiLogic::copyToClipboard(const QString &text) +{ + qApp->clipboard()->setText(text); +} diff --git a/client/ui/uilogic.h b/client/ui/uilogic.h index 320f7af1..3a33ea45 100644 --- a/client/ui/uilogic.h +++ b/client/ui/uilogic.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "property_helper.h" #include "pages.h" @@ -45,15 +47,13 @@ class UiLogic : public QObject Q_OBJECT AUTO_PROPERTY(bool, pageEnabled) + AUTO_PROPERTY(int, pagesStackDepth) + AUTO_PROPERTY(int, currentPageValue) READONLY_PROPERTY(QObject *, containersModel) READONLY_PROPERTY(QObject *, protocolsModel) - Q_PROPERTY(int currentPageValue READ getCurrentPageValue WRITE setCurrentPageValue NOTIFY currentPageValueChanged) - Q_PROPERTY(QString trayIconUrl READ getTrayIconUrl WRITE setTrayIconUrl NOTIFY trayIconUrlChanged) - Q_PROPERTY(bool trayActionDisconnectEnabled READ getTrayActionDisconnectEnabled WRITE setTrayActionDisconnectEnabled NOTIFY trayActionDisconnectEnabledChanged) - Q_PROPERTY(bool trayActionConnectEnabled READ getTrayActionConnectEnabled WRITE setTrayActionConnectEnabled NOTIFY trayActionConnectEnabledChanged) - + // TODO: review Q_PROPERTY(QString dialogConnectErrorText READ getDialogConnectErrorText WRITE setDialogConnectErrorText NOTIFY dialogConnectErrorTextChanged) public: @@ -82,6 +82,9 @@ public: friend class OtherProtocolsLogic; + Q_INVOKABLE virtual void onUpdatePage() {} // UiLogic is set as logic class for some qml pages + Q_INVOKABLE void onUpdateAllPages(); + Q_INVOKABLE void initalizeUiLogic(); Q_INVOKABLE void onCloseWindow(); @@ -90,24 +93,20 @@ public: Q_INVOKABLE void onGotoPage(PageEnumNS::Page p, bool reset = true, bool slide = true) { emit goToPage(p, reset, slide); } Q_INVOKABLE void onGotoProtocolPage(Protocol p, bool reset = true, bool slide = true) { emit goToProtocolPage(p, reset, slide); } + Q_INVOKABLE void onGotoShareProtocolPage(Protocol p, bool reset = true, bool slide = true) { emit goToShareProtocolPage(p, reset, slide); } + + Q_INVOKABLE void onGotoCurrentProtocolsPage(); Q_INVOKABLE void keyPressEvent(Qt::Key key); - - int getCurrentPageValue() const; - void setCurrentPageValue(int currentPageValue); - QString getTrayIconUrl() const; - void setTrayIconUrl(const QString &trayIconUrl); - bool getTrayActionDisconnectEnabled() const; - void setTrayActionDisconnectEnabled(bool trayActionDisconnectEnabled); - bool getTrayActionConnectEnabled() const; - void setTrayActionConnectEnabled(bool trayActionConnectEnabled); + Q_INVOKABLE bool saveTextFile(const QString& desc, const QString& ext, const QString& data); + Q_INVOKABLE bool saveBinaryFile(const QString& desc, const QString& ext, const QString& data); + Q_INVOKABLE void copyToClipboard(const QString& text); QString getDialogConnectErrorText() const; void setDialogConnectErrorText(const QString &dialogConnectErrorText); signals: - void currentPageValueChanged(); void trayIconUrlChanged(); void trayActionDisconnectEnabledChanged(); void trayActionConnectEnabledChanged(); @@ -116,18 +115,18 @@ signals: void goToPage(PageEnumNS::Page page, bool reset = true, bool slide = true); void goToProtocolPage(Protocol protocol, bool reset = true, bool slide = true); + void goToShareProtocolPage(Protocol protocol, bool reset = true, bool slide = true); + void closePage(); void setStartPage(PageEnumNS::Page page, bool slide = true); void showPublicKeyWarning(); void showConnectErrorDialog(); void show(); void hide(); + void raise(); private: - int m_currentPageValue; - QString m_trayIconUrl; - bool m_trayActionDisconnectEnabled; - bool m_trayActionConnectEnabled; + QSystemTrayIcon *m_tray; QString m_dialogConnectErrorText; @@ -136,6 +135,7 @@ private slots: void installServer(QMap &containers); void setTrayState(VpnProtocol::ConnectionState state); + void onTrayActivated(QSystemTrayIcon::ActivationReason reason); private: PageEnumNS::Page currentPage(); @@ -192,7 +192,12 @@ public: Q_INVOKABLE PageProtocolLogicBase *protocolLogic(Protocol p); + QObject *qmlRoot() const; + void setQmlRoot(QObject *newQmlRoot); + private: + QObject *m_qmlRoot{nullptr}; + AppSettingsLogic *m_appSettingsLogic; GeneralSettingsLogic *m_generalSettingsLogic; NetworkSettingsLogic *m_networkSettingsLogic; @@ -210,6 +215,7 @@ private: QMap m_protocolLogicMap; VpnConnection* m_vpnConnection; + QThread m_vpnConnectionThread; Settings m_settings; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 6169902b..456a8568 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -23,12 +23,7 @@ VpnConnection::VpnConnection(QObject* parent) : QObject(parent) { - QTimer::singleShot(0, this, [this](){ - if (!IpcClient::init()) { - qWarning() << "Error occured when init IPC client"; - emit serviceIsNotReady(); - } - }); + } VpnConnection::~VpnConnection() @@ -190,10 +185,6 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, for (ProtocolEnumNS::Protocol proto : ContainerProps::protocolsForContainer(container)) { -// QString vpnConfigData = -// createVpnConfigurationForProto( -// serverIndex, credentials, container, containerConfig, proto, &e); - QJsonObject vpnConfigData = QJsonDocument::fromJson( createVpnConfigurationForProto( serverIndex, credentials, container, containerConfig, proto, &e).toUtf8()). @@ -205,7 +196,6 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, } vpnConfiguration.insert(ProtocolProps::key_proto_config_data(proto), vpnConfigData); - } Protocol proto = ContainerProps::defaultProtocol(container); @@ -214,11 +204,27 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, return vpnConfiguration; } -ErrorCode VpnConnection::connectToVpn(int serverIndex, +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(); + + #if !defined (Q_OS_ANDROID) && !defined (Q_OS_IOS) + if (!m_IpcClient) { + m_IpcClient = new IpcClient; + } + + if (!m_IpcClient->isSocketConnected()) { + if (!IpcClient::init(m_IpcClient)) { + qWarning() << "Error occured when init IPC client"; + emit serviceIsNotReady(); + emit connectionStateChanged(VpnProtocol::Error); + return; + } + } +#endif + m_remoteAddress = credentials.hostName; emit connectionStateChanged(VpnProtocol::Connecting); @@ -233,26 +239,26 @@ ErrorCode VpnConnection::connectToVpn(int serverIndex, m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig); if (e) { emit connectionStateChanged(VpnProtocol::Error); - return e; + return; } #ifndef Q_OS_ANDROID - m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration)); if (!m_vpnProtocol) { - return ErrorCode::InternalError; + emit VpnProtocol::Error; + return; } m_vpnProtocol->prepare(); - #else Protocol proto = ContainerProps::defaultProtocol(container); AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration); if (!androidVpnProtocol->initialize()) { qDebug() << QString("Init failed") ; - return UnknownError; + emit VpnProtocol::Error; + return; } m_vpnProtocol.reset(androidVpnProtocol); #endif @@ -263,7 +269,8 @@ ErrorCode VpnConnection::connectToVpn(int serverIndex, ServerController::disconnectFromHost(credentials); - return m_vpnProtocol.data()->start(); + e = m_vpnProtocol.data()->start(); + if (e) emit VpnProtocol::Error; } QString VpnConnection::bytesPerSecToText(quint64 bytes) diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 5ad0ef94..9d0c3337 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -34,10 +34,7 @@ public: const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); - ErrorCode connectToVpn(int serverIndex, - const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig); - void disconnectFromVpn(); bool isConnected() const; bool isDisconnected() const; @@ -51,6 +48,12 @@ public: const QString &remoteAddress() const; +public slots: + void connectToVpn(int serverIndex, + const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig); + + void disconnectFromVpn(); + signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void connectionStateChanged(VpnProtocol::ConnectionState state); @@ -70,6 +73,7 @@ private: QJsonObject m_vpnConfiguration; QJsonObject m_routeMode; QString m_remoteAddress; + IpcClient *m_IpcClient {nullptr}; }; diff --git a/deploy/build_windows.bat b/deploy/build_windows.bat index bd83e9dc..88f29fc9 100644 --- a/deploy/build_windows.bat +++ b/deploy/build_windows.bat @@ -82,6 +82,7 @@ signtool sign /v /sm /s My /n "Privacy Technologies OU" /fd sha256 /tr http://ti echo "Copying deploy data..." xcopy %DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f +copy "%WORK_DIR:"=%\service\wireguard-service\release\wireguard-service.exe" %OUT_APP_DIR%\wireguard\ del %OUT_APP_DIR%\botand.dll diff --git a/deploy/data/windows/x32/wireguard/tunnel.dll b/deploy/data/windows/x32/wireguard/tunnel.dll new file mode 100644 index 00000000..016595ab Binary files /dev/null and b/deploy/data/windows/x32/wireguard/tunnel.dll differ diff --git a/deploy/data/windows/x32/wireguard/wireguard.dll b/deploy/data/windows/x32/wireguard/wireguard.dll new file mode 100644 index 00000000..b5b141ad Binary files /dev/null and b/deploy/data/windows/x32/wireguard/wireguard.dll differ diff --git a/deploy/data/windows/x64/wireguard/tunnel.dll b/deploy/data/windows/x64/wireguard/tunnel.dll new file mode 100644 index 00000000..310a4a31 Binary files /dev/null and b/deploy/data/windows/x64/wireguard/tunnel.dll differ diff --git a/deploy/data/windows/x64/wireguard/wg.exe b/deploy/data/windows/x64/wireguard/wg.exe deleted file mode 100644 index b69dfc41..00000000 Binary files a/deploy/data/windows/x64/wireguard/wg.exe and /dev/null differ diff --git a/deploy/data/windows/x64/wireguard/wireguard.dll b/deploy/data/windows/x64/wireguard/wireguard.dll new file mode 100644 index 00000000..8af4d087 Binary files /dev/null and b/deploy/data/windows/x64/wireguard/wireguard.dll differ diff --git a/deploy/data/windows/x64/wireguard/wireguard.exe b/deploy/data/windows/x64/wireguard/wireguard.exe deleted file mode 100644 index c63bd1c9..00000000 Binary files a/deploy/data/windows/x64/wireguard/wireguard.exe and /dev/null differ diff --git a/service/service.pro b/service/service.pro index 7fd85af1..e89166db 100644 --- a/service/service.pro +++ b/service/service.pro @@ -4,4 +4,7 @@ include(common.pri) qtservice-uselib:SUBDIRS=buildlib SUBDIRS+=server + win32 { + SUBDIRS+=wireguard-service + } } diff --git a/service/wireguard-service/main.cpp b/service/wireguard-service/main.cpp new file mode 100644 index 00000000..8e5f231e --- /dev/null +++ b/service/wireguard-service/main.cpp @@ -0,0 +1,31 @@ +#include "wireguardtunnelservice.h" +#include +#include + +int wmain(int argc, wchar_t** argv) +{ + if (argc != 3) { + debug_log(L"Wrong argument provided"); + return 1; + } + TCHAR option[20]; + TCHAR configFile[5000]; + + StringCchCopy(option, 20, argv[1]); + StringCchCopy(configFile, 5000, argv[2]); + + WireguardTunnelService tunnel(configFile); + + if (lstrcmpi(option, TEXT("--run")) == 0) { + debug_log(L"start tunnel"); + tunnel.startTunnel(); + } else if (lstrcmpi(option, TEXT("--add")) == 0) { + tunnel.addService(); + } else if (lstrcmpi(option, TEXT("--remove")) == 0) { + tunnel.removeService(); + } else { + debug_log(L"Wrong argument provided"); + return 1; + } + return 0; +} diff --git a/service/wireguard-service/wireguard-service.pro b/service/wireguard-service/wireguard-service.pro new file mode 100644 index 00000000..f248bbbf --- /dev/null +++ b/service/wireguard-service/wireguard-service.pro @@ -0,0 +1,23 @@ +TARGET = wireguard-service +TEMPLATE = app +CONFIG += console +CONFIG -= app_bundle +CONFIG -= qt +LIBS += \ + -luser32 \ + -lrasapi32 \ + -lshlwapi \ + -liphlpapi \ + -lws2_32 \ + -liphlpapi \ + -lgdi32 \ + -lAdvapi32 \ + -lKernel32 + +HEADERS = \ + wireguardtunnelservice.h + +SOURCES = \ + main.cpp \ + wireguardtunnelservice.cpp + diff --git a/service/wireguard-service/wireguardtunnelservice.cpp b/service/wireguard-service/wireguardtunnelservice.cpp new file mode 100644 index 00000000..9864038e --- /dev/null +++ b/service/wireguard-service/wireguardtunnelservice.cpp @@ -0,0 +1,160 @@ +#include "wireguardtunnelservice.h" +#include +#include +#include +#include +#include +#include +#include + + +void debug_log(const std::wstring& msg) +{ + std::wcerr << msg << std::endl; +} + +WireguardTunnelService::WireguardTunnelService(const std::wstring& configFile): + m_configFile{configFile} +{ +} + +void WireguardTunnelService::addService() +{ + SC_HANDLE scm; + SC_HANDLE service; + scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (NULL == scm) { + debug_log(L"OpenSCManager failed"); + return; + } + WCHAR szFileName[MAX_PATH]; + + GetModuleFileNameW(NULL, szFileName, MAX_PATH); + std::wstring runCommand = szFileName; + runCommand += TEXT(" --run "); + runCommand += m_configFile; + + debug_log(runCommand); + // check if service is already running + service = OpenServiceW( + scm, + SVCNAME, + SERVICE_ALL_ACCESS + ); + if (NULL != service) { + //service is already running, remove it before add new service + debug_log(L"service is already running, remove it before add new service"); + CloseServiceHandle(service); + removeService(); + } + service = CreateServiceW( + scm, + SVCNAME, + SVCNAME, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + runCommand.c_str(), + NULL, + NULL, + TEXT("Nsi\0TcpIp"), + NULL, + NULL); + if (NULL == service) { + debug_log(L"CreateServiceW failed"); + CloseServiceHandle(scm); + return; + } + SERVICE_SID_INFO info; + info.dwServiceSidType = SERVICE_SID_TYPE_UNRESTRICTED; + if (ChangeServiceConfig2W(service, + SERVICE_CONFIG_SERVICE_SID_INFO, + &info) == 0) { + debug_log(L"ChangeServiceConfig2 failed"); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return; + } + if (StartServiceW(service, 0, NULL) == 0) { + debug_log(L"StartServiceW failed"); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return; + } + if (DeleteService(service) == 0) { + debug_log(L"DeleteService failed"); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return; + } + CloseServiceHandle(service); + CloseServiceHandle(scm); +} + +void WireguardTunnelService::removeService() +{ + SC_HANDLE scm; + SC_HANDLE service; + scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (NULL == scm) { + debug_log(L"OpenSCManager failed"); + return; + } + service = OpenServiceW( + scm, + SVCNAME, + SERVICE_ALL_ACCESS + ); + if (NULL == service) { + debug_log(L"OpenServiceW failed"); + CloseServiceHandle(scm); + return; + } + SERVICE_STATUS stt; + if (ControlService(service, SERVICE_CONTROL_STOP, &stt) == 0) { + debug_log(L"ControlService failed"); + DeleteService(service); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return; + } + for (int i = 0; + i < 180 && QueryServiceStatus(scm, &stt) && stt.dwCurrentState != SERVICE_STOPPED; + ++i) { + std::this_thread::sleep_for(std::chrono::seconds{1}); + } + DeleteService(service); + CloseServiceHandle(service); + CloseServiceHandle(scm); +} + + +int WireguardTunnelService::startTunnel() +{ + debug_log(TEXT(__FUNCTION__)); + + HMODULE tunnelLib = LoadLibrary(TEXT("tunnel.dll")); + if (!tunnelLib) { + debug_log(L"Failed to load tunnel.dll"); + return 1; + } + + typedef bool WireGuardTunnelService(const LPCWSTR settings); + + WireGuardTunnelService* tunnelProc = (WireGuardTunnelService*)GetProcAddress( + tunnelLib, "WireGuardTunnelService"); + if (!tunnelProc) { + debug_log(L"Failed to get WireGuardTunnelService function"); + return 1; + } + + debug_log(m_configFile.c_str()); + + if (!tunnelProc(m_configFile.c_str())) { + debug_log(L"Failed to activate the tunnel service"); + return 1; + } + return 0; +} + diff --git a/service/wireguard-service/wireguardtunnelservice.h b/service/wireguard-service/wireguardtunnelservice.h new file mode 100644 index 00000000..3afd64c0 --- /dev/null +++ b/service/wireguard-service/wireguardtunnelservice.h @@ -0,0 +1,22 @@ +#ifndef WIREGUARDTUNNELSERVICE_H +#define WIREGUARDTUNNELSERVICE_H + +#include +#include + +#define SVCNAME TEXT("AmneziaVPNWireGuardService") + +class WireguardTunnelService +{ +public: + WireguardTunnelService(const std::wstring& configFile); + void addService(); + void removeService(); + int startTunnel(); +private: + std::wstring m_configFile; +}; + +void debug_log(const std::wstring& msg); + +#endif // WIREGUARDTUNNELSERVICE_H