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 82b3b4fd..44d27db9 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 75a410d0..5f733905 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,28 @@
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/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 +115,41 @@
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/check_server_is_busy.sh
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,10 +163,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
- server_scripts/check_server_is_busy.sh
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 =
"