diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 187150da..27d17c0d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -42,6 +42,13 @@ jobs: export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin bash deploy/build_linux.sh + - name: 'Upload artifact' + uses: actions/upload-artifact@v3 + with: + name: AmneziaVPN_Linux + path: deploy/AmneziaVPN_Linux_Installer + retention-days: 3 + # ------------------------------------------------------ Build-Windows: @@ -90,6 +97,13 @@ jobs: set QIF_BIN_DIR="${{ runner.temp }}\\Qt\\Tools\\QtInstallerFramework\\${{ env.QIF_VERSION }}\\bin" call deploy\\build_windows.bat + - name: 'Upload artifact' + uses: actions/upload-artifact@v3 + with: + name: AmneziaVPN_Windows + path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe + retention-days: 3 + # ------------------------------------------------------ Build-IOS: @@ -166,14 +180,6 @@ jobs: team-id: 'X7UJ388FXK' configuration: Release - - - - - - - - # ------------------------------------------------------ Build-MacOS: @@ -219,6 +225,13 @@ jobs: export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin" bash deploy/build_macos.sh + - name: 'Upload artifact' + uses: actions/upload-artifact@v3 + with: + name: AmneziaVPN_MacOS + path: AmneziaVPN.dmg + retention-days: 3 + # ------------------------------------------------------ Build-Android: diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 8aeddc32..1a1f052d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -292,8 +292,6 @@ endif() if(IOS) message("Client iOS build") - - find_package(Qt6 REQUIRED COMPONENTS ShaderTools) set(LIBS ${LIBS} Qt6::ShaderTools) @@ -346,7 +344,11 @@ qt_add_executable(${PROJECT} ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC}) qt_add_translations(${PROJECT} TS_FILES ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts) -# NETWORKEXTENSION=1 +if(APPLE AND NOT IOS) + set_target_properties(AmneziaVPN PROPERTIES + MACOSX_BUNDLE TRUE + ) +endif() if(IOS) enable_language(OBJC) diff --git a/client/client.pro b/client/client.pro index f365cbec..13383b7c 100644 --- a/client/client.pro +++ b/client/client.pro @@ -76,6 +76,7 @@ HEADERS += \ ui/pages_logic/protocols/OtherProtocolsLogic.h \ ui/pages_logic/protocols/PageProtocolLogicBase.h \ ui/pages_logic/protocols/ShadowSocksLogic.h \ + ui/pages_logic/protocols/WireGuardLogic.h \ ui/property_helper.h \ ui/models/servers_model.h \ ui/uilogic.h \ @@ -138,6 +139,7 @@ SOURCES += \ ui/pages_logic/protocols/PageProtocolLogicBase.cpp \ ui/pages_logic/protocols/ShadowSocksLogic.cpp \ ui/models/servers_model.cpp \ + ui/pages_logic/protocols/WireGuardLogic.cpp \ ui/uilogic.cpp \ ui/qautostart.cpp \ ui/models/sites_model.cpp \ diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index c158822d..c5f15d5b 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -58,6 +58,12 @@ constexpr char additional_server_config[] = "additional_server_config"; // proto config keys constexpr char last_config[] = "last_config"; + +constexpr char isThirdPartyConfig[] = "isThirdPartyConfig"; + +constexpr char openvpn[] = "openvpn"; +constexpr char wireguard[] = "wireguard"; + } namespace protocols { diff --git a/client/resources.qrc b/client/resources.qrc index 130857a7..7546d56b 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -63,6 +63,7 @@ server_scripts/website_tor/run_container.sh ui/qml/main.qml ui/qml/TitleBar.qml + ui/qml/Pages/PageBase.qml ui/qml/Pages/PageAppSetting.qml ui/qml/Pages/PageGeneralSettings.qml ui/qml/Pages/PageNetworkSetting.qml @@ -81,6 +82,32 @@ ui/qml/Pages/PageSites.qml ui/qml/Pages/PageStart.qml ui/qml/Pages/PageVPN.qml + ui/qml/Pages/PageQrDecoder.qml + ui/qml/Pages/PageAbout.qml + ui/qml/Pages/PageQrDecoderIos.qml + ui/qml/Pages/PageViewConfig.qml + ui/qml/Pages/PageClientManagement.qml + ui/qml/Pages/ClientInfo/PageClientInfoBase.qml + ui/qml/Pages/ClientInfo/PageClientInfoOpenVPN.qml + ui/qml/Pages/ClientInfo/PageClientInfoWireGuard.qml + ui/qml/Pages/Protocols/PageProtoCloak.qml + ui/qml/Pages/Protocols/PageProtoOpenVPN.qml + ui/qml/Pages/Protocols/PageProtoShadowSocks.qml + ui/qml/Pages/Protocols/PageProtoSftp.qml + ui/qml/Pages/Protocols/PageProtoTorWebSite.qml + ui/qml/Pages/Protocols/PageProtocolBase.qml + ui/qml/Pages/Protocols/PageProtoWireGuard.qml + ui/qml/Pages/InstallSettings/InstallSettingsBase.qml + ui/qml/Pages/InstallSettings/SelectContainer.qml + 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/Pages/Share/PageShareProtoAmnezia.qml + ui/qml/Pages/Share/PageShareProtoWireGuard.qml + ui/qml/Pages/Share/PageShareProtoIkev2.qml ui/qml/Controls/BasicButtonType.qml ui/qml/Controls/BlueButtonType.qml ui/qml/Controls/CheckBoxType.qml @@ -92,57 +119,40 @@ ui/qml/Controls/ShareConnectionButtonType.qml ui/qml/Controls/ShareConnectionContent.qml ui/qml/Controls/TextFieldType.qml - ui/qml/Pages/PageBase.qml - ui/qml/Config/GlobalConfig.qml - ui/qml/Config/qmldir - images/background_connected.png - images/background_connected@2x.png - ui/qml/Pages/Protocols/PageProtoCloak.qml - ui/qml/Pages/Protocols/PageProtoOpenVPN.qml - ui/qml/Pages/Protocols/PageProtoShadowSocks.qml - ui/qml/Controls/BackButton.qml - ui/qml/Pages/InstallSettings/InstallSettingsBase.qml - ui/qml/Controls/Caption.qml - ui/qml/Controls/Logo.qml - ui/qml/Pages/InstallSettings/SelectContainer.qml - ui/qml/Pages/Protocols/PageProtocolBase.qml - images/delete.png + ui/qml/Controls/RichLabelType.qml + ui/qml/Controls/SvgImageType.qml + ui/qml/Controls/FlickableType.qml + ui/qml/Controls/UrlButtonType.qml + ui/qml/Controls/TextAreaType.qml + ui/qml/Controls/ContextMenu.qml ui/qml/Controls/FadeBehavior.qml ui/qml/Controls/VisibleBehavior.qml + ui/qml/Controls/Caption.qml + ui/qml/Controls/Logo.qml + ui/qml/Controls/BackButton.qml + ui/qml/Controls/ShareConnectionButtonCopyType.qml + ui/qml/Controls/SvgButtonType.qml + ui/qml/Config/GlobalConfig.qml + ui/qml/Config/qmldir server_scripts/dns/configure_container.sh server_scripts/dns/Dockerfile server_scripts/dns/run_container.sh server_scripts/sftp/configure_container.sh server_scripts/sftp/Dockerfile server_scripts/sftp/run_container.sh - ui/qml/Pages/Protocols/PageProtoSftp.qml - ui/qml/Pages/Protocols/PageProtoTorWebSite.qml server_scripts/ipsec/configure_container.sh 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/background_connected.png + images/background_connected@2x.png + images/delete.png images/animation.gif images/connected.png images/disconnected.png - ui/qml/Pages/PageQrDecoder.qml - ui/qml/Pages/PageAbout.qml - ui/qml/Controls/RichLabelType.qml images/svg/gpp_good_black_24dp.svg - ui/qml/Controls/SvgImageType.qml images/svg/gpp_maybe_black_24dp.svg images/svg/close_black_24dp.svg images/svg/delete_black_24dp.svg @@ -156,13 +166,6 @@ images/svg/vpn_key_black_24dp.svg images/svg/control_point_black_24dp.svg images/svg/settings_suggest_black_24dp.svg - ui/qml/Controls/SvgButtonType.qml - ui/qml/Pages/PageQrDecoderIos.qml server_scripts/website_tor/Dockerfile - ui/qml/Pages/PageViewConfig.qml - ui/qml/Pages/PageClientManagement.qml - ui/qml/Pages/ClientInfo/PageClientInfoBase.qml - ui/qml/Pages/ClientInfo/PageClientInfoOpenVPN.qml - ui/qml/Pages/ClientInfo/PageClientInfoWireGuard.qml diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index 27f865b5..4a7d2343 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -29,6 +29,7 @@ void ServerContainersLogic::onUpdatePage() ProtocolsModel *p_model = qobject_cast(uiLogic()->protocolsModel()); p_model->setSelectedServerIndex(uiLogic()->selectedServerIndex); + set_isManagedServer(m_settings->haveAuthData(uiLogic()->selectedServerIndex)); emit updatePage(); } diff --git a/client/ui/pages_logic/ServerContainersLogic.h b/client/ui/pages_logic/ServerContainersLogic.h index 3f2a26cd..d0081516 100644 --- a/client/ui/pages_logic/ServerContainersLogic.h +++ b/client/ui/pages_logic/ServerContainersLogic.h @@ -19,6 +19,8 @@ public: Q_INVOKABLE void onPushButtonRemoveClicked(DockerContainer c); Q_INVOKABLE void onPushButtonContinueClicked(DockerContainer c, int port, TransportProto tp); + AUTO_PROPERTY(bool, isManagedServer) + public: explicit ServerContainersLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~ServerContainersLogic() = default; diff --git a/client/ui/pages_logic/ServerSettingsLogic.cpp b/client/ui/pages_logic/ServerSettingsLogic.cpp index 3b123174..22555a91 100644 --- a/client/ui/pages_logic/ServerSettingsLogic.cpp +++ b/client/ui/pages_logic/ServerSettingsLogic.cpp @@ -37,11 +37,17 @@ void ServerSettingsLogic::onUpdatePage() set_pushButtonShareFullVisible(m_settings->haveAuthData(uiLogic()->selectedServerIndex)); const QJsonObject &server = m_settings->server(uiLogic()->selectedServerIndex); const QString &port = server.value(config_key::port).toString(); - set_labelServerText(QString("%1@%2%3%4") - .arg(server.value(config_key::userName).toString()) - .arg(server.value(config_key::hostName).toString()) - .arg(port.isEmpty() ? "" : ":") - .arg(port)); + + const QString &userName = server.value(config_key::userName).toString(); + const QString &hostName = server.value(config_key::hostName).toString(); + QString name = QString("%1%2%3%4%5") + .arg(userName) + .arg(userName.isEmpty() ? "" : "@") + .arg(hostName) + .arg(port.isEmpty() ? "" : ":") + .arg(port); + + set_labelServerText(name); set_lineEditDescriptionText(server.value(config_key::description).toString()); DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex); diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index e899c514..76ba56fd 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -15,6 +15,36 @@ #include "platforms/android/android_controller.h" #endif +namespace { +enum class ConfigTypes { + Amnezia, + OpenVpn, + WireGuard +}; + +ConfigTypes checkConfigFormat(const QString &config) +{ + const QString openVpnConfigPatternCli = "client"; + const QString openVpnConfigPatternProto1 = "proto tcp"; + const QString openVpnConfigPatternProto2 = "proto udp"; + const QString openVpnConfigPatternDriver1 = "dev tun"; + const QString openVpnConfigPatternDriver2 = "dev tap"; + + const QString wireguardConfigPatternSectionInterface = "[Interface]"; + const QString wireguardConfigPatternSectionPeer = "[Peer]"; + + if (config.contains(openVpnConfigPatternCli) && + (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) && + (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { + return ConfigTypes::OpenVpn; + } else if (config.contains(wireguardConfigPatternSectionInterface) && + config.contains(wireguardConfigPatternSectionPeer)) + return ConfigTypes::WireGuard; + return ConfigTypes::Amnezia; +} + +} + StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), m_pushButtonConnectEnabled{true}, @@ -135,15 +165,22 @@ void StartPageLogic::onPushButtonImport() void StartPageLogic::onPushButtonImportOpenFile() { - QString fileName = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Open profile"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), tr("*.vpn")); + QString fileName = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Open config file"), + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn *.ovpn *.conf"); if (fileName.isEmpty()) return; QFile file(fileName); file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); - importConnectionFromCode(QString(data)); + auto configFormat = checkConfigFormat(QString(data)); + if (configFormat == ConfigTypes::OpenVpn) { + importConnectionFromOpenVpnConfig(QString(data)); + } else if (configFormat == ConfigTypes::WireGuard) { + importConnectionFromWireguardConfig(QString(data)); + } else { + importConnectionFromCode(QString(data)); + } } bool StartPageLogic::importConnection(const QJsonObject &profile) @@ -201,3 +238,90 @@ bool StartPageLogic::importConnectionFromQr(const QByteArray &data) return false; } + +bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) +{ + QJsonObject openVpnConfig; + openVpnConfig[config_key::config] = config; + + QJsonObject lastConfig; + lastConfig[config_key::last_config] = QString(QJsonDocument(openVpnConfig).toJson()); + lastConfig[config_key::isThirdPartyConfig] = true; + + QJsonObject containers; + containers.insert(config_key::container, QJsonValue("amnezia-openvpn")); + containers.insert(config_key::openvpn, QJsonValue(lastConfig)); + + QJsonArray arr; + arr.push_back(containers); + + QString hostName; + const static QRegularExpression hostNameRegExp("remote (.*) [0-9]*"); + QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(config); + if (hostNameMatch.hasMatch()) { + hostName = hostNameMatch.captured(1); + } + + QJsonObject o; + o[config_key::containers] = arr; + o[config_key::defaultContainer] = "amnezia-openvpn"; + o[config_key::description] = m_settings->nextAvailableServerName(); + + + const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); + QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(config); + if (dnsMatch.hasNext()) { + o[config_key::dns1] = dnsMatch.next().captured(1); + } + if (dnsMatch.hasNext()) { + o[config_key::dns2] = dnsMatch.next().captured(1); + } + + o[config_key::hostName] = hostName; + + return importConnection(o); +} + +bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) +{ + QJsonObject lastConfig; + lastConfig[config_key::config] = config; + + const static QRegularExpression hostNameAndPortRegExp("Endpoint = (.*):([0-9]*)"); + QRegularExpressionMatch hostNameAndPortMatch = hostNameAndPortRegExp.match(config); + QString hostName; + QString port; + if (hostNameAndPortMatch.hasMatch()) { + hostName = hostNameAndPortMatch.captured(1); + port = hostNameAndPortMatch.captured(2); + } + + QJsonObject wireguardConfig; + wireguardConfig[config_key::last_config] = QString(QJsonDocument(lastConfig).toJson()); + wireguardConfig[config_key::isThirdPartyConfig] = true; + wireguardConfig[config_key::port] = port; + wireguardConfig[config_key::transport_proto] = "udp"; + + QJsonObject containers; + containers.insert(config_key::container, QJsonValue("amnezia-wireguard")); + containers.insert(config_key::wireguard, QJsonValue(wireguardConfig)); + + QJsonArray arr; + arr.push_back(containers); + + QJsonObject o; + o[config_key::containers] = arr; + o[config_key::defaultContainer] = "amnezia-wireguard"; + o[config_key::description] = m_settings->nextAvailableServerName(); + + const static QRegularExpression dnsRegExp("DNS = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); + QRegularExpressionMatch dnsMatch = dnsRegExp.match(config); + if (dnsMatch.hasMatch()) { + o[config_key::dns1] = dnsMatch.captured(1); + o[config_key::dns2] = dnsMatch.captured(2); + } + + o[config_key::hostName] = hostName; + + return importConnection(o); +} diff --git a/client/ui/pages_logic/StartPageLogic.h b/client/ui/pages_logic/StartPageLogic.h index e7f4d557..183d0bd3 100644 --- a/client/ui/pages_logic/StartPageLogic.h +++ b/client/ui/pages_logic/StartPageLogic.h @@ -34,6 +34,8 @@ public: bool importConnection(const QJsonObject &profile); bool importConnectionFromCode(QString code); bool importConnectionFromQr(const QByteArray &data); + bool importConnectionFromOpenVpnConfig(const QString &config); + bool importConnectionFromWireguardConfig(const QString &config); public: explicit StartPageLogic(UiLogic *uiLogic, QObject *parent = nullptr); diff --git a/client/ui/pages_logic/ViewConfigLogic.cpp b/client/ui/pages_logic/ViewConfigLogic.cpp index 1dfe9abe..9ccd9d3e 100644 --- a/client/ui/pages_logic/ViewConfigLogic.cpp +++ b/client/ui/pages_logic/ViewConfigLogic.cpp @@ -13,6 +13,8 @@ void ViewConfigLogic::onUpdatePage() { set_configText(QJsonDocument(configJson()).toJson()); + auto s = configJson()[config_key::isThirdPartyConfig].toBool(); + m_openVpnLastConfigs = m_openVpnMalStrings = "