Compare commits

..

98 Commits

Author SHA1 Message Date
vkamn
cfe8641fd8 fix: context menu fixes for qt6.9 2025-11-20 10:10:49 +08:00
vkamn
3b3a0aaceb chore: bump version (#1997) 2025-11-18 00:22:58 +08:00
vkamn
01ec79b7d5 fix: news fetch (#1994)
* fix: fixed news nested qml call

* feat: async proxy bypass
2025-11-18 00:21:02 +08:00
vkamn
3d6339e2dd chore: bump version (#1989) 2025-11-14 13:59:47 +08:00
NickVs2015
b4d78d865a fix: fix android crash (#1988) 2025-11-14 13:57:52 +08:00
NickVs2015
b53cdcff08 fix: fix self-hosted TextFields and Keyboard reset issue (#1983)
Co-authored-by: vkamn <vk@amnezia.org>
2025-11-12 15:57:53 +08:00
vkamn
3cc18c5807 chore: bump version (#1982) 2025-11-11 23:03:24 +08:00
NickVs2015
5fdce1e49e fix: fix ui android issues (#1980)
* Fix UI issues

* Fix Screen Swipe
2025-11-11 22:03:27 +08:00
Yaroslav
2ee61a040b fix: iOS appstore publish fix (#1922) 2025-11-04 12:10:30 +08:00
vkamn
741b5cc0f9 fix: qt6 9 support (#1973)
* Fix qt 6.9 support

* add support android sdk 36

* feat: add support SafeMargins from Android

* Fix black screen

---------

Co-authored-by: NickVs2015 <nv@amnezia.org>
2025-11-04 11:43:36 +08:00
MrMirDan
aaf0e070dc fix: hide description (#1959) 2025-11-03 10:27:01 +08:00
vkamn
e0e126eda8 chore: bump version (#1969) 2025-11-03 10:26:33 +08:00
vkamn
236daf6b3b feat: ad label (#1966)
* refactor: ad label desing refatroing

* feat: add ad label settings processing

* chore: fix ru translations

* chore: minor fixes
2025-11-03 10:26:22 +08:00
vkamn
f1481b1b1f feat: add async post in gateway controller (#1963) 2025-10-29 23:24:24 +08:00
vkamn
f6e7d3ccf1 fix: minor ui fixes (#1917)
* feat: improve storage processing

* fix: minor ui fixes
2025-10-09 23:22:58 +08:00
Mitternacht822
a754a11913 fix: added displaying vpn_key field added in older version of the app (#1873)
* fix(api_key): added displaying vpn_key field added in older version of the app

* revert changes

* fix: implemented generation of api key text for PremiumV2

* fix: deleted unnecessary code

* saving apikey text when generating

* added method for vpn key export, fixed wrong saving file
2025-10-07 23:16:28 +08:00
vkamn
4d25e3b6f6 chore: minor bugfixes (#1915) 2025-10-07 23:15:06 +08:00
MrMirDan
1fac280497 fix: main app info added after clearing logs (#1913) 2025-10-06 21:07:04 +08:00
Yaroslav
c886c5e6a7 feat: enhance OpenVPN configuration handling and logging for iOS plat… (#1910)
* feat: enhance OpenVPN configuration handling and logging for iOS platform

* refactor: remove $OPENVPN_TA_KEY_SANITIZED and use $OPENVPN_TA_KEY instead
2025-10-06 21:04:49 +08:00
aiamnezia
cd7f78b9ca feat: news and notifications page (#1660)
* Add news and notifications

* Add localization for news and notifications

* Remove news caching

* Add fetching news befor openning news page

* Fix not updating news page

* Delete debug output

* Remove news and notificztions with only self-hosted servers

* Add stack filters to fetching news request

* Add fetching news with changing stack in the client

* small refactoring

* polishing

* Rename newsModel files and fix naming in code

* fix: remove custom signals; fetch news only on stack expansion

* chore: delete unnecessary code

* chore: code style fixes

* fix: fixed memory leak in gateway controller

---------

Co-authored-by: vkamn <vk@amnezia.org>
2025-10-06 12:06:36 +08:00
vkamn
a587d3230f fix: again fixed site link for features field (#1908) 2025-10-06 11:38:57 +08:00
MrMirDan
93e7b45136 fix: removed 'clear site list' button icon (#1909) 2025-10-06 11:37:42 +08:00
vkamn
e024f71ce1 fix: allow remove expired api configs (#1907) 2025-10-03 14:45:12 +08:00
MrMirDan
50d1be7b4a chore: update for RU translation (#1893) 2025-10-02 20:59:45 +08:00
MrMirDan
3ec6d8973b fix: warning visible only on windows (#1900) 2025-10-02 20:59:23 +08:00
Yaroslav Gurov
3ea47d31a9 fix: restore dns after using xray (#1902) 2025-10-02 20:58:53 +08:00
vkamn
30c8cc4548 feat: add isConnectEvent field to api request (#1896) 2025-09-30 12:10:27 +08:00
vkamn
98586d2dd9 fix: fixed site link (#1897) 2025-09-30 12:07:27 +08:00
vkamn
c66d8ecca0 chore: bump version (#1892) 2025-09-29 11:07:27 +08:00
vkamn
db535f7e7d chore: increase default values (#1891) 2025-09-29 11:05:30 +08:00
vkamn
89f30d8c31 fix: fixed native wg obfuscation (#1890) 2025-09-29 10:58:44 +08:00
Yaroslav
8bce432824 fix: enable paste from clipboard on ios in addition to android (#1868) 2025-09-29 10:56:41 +08:00
MrMirDan
f3539b2632 fix: proper wl name on connection key page (#1867)
* fix: proper wl name on connection key page

* some changings

* little change

* added missing import

* fix: proper wl default filename
2025-09-29 10:55:53 +08:00
MrMirDan
7a96c212f3 fix: rename user in search (#1847) 2025-09-29 10:51:52 +08:00
MrMirDan
2d5dc54e0f fix: keyboard navigation for text fields (#1879) 2025-09-29 10:50:57 +08:00
MrMirDan
cef4c262e9 fix: keyboard fix for api 'connection key' buttons (#1872) 2025-09-29 10:50:18 +08:00
MrMirDan
34309261a8 fix: scrollbar always visible (#1877)
* fix: scrollbar always visible

* fix: scrollbar always visible on app split tunneling page
2025-09-29 10:49:19 +08:00
MrMirDan
657eeb40c7 fix: mirror error code link (#1863)
* fix: mirror error code link

* remake
2025-09-29 10:48:36 +08:00
MrMirDan
b4938c2cc9 fix: default lang matching between app and OS (#1855)
* fix: default lang matching between app and OS

* remake

* fix: set default lang value
2025-09-29 10:47:54 +08:00
MrMirDan
524fefc5cb feat: warning on app split tunneling for windows (#1880) 2025-09-29 10:45:14 +08:00
Yaroslav
73f13404bb feat: add support for multiple scenes and handle URL contexts in iOS 13+ (#1889) 2025-09-29 10:40:58 +08:00
MrMirDan
5fc68cca83 fix: split tunneling restoration from backup (#1835) 2025-09-15 10:55:18 +08:00
Mitternacht822
fcb7b8fa8d fix: save/restore AmneziaDNS state (#1833) 2025-09-15 10:54:34 +08:00
aiamnezia
a81e32ff95 fix: clean service/client logs in uninstall scripts (#1846)
- Windows (x64/x86):
  - Remove delegation to `AmneziaVPN.exe -c`
  - Delete `%ProgramData%\AmneziaVPN\log\AmneziaVPN-service.log`
  - Delete current user logs at `%AppData%\AmneziaVPN.ORG\AmneziaVPN\log`
  - Remove empty parent dirs (app/org, log)

- Linux:
  - Delete only `/var/log/AmneziaVPN/AmneziaVPN-service.log` (preserve `post-uninstall.log`)
  - Delete current user logs at `$HOME/.local/share/AmneziaVPN.ORG/AmneziaVPN/log`
2025-09-15 10:53:51 +08:00
albexk
c897052107 chore: bump version (#1850) 2025-09-10 19:28:36 +08:00
vkamn
4d0efc7ea5 fix: remove duplicate m_vpnConnection delete from AmneziaApplication destructor (#1848) 2025-09-10 15:01:52 +08:00
Ivan
a77842c9e3 feat: add server diagnostics script (#1837)
Co-authored-by: Ivan Istomin <istomin-ms@yandex.ru>
2025-09-09 19:33:35 +08:00
Mitternacht822
0ded9db780 refactor: use QCommandLineOption members for autostart/cleanup (#1820)
* refactor(app options): use QCommandLineOption members for autostart/cleanup

* fix(app): initialize QCommandLineOption members in ctor/field to avoid no-default-ctor build failures
2025-09-03 12:03:45 +08:00
Mitternacht822
58d480fcb5 fix: moved startMinimized to Q_Property (#1819) 2025-09-03 12:03:10 +08:00
aiamnezia
7154428d26 fix: sharing QR code size (#1830) 2025-09-03 11:58:36 +08:00
MrMirDan
02a52d0169 fix: full config default filename (#1831) 2025-09-03 11:57:30 +08:00
MrMirDan
ec60764072 fix: rename/revoke user while in search on share page (#1787)
* fix: revoke user config

* fix: user renaming

* fix: revoke signal

* some fixes

* remaded fix
2025-09-03 11:56:08 +08:00
MrMirDan
17d2fa5532 fix: premium key duplication (#1818)
* ru translation fix

* crc saving

* little fix

* updated crc saving

* fix: added comparison by key

* remaded fix
2025-09-03 11:54:11 +08:00
MrMirDan
3ca8b534e8 fix: go to home page after first protocol manual installation (#1829) 2025-09-03 11:52:45 +08:00
MrMirDan
e88f7c5e46 fix: index assignment (#1821) 2025-09-02 13:03:05 +08:00
MrMirDan
3ac5d7bd1f chore: ru translation update (#1815) 2025-08-27 18:37:43 +08:00
vkamn
19cad00a00 fix: minor ui fixes (#1817)
* fix: minor ui fixes with services list

* fix: fix page share connection headers and config description
2025-08-27 16:42:28 +08:00
vkamn
1ea716a163 fix: fix page share connection headers and config description 2025-08-27 16:41:20 +08:00
vkamn
4551659c2a fix: minor ui fixes with services list 2025-08-27 15:15:53 +08:00
MrMirDan
c568bf8c24 chore: ru translation update (#1812)
* ru translation update

* fixes
2025-08-26 20:32:00 +08:00
vkamn
a412d91105 feat: subscription expiration processing (#1814) 2025-08-26 20:31:41 +08:00
vkamn
ad01f23bbe feat: add service description customization (#1811) 2025-08-26 12:17:37 +08:00
vkamn
656070b132 feat: add request id (#1809) 2025-08-25 22:05:00 +08:00
MrMirDan
c907f5ca36 fix: removed service logs section for mobile platforms (#1810) 2025-08-25 22:04:48 +08:00
Mykola Baibuz
94a13b2b54 fix: set guid for windows tun2socks tun interface (#1808) 2025-08-25 11:03:42 +08:00
MrMirDan
169f11d9c7 chore: added trimming I's and J's params on save (#1774)
* trimming params on save

* removed unused code
2025-08-21 12:29:22 +08:00
vkamn
816dc3af95 feat: add ping before request to proxy (#1805) 2025-08-21 12:28:03 +08:00
Mykola Baibuz
b802863de5 fix: check for empty secondary DNS (#1799) 2025-08-20 14:19:22 +08:00
vkamn
8dc2a4b76c fix: fixed switcher behavior (#1801) 2025-08-20 13:01:09 +08:00
vkamn
beb1c6dbf2 feat: added cache for proxy bypass (#1797) 2025-08-20 13:00:35 +08:00
vkamn
3eb06916c7 chore: bump version (#1802)
* chore: bump version

* fix: fixed ios build
2025-08-20 13:00:20 +08:00
Cyril Anisimov
30d0f84a4f fix: fixed focus view and reverse focus change in headers (#1791)
* fix: add view movement on changing the focus in backwards direction

* fix: return value in isFirstFocusItemInHeader function
2025-08-20 12:59:57 +08:00
Mykola Baibuz
251f2aa5db fix: remove double disconnect for Win IPSec (#1800) 2025-08-20 12:58:39 +08:00
Nethius
16d92ddb7c fix: UI fixes after merge with d20ed4a (#1779)
* fix: ui fixes after merge with d20ed4a

* update OpenVPN settings page

* chore: page settings dns margins

---------

Co-authored-by: Cyril Anisimov <CyAn84@gmail.com>
2025-08-11 13:40:28 +08:00
Cyril Anisimov
e9d4fd8482 fix checkbox switch (#1777) 2025-08-10 11:13:58 +08:00
Yaroslav
9fdcf5ab13 feat: macos with network extension Implementation (#1468)
* There's a common issue of building iOS apps on Qt 6.8 because of new introduced ffmpeg dependency in multimedia Qt package
ref: https://community.esri.com/t5/qt-maps-sdk-questions/build-failure-on-ios-with-qt-6-8/m-p/1548701#M5339

* Cmake related changes

* Source code changes

* Various entitlements

* Ci-cd config update

* Resources changes

* Submodules updated

* Remove me

* QtWidget exclusion omitted

* Distribution errors fixed

* Outdated files deleted

* macos_ne cmake fixed

* fix: update provisioning profile specifiers for macOS network extension

* fix: update provisioning profile specifiers and code sign flags for macOS build

* Revert me
(temporary 3rd-build commit pointer)

* fix: Welcome screen fix

* fix: ci/cd hanging forever fix

* fix: Fixed error popup on macos on file save

* refactor: rename networkextension target to AmneziaVPNNetworkExtension in macos build configuration

* feat: add autostart support for Mac App Store builds on macOS

Fixes: QA-8

* feat: add debug logging to Autostart functionality on macOS

* Revert "feat: add autostart support for Mac App Store builds on macOS"

This reverts commit 3bd25656fb.

* feat: add platform-specific close window behavior for macOS App Store build with Network Extension

Closes: QA-12

* When the application starts with "Start minimized" enabled on macOS (especially the
sandboxed App-Store build compiled with MACOS_NE), fully hiding the window prevents it
from being restored by clicking the Dock icon. The proper behaviour is to start the
window in the *minimized* state instead. That way the window is still part of the
window list and the system automatically brings it back when the user clicks the Dock
icon, replicating the native experience.

On the other platforms we keep the old behaviour (hide the window completely and rely
on the tray icon), therefore we switch at runtime by checking the current OS.

Closes: QA-7

Closes: QA-8

* Revert "When the application starts with "Start minimized" enabled on macOS (especially the"

This reverts commit 7b0d17987c.

* feat: MACOS_NE systray menu support

* feat: add macOS notification handler and install event filter on main window

* feat: implement custom close behavior for Amnezia application on different platforms

* fix: update provisioning profile specifiers for macos builds

* fix: Fatal error in logs

CLI-216

* fix: disabled unavailable on macos ne service logs

* fix: dock icon now hides only when window is closed; menubar icon shows always

Initial state of the docker icon to be presented follows "Start minimized" setting in app settings.

* temp-fix: temporary disable all OpenVPN options of VPN on MACOS_NE since it's not working yet.

* fix: build script updated

* feat: add macOS NE build workflow to GitHub Actions

* fix: Not working Auto start toggle is hidden

* fix: Log spamming during xray connection fixed

* 3rd-prebuild points to commit that stores macos_ne universal binaries.

* fix: missing native dependency on linking stage fixed

* chore: update link to submodule

---------

Co-authored-by: vladimir.kuznetsov <nethiuswork@gmail.com>
2025-08-10 11:12:19 +08:00
serj95reg
a6e6de33c8 feat: updated xray version in dockerfile to 25.8.3 (#1771) 2025-08-08 10:34:51 +08:00
Mitternacht822
53c7fd4d81 fix: android build (#1768)
* added signal-slot connection between corecontroller and systemtraynofificationhandler updating websiteurl

* cleared up the commented lines

* fixed andorid includes for systemtraynotificationhandler
2025-08-07 11:12:09 +08:00
Nethius
2608ea4367 chore: fix typo (#1769) 2025-08-06 11:00:43 +08:00
Cyril Anisimov
d20ed4ad01 refactoring: improved stability of focus controller (#1464)
* change position view mode

* remove `parentFlickable` from `PageShare`

* replace `FlickableType` with `ListViewType` in `PageSettings`

* reorganize `PageSettingsAbout` for improved structure

* replace `Flickable` with `ListViewType` in drawer in `PageSettingsApiNativeConfigs`

* replace `FlickableType` with `ListViewType` in `PageSettingsApplication` and update layout structure

* replace `FlickableType` with `ListViewType` in `PageSettingsAppSplitTunneling` and adjust layout for better structure

* replace `FlickableType` with `ListViewType` in `PageSettingsBackup`

* replace `FlickableType` with `ListViewType` in `PageSettingsConnection`

* replace `FlickableType` with `ListViewType` in `PageSettingsDns`

* replace `FlickableType` with `ListViewType` in `PageSettingsLogging`

* replace `FlickableType` with `ListViewType` in `PageSettingsServerData`

* update structure of `PageSettingsServerProtocol`

* update `PageSettingsServersList`

* replace `ListView` with `ListViewType` in `PageSettingsSplitTunneling`

* replace `FlickableType` with `ListViewType` in `PageServiceDnsSettings`

* update `PageServiceSftpSettings`

* update `PageServiceSocksProxySettings`

* replace `FlickableType` with `ListViewType` in `PageServiceTorWebsiteSettings`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardApiServiceInfo`

* update `PageSetupWizardApiServicesList`

* replace `ListView` with `ListViewType` in `PageSetupWizardConfigSource`

* replace `ListView` with `ListViewType` in `PageSetupWizardCredentials`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardEasy`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardInstalling`

* replace `ListView` with `ListViewType` in `PageSetupWizardProtocols`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardProtocolSettings`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardTextKey`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardViewConfig`

* update `PageProtocolAwgClientSettings`

* update `PageProtocolAwgSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolCloakSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolRaw`

* replace `FlickableType` with `ListViewType` in `PageProtocolShadowSocksSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolWireGuardClientSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolWireGuardSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolXraySettings`

* replace `FlickableType` with `ListViewType` in `PageShareFullAccess`

* replace `FlickableType` with `ListViewType` in `PageDeinstalling`

* update `PageDevMenu`

* remove `Flickable` references in `LabelWithButtonType`

* remove useless key navigation handlers from `ListViewType`

* replace `ListView` with `ListViewType` in `ListViewWithRadioButtonType.qml` and remove unnecessary properties

* remove references to `Flickable` in `TextAreaType.qml`

* remove references to `Flickable` in `TextAreaWithFooterType`

* remove references to `FlickableType` in `TextFieldWithHeaderType`

* remove references to `FlickableType` in `SwitcherType`

* remove references to `FlickableType` in `CheckBoxType`

* remove references to `FlickableType` in `CardWithIconsType.qml`

* remove references to `FlickableType` in `BasicButtonType.qml`

* update `ServersListView`

* update `SettingsContainersListView`

* update `InstalledAppsDrawer`

* update `SelectLanguageDrawer`

* update `HomeContainersListView`

* update `HomeSplitTunnelingDrawer`

* fix `PageSetupWizardApiServicesList`

---------

Co-authored-by: vladimir.kuznetsov <nethiuswork@gmail.com>
2025-08-06 10:35:51 +08:00
KsZnak
eae2936449 Update README links.md [no ci]
Update README links.md
2025-08-04 19:35:45 +01:00
KsZnak
da8ad1f6ba UTM added.md [no ci]
Update README_RU.md
2025-08-04 19:34:12 +01:00
Mitternacht822
5472347969 feature: added warning label when config files have changed in premium configuration files menu (#1718)
* added warning label when config files have changed in premium configuration files display

* moved warning display from PageSettingsApiNativeConfigs.qml to PageSettingsApiServerInfo.qml
2025-08-04 14:13:22 +08:00
Mitternacht822
a43f7a6926 feat: added vpn key to subscription settings page (#1717)
* added subscription key display element to subscription management page

* refactrored KeySubscription item to a new page

* minor fix

* changed PageShareDrawer into PageShareConnection

* added back button

* Removed deprecated ShareConnectionDrawer and migrated to PageShareConnection

* fixed issue when show-connection settings button was not working

* deleted empty lines

* minor fix
2025-08-01 21:02:12 +08:00
Cyril Anisimov
47f917de0b refactoring: change logs time to UTC (#1578)
* update logger to show utc

* add logger to `FocusController`

* add utc timestamps to android logs
2025-08-01 11:56:16 +08:00
Cyril Anisimov
dbeb7edd7a refactor: update ScrollBar policy to use AsNeeded for better usability (#1579)
* refactor: update `ScrollBarType` policy to use
`AsNeeded` for better usability

* add selecting of location settings with Enter

* add handlers to enter push
2025-08-01 11:56:02 +08:00
Mitternacht822
6cede712f5 fix: backup contains platform specific variables (#1646)
* fixed issue with restoring wrong platform specific variables in backup

* fixed wrong line

* fixed issue when restong app split tunneling mode not intended for windows platform

* added field containing application platform to backup file, added feature to clear appsSplitTunneling list from backup file if backup was made on other platform
2025-08-01 11:54:58 +08:00
Mitternacht822
d328739192 fix: add update model after clear profile (#1674)
* fixed issue when ui was not getting update about clearing profile cache right after it

* fixed the problem of not clearing the profile

* refactored reload function in protolocolsModel

* refactored the issue with signal connect in corecontroller
2025-08-01 11:52:11 +08:00
Mitternacht822
d15c0bd962 fix: fixed system tray open site link (#1686)
* added signal-slot connection between corecontroller and systemtraynofificationhandler updating websiteurl

* cleared up the commented lines
2025-08-01 11:50:31 +08:00
Mitternacht822
d53c794936 fix: fixed language load after settings reset (#1735) 2025-08-01 11:47:43 +08:00
Mitternacht822
e5dcb25a4a fix: removed the ability to change location while making connection (#1736) 2025-08-01 11:45:19 +08:00
Mitternacht822
f9002b4f43 refactoring: made start-minimized-option available only when autostart-option is truned on (#1740) 2025-08-01 11:38:36 +08:00
Nethius
0531508a75 feat: added 'clear site list' button (#1747) (#1753)
* feat: added 'clear site list' button (#1747)

* chore: rename 'Export/Import Sites' to 'Additional options'

---------

Co-authored-by: MrMirDan <58086007+MrMirDan@users.noreply.github.com>
2025-08-01 11:37:56 +08:00
Mitternacht822
174e85a20a fix: not restoring parameters for open vpn after scanning server (#1759)
* added lines for restoring settings when scanning server for OpenVPN, OpenVPN over Cloak and OpenVPN over SS protocols

* minor fix

* added functionality to restore config for multiprotocol configsCloak and Shadowsocks
2025-08-01 11:36:52 +08:00
MrMirDan
e9abb6f1e2 fix: mirror links (#1760)
* Instructions links

* amnezia free feature link

* trying fix api instructions page issue

* androidTV link fix

* tv link fix 2
2025-08-01 11:36:30 +08:00
Nethius
5be44f9596 chore: bump version (#1757)
* chore: update link to submodule

* chore: bump version
2025-07-29 12:20:43 +08:00
vladimir.kuznetsov
90efaaff92 chore: bump version 2025-07-29 12:19:54 +08:00
vladimir.kuznetsov
99b554e7c3 chore: update link to submodule 2025-07-29 12:19:27 +08:00
369 changed files with 17925 additions and 18429 deletions

View File

@@ -1,69 +0,0 @@
# Amnezia VPN Client - MVVM Refactoring Rules
## Architecture Requirements
### MVVM Pattern
- Follow Model-View-ViewModel architecture strictly
- Maintain clear separation of concerns between layers
- UI controllers should only handle user input and delegate to core controllers
- Core controllers contain all business logic and data manipulation
### Dependencies Direction
- UI layer depends on Core layer (unidirectional dependency)
- UI controllers must not depend on each other
- Core controllers must not depend on each other (exceptions: ServerController, VpnConfigurationsController, GatewayController can be created "on the fly")
### Settings Usage
- UI classes must NOT use Settings class directly
- Create wrapper methods in core controllers for Settings operations
### Server Controller Creation
- ServerController creation must happen in Core controllers, never in UI
- Remove direct instantiation: `new ServerController(m_settings)` from UI code
## Code Organization
### Folder Structure
- Core controllers: `client/core/controllers/`
- Core models: `client/core/models/`
- UI controllers: `client/ui/controllers/`
- UI models: `client/ui/models/`
- Common models and controllers in root of respective folders
### Controller Management
- All controllers must be instantiated in central `coreController`
- Dependencies between core controllers resolved in `coreController`
- UI controllers accept core controllers via constructor injection
- UI controllers should be renamed with "UI" suffix (e.g., `exportUIController`)
## Code Style
### Comments
- Do NOT include comments in code
- Code should be self-documenting
### Models Behavior
- Models should only display data
- Keep core models as pure data structures without business logic
## Refactoring Process
### UI Controller Refactoring
- Remove all business logic from UI controllers
- Replace direct method calls with delegation to core controllers
- Maintain only UI-specific logic (signals, model updates)
- Remove Settings dependencies
### Core Controller Pattern
- Create corresponding methods in core controllers for UI operations
- Core controllers handle all complex logic, network operations, file I/O
- Return structured results with error codes and data
- Manage ServerController lifecycle internally
## Git Workflow
- Use `git mv` for renaming controllers to maintain history
## Error Handling
- Core controllers return structured error results
- UI controllers only handle error presentation
- Maintain consistent error propagation patterns

View File

@@ -358,7 +358,7 @@ jobs:
- name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '15.4.0'
xcode-version: '16.2.0'
- name: 'Install Qt'
uses: jurplel/install-qt-action@v3
@@ -402,14 +402,75 @@ jobs:
path: deploy/build/client/AmneziaVPN.app
retention-days: 7
Build-MacOS-NE:
runs-on: macos-latest
env:
QT_VERSION: 6.8.3
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
steps:
- name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.2.0'
- name: 'Install Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'mac'
target: 'desktop'
arch: 'clang_64'
modules: 'qtremoteobjects qt5compat qtshadertools'
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Get sources'
uses: actions/checkout@v4
with:
submodules: 'true'
fetch-depth: 10
- name: 'Setup ccache'
uses: hendrikmuhs/ccache-action@v1.2
- name: 'Build project'
run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
bash deploy/build_macos_ne.sh
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_MacOS_unpacked
path: deploy/build/client/AmneziaVPN.app
retention-days: 7
# ------------------------------------------------------
Build-Android:
runs-on: ubuntu-latest
env:
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.7.3
ANDROID_BUILD_PLATFORM: android-36
QT_VERSION: 6.8.3
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ deploy/build_32/*
deploy/build_64/*
winbuild*.bat
.cache/
.vscode/
# Qt-es

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.9.1)
set(AMNEZIAVPN_VERSION 4.8.11.4)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
DESCRIPTION "AmneziaVPN"
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2091)
set(APP_ANDROID_VERSION_CODE 2099)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
@@ -29,16 +29,22 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
endif()
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(APPLE AND NOT IOS)
set(CMAKE_OSX_ARCHITECTURES "x86_64")
if(APPLE)
if(IOS)
set(CMAKE_OSX_ARCHITECTURES "arm64")
elseif(MACOS_NE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
else()
set(CMAKE_OSX_ARCHITECTURES "x86_64")
endif()
endif()
add_subdirectory(client)
if(NOT IOS AND NOT ANDROID)
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
add_subdirectory(service)
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)

View File

@@ -9,17 +9,17 @@
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
### [Website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
> If the [Amnezia website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror).
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
<a href="https://amnezia.org/en/downloads?utm_source=github&utm_campaign=amnezia_button-readme-en"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/en/downloads&utm_source=github&utm_campaign=amnezia_button-readme-en-mirrow"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)

View File

@@ -6,16 +6,16 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
### [Сайт](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
> Если [сайт Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror).
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/ru/downloads&utm_source=github&utm_campaign=amnezia_button-readme-ru-mirror"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)

View File

@@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
@@ -53,6 +52,9 @@ endif()
qt_standard_project_setup()
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
target_include_directories(${PROJECT} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
@@ -110,6 +112,15 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}
)
if(MACOS_NE)
message("MACOS_NE is ON")
add_definitions(-DQ_OS_MAC)
add_definitions(-DMACOS_NE)
message("Add macros for MacOS Network Extension")
else()
message("MACOS_NE is OFF")
endif()
include_directories(mozilla)
include_directories(mozilla/shared)
include_directories(mozilla/models)
@@ -139,7 +150,7 @@ if(WIN32)
endif()
if(APPLE)
cmake_policy(SET CMP0099 OLD)
cmake_policy(SET CMP0099 NEW)
cmake_policy(SET CMP0114 NEW)
if(NOT BUILD_OSX_APP_IDENTIFIER)
@@ -158,7 +169,6 @@ if(APPLE)
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
endif()
if(LINUX AND NOT ANDROID)
@@ -166,8 +176,7 @@ if(LINUX AND NOT ANDROID)
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
message("Client desktop build")
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
add_compile_definitions(AMNEZIA_DESKTOP)
endif()
@@ -178,7 +187,9 @@ endif()
if(IOS)
include(cmake/ios.cmake)
include(cmake/ios-arch-fixup.cmake)
elseif(APPLE AND NOT IOS)
elseif(APPLE AND MACOS_NE)
include(cmake/macos_ne.cmake)
elseif(APPLE)
include(cmake/osxtools.cmake)
include(cmake/macos.cmake)
endif()
@@ -199,7 +210,7 @@ elseif(APPLE AND NOT IOS)
set(DEPLOY_PLATFORM_PATH "macos")
endif()
if(NOT IOS AND NOT ANDROID)
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
add_custom_command(
TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
@@ -214,7 +225,6 @@ if(NOT IOS AND NOT ANDROID)
$<TARGET_FILE_DIR:${PROJECT}>
COMMAND_EXPAND_LISTS
)
endif()
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})

View File

@@ -12,6 +12,7 @@
#include <QTextDocument>
#include <QTimer>
#include <QTranslator>
#include <QEvent>
#include "logger.h"
#include "ui/controllers/pageController.h"
@@ -21,8 +22,12 @@
#include "platforms/ios/QRCodeReaderBase.h"
#include "protocols/qml_register_protocols.h"
#include <QtQuick/QQuickWindow> // for QQuickWindow
#include <QWindow> // for qobject_cast<QWindow*>
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv),
m_optAutostart({QStringLiteral("a"), QStringLiteral("autostart")}, QStringLiteral("System autostart")),
m_optCleanup ({QStringLiteral("c"), QStringLiteral("cleanup")}, QStringLiteral("Cleanup logs"))
{
setQuitOnLastWindowClosed(false);
@@ -49,7 +54,6 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
AmneziaApplication::~AmneziaApplication()
{
m_vpnConnectionThread.quit();
m_vpnConnectionThread.wait(3000);
if (m_engine) {
QObject::disconnect(m_engine, 0, 0, 0);
@@ -63,15 +67,28 @@ void AmneziaApplication::init()
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
QObject::connect(
m_engine, &QQmlApplicationEngine::objectCreated, this,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
m_engine, &QQmlApplicationEngine::objectCreated, this,
[this, url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl) {
QCoreApplication::exit(-1);
return;
}
// install filter on main window
if (auto win = qobject_cast<QQuickWindow*>(obj)) {
win->installEventFilter(this);
win->show();
}
},
Qt::QueuedConnection);
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
#ifdef MACOS_NE
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", true);
#else
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
#endif
m_vpnConnection.reset(new VpnConnection(m_settings));
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
@@ -94,7 +111,7 @@ void AmneziaApplication::init()
Logger::setServiceLogsEnabled(enabled);
#ifdef Q_OS_WIN //TODO
if (m_parser.isSet("a"))
if (m_parser.isSet(m_optAutostart))
m_coreController->pageController()->showOnStartup();
else
emit m_coreController->pageController()->raiseMainWindow();
@@ -162,15 +179,12 @@ bool AmneziaApplication::parseCommands()
m_parser.addHelpOption();
m_parser.addVersionOption();
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
m_parser.addOption(c_autostart);
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
m_parser.addOption(c_cleanup);
m_parser.addOption(m_optAutostart);
m_parser.addOption(m_optCleanup);
m_parser.process(*this);
if (m_parser.isSet(c_cleanup)) {
if (m_parser.isSet(m_optCleanup)) {
Logger::cleanUp();
QTimer::singleShot(100, this, [this] { quit(); });
exec();
@@ -179,9 +193,8 @@ bool AmneziaApplication::parseCommands()
return true;
}
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
void AmneziaApplication::startLocalServer()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
void AmneziaApplication::startLocalServer() {
const QString serverName("AmneziaVPNInstance");
QLocalServer::removeServer(serverName);
@@ -198,6 +211,22 @@ void AmneziaApplication::startLocalServer()
}
#endif
bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::Close) {
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
quit();
#else
if (m_coreController && m_coreController->pageController()) {
m_coreController->pageController()->hideMainWindow();
}
#endif
return true; // eat the close
}
// call base QObject::eventFilter
return QObject::eventFilter(watched, event);
}
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{
return m_engine;

View File

@@ -7,9 +7,9 @@
#include <QQmlContext>
#include <QThread>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#include <QGuiApplication>
#else
#include <QApplication>
#include <QApplication>
#endif
#include <QClipboard>
@@ -20,9 +20,9 @@
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#define AMNEZIA_BASE_CLASS QGuiApplication
#define AMNEZIA_BASE_CLASS QGuiApplication
#else
#define AMNEZIA_BASE_CLASS QApplication
#define AMNEZIA_BASE_CLASS QApplication
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
@@ -37,7 +37,7 @@ public:
void loadFonts();
bool parseCommands();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
void startLocalServer();
#endif
@@ -56,10 +56,15 @@ private:
QCommandLineParser m_parser;
QCommandLineOption m_optAutostart;
QCommandLineOption m_optCleanup;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;
QNetworkAccessManager *m_nam;
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
};
#endif // AMNEZIA_APPLICATION_H

View File

@@ -45,7 +45,8 @@
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:launchMode="singleInstance"
android:windowSoftInputMode="stateUnchanged|adjustResize"
android:windowSoftInputMode="adjustResize|stateUnchanged"
android:enableOnBackInvokedCallback="false"
android:exported="true">
<intent-filter>
@@ -214,4 +215,4 @@
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
</provider>
</application>
</manifest>
</manifest>

View File

@@ -6,6 +6,9 @@
<item name="android:colorBackground">@color/black</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="android:enforceStatusBarContrast">false</item>
</style>
<style name="Translucent" parent="NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>

View File

@@ -35,6 +35,11 @@ import android.widget.Toast
import androidx.annotation.MainThread
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.graphics.Insets
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import java.io.IOException
import kotlin.LazyThreadSafetyMode.NONE
import kotlin.coroutines.CoroutineContext
@@ -170,10 +175,9 @@ class AmneziaActivity : QtActivity() {
super.onCreate(savedInstanceState)
Log.d(TAG, "Create Amnezia activity")
loadLibs()
window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
statusBarColor = getColor(R.color.black)
}
// Configure window for edge-to-edge display
configureWindowForEdgeToEdge()
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
val proto = mainScope.async(Dispatchers.IO) {
VpnStateStore.getVpnState().vpnProto
@@ -265,6 +269,82 @@ class AmneziaActivity : QtActivity() {
super.onStop()
}
override fun onResume() {
super.onResume()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
window.decorView.apply {
invalidate()
postDelayed({
sendTouch(1f, 1f)
}, 100)
postDelayed({
sendTouch(2f, 2f)
}, 200)
postDelayed({
requestLayout()
invalidate()
}, 250)
}
}
}
private fun configureWindowForEdgeToEdge() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
addFlags(LayoutParams.FLAG_LAYOUT_NO_LIMITS)
statusBarColor = android.graphics.Color.TRANSPARENT
navigationBarColor = android.graphics.Color.TRANSPARENT
}
WindowInsetsControllerCompat(window, window.decorView).apply {
isAppearanceLightStatusBars = false
isAppearanceLightNavigationBars = false
}
// Workaround for Android 14 (API 34+) IME adjustResize bug
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
setupImeInsetsListener()
}
} else {
window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
statusBarColor = getColor(R.color.black)
}
}
}
private fun setupImeInsetsListener() {
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, windowInsets ->
val imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
val imeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = if (imeVisible) imeInsets.bottom else 0
val density = resources.displayMetrics.density
val imeHeightDp = (imeHeight / density).toInt()
// Also track system bars (navigation bar, status bar) changes
val systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val navBarHeight = systemBarsInsets.bottom
val navBarHeightDp = (navBarHeight / density).toInt()
val statusBarHeight = systemBarsInsets.top
val statusBarHeightDp = (statusBarHeight / density).toInt()
mainScope.launch {
qtInitialized.await()
QtAndroidController.onImeInsetsChanged(imeHeightDp)
QtAndroidController.onSystemBarsInsetsChanged(navBarHeightDp, statusBarHeightDp)
}
// Return windowInsets instead of CONSUMED to allow proper handling
windowInsets
}
}
override fun onDestroy() {
Log.d(TAG, "Destroy Amnezia activity")
unregisterBroadcastReceiver(notificationStateReceiver)
@@ -666,6 +746,43 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused")
fun isOnTv(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
@Suppress("unused")
fun isEdgeToEdgeEnabled(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
@Suppress("unused")
fun getStatusBarHeight(): Int {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) return 0
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
val heightPx = if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else {
0
}
// Convert physical pixels to device-independent pixels for QML
val density = resources.displayMetrics.density
val heightDp = (heightPx / density).toInt()
return heightDp
}
@Suppress("unused")
fun getNavigationBarHeight(): Int {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) return 0
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
val heightPx = if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else {
0
}
// Convert physical pixels to device-independent pixels for QML
val density = resources.displayMetrics.density
val heightDp = (heightPx / density).toInt()
return heightDp
}
@Suppress("unused")
fun startQrCodeReader() {
Log.v(TAG, "Start camera")

View File

@@ -38,15 +38,15 @@ object AppListProvider {
}
}
private class App(pi: PackageInfo, pm: PackageManager, ai: ApplicationInfo = pi.applicationInfo) : Comparable<App> {
private class App(pi: PackageInfo, pm: PackageManager, ai: ApplicationInfo? = pi.applicationInfo) : Comparable<App> {
val name: String?
val packageName: String = pi.packageName
val icon: Boolean = ai.icon != 0
val icon: Boolean = (ai?.icon ?: 0) != 0
val isLaunchable: Boolean = pm.getLaunchIntentForPackage(packageName) != null
init {
val name = ai.loadLabel(pm).toString()
this.name = if (name != packageName) name else null
val name = ai?.loadLabel(pm)?.toString()
this.name = name?.takeIf { it != packageName }
}
override fun compareTo(other: App): Int {

View File

@@ -28,4 +28,7 @@ object QtAndroidController {
external fun onAuthResult(result: Boolean)
external fun decodeQrCode(data: String): Boolean
external fun onImeInsetsChanged(heightDp: Int)
external fun onSystemBarsInsetsChanged(navBarHeightDp: Int, statusBarHeightDp: Int)
}

View File

@@ -10,6 +10,8 @@ import java.nio.channels.FileChannel
import java.nio.channels.FileLock
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.ZonedDateTime
import java.time.ZoneOffset
import java.util.concurrent.locks.ReentrantLock
import org.amnezia.vpn.util.Log.Priority.D
import org.amnezia.vpn.util.Log.Priority.E
@@ -135,8 +137,8 @@ object Log {
}
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
val date = LocalDateTime.now().format(dateTimeFormat)
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
val utcDate = ZonedDateTime.now(ZoneOffset.UTC).format(dateTimeFormat)
return "${utcDate}Z ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
"$tag: $msg\n"
}

View File

@@ -27,9 +27,15 @@ if(WIN32)
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif()
elseif(APPLE AND NOT IOS)
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
if(MACOS_NE)
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2")
else()
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
endif()
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")

View File

@@ -1,6 +1,6 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
set(APP_ANDROID_MIN_SDK 26)
set(APP_ANDROID_MIN_SDK 28)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE)
@@ -11,8 +11,8 @@ set_target_properties(${PROJECT} PROPERTIES
QT_ANDROID_VERSION_NAME ${CMAKE_PROJECT_VERSION}
QT_ANDROID_VERSION_CODE ${APP_ANDROID_VERSION_CODE}
QT_ANDROID_MIN_SDK_VERSION ${APP_ANDROID_MIN_SDK}
QT_ANDROID_TARGET_SDK_VERSION 34
QT_ANDROID_SDK_BUILD_TOOLS_REVISION 34.0.0
QT_ANDROID_TARGET_SDK_VERSION 36
QT_ANDROID_SDK_BUILD_TOOLS_REVISION 36.0.0
QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android
)

View File

@@ -46,6 +46,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/AmneziaSceneDelegateHooks.mm
)

View File

@@ -14,7 +14,7 @@ set(LIBS ${LIBS}
${FW_SECURITY}
${FW_COREWLAN}
${FW_NETWORK}
${FW_USERNOTIFICATIONS}
${FW_USER_NOTIFICATIONS}
${FW_NETWORK_EXTENSION}
)
@@ -35,6 +35,8 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
)
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
set(MACOSX_BUNDLE_ICON_FILE app.icns)
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
@@ -53,4 +55,3 @@ execute_process(
)
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")

168
client/cmake/macos_ne.cmake Normal file
View File

@@ -0,0 +1,168 @@
message("Client ==> MacOS NE build")
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
enable_language(OBJC)
enable_language(Swift)
find_package(Qt6 REQUIRED COMPONENTS ShaderTools Widgets)
# Link Qt Widgets for QWidget, QMenu, QAction etc.
set(LIBS ${LIBS} Qt6::ShaderTools Qt6::Widgets)
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
find_library(FW_AVFOUNDATION AVFoundation)
find_library(FW_FOUNDATION Foundation)
find_library(FW_STOREKIT StoreKit)
find_library(FW_SERVICEMGMT ServiceManagement)
find_library(FW_USERNOTIFICATIONS UserNotifications)
find_library(FW_NETWORKEXTENSION NetworkExtension)
set(LIBS ${LIBS}
${FW_AUTHENTICATIONSERVICES}
${FW_AVFOUNDATION}
${FW_FOUNDATION}
${FW_STOREKIT}
${FW_SERVICEMGMT}
${FW_USERNOTIFICATIONS}
${FW_NETWORKEXTENSION}
)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
)
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
set(MACOSX_BUNDLE_ICON_FILE app.icns)
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(SOURCES ${SOURCES} ${ICON_FILE})
target_include_directories(${PROJECT} PRIVATE
${Qt6Gui_PRIVATE_INCLUDE_DIRS}
${Qt6Widgets_PRIVATE_INCLUDE_DIRS}
)
set_target_properties(${PROJECT} PROPERTIES
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in
MACOSX_BUNDLE_ICON_FILE "AppIcon"
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/app.entitlements"
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
XCODE_GENERATE_SCHEME TRUE
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
XCODE_EMBED_APP_EXTENSIONS AmneziaVPNNetworkExtension
)
if(DEPLOY)
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.AmneziaVPN"
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.AmneziaVPN"
)
else()
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
endif()
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"
)
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
)
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_compile_options(${PROJECT} PRIVATE
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
)
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
target_sources(${PROJECT} PRIVATE
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
)
target_sources(${PROJECT} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
)
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
)
add_subdirectory(macos/networkextension)
add_dependencies(${PROJECT} AmneziaVPNNetworkExtension)
get_target_property(QtCore_location Qt6::Core LOCATION)
message("QtCore_location")
message(${QtCore_location})
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework"
)
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos)
target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework")
add_custom_command(TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
COMMAND /usr/bin/codesign --force --sign "Apple Distribution"
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Signing OpenVPNAdapter framework"
)

View File

@@ -4,7 +4,7 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/migrations.h
${CLIENT_ROOT_DIR}/../ipc/ipc.h
${CLIENT_ROOT_DIR}/amnezia_application.h
${CLIENT_ROOT_DIR}/core/models/containers/containers_defs.h
${CLIENT_ROOT_DIR}/containers/containers_defs.h
${CLIENT_ROOT_DIR}/core/defs.h
${CLIENT_ROOT_DIR}/core/errorstrings.h
${CLIENT_ROOT_DIR}/core/scripts_registry.h
@@ -12,18 +12,8 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/api/apiDefs.h
${CLIENT_ROOT_DIR}/core/qrCodeUtils.h
${CLIENT_ROOT_DIR}/core/controllers/coreController.h
${CLIENT_ROOT_DIR}/core/controllers/api/gatewayController.h
${CLIENT_ROOT_DIR}/core/controllers/api/apiConfigController.h
${CLIENT_ROOT_DIR}/core/controllers/api/apiSettingsController.h
${CLIENT_ROOT_DIR}/core/controllers/api/apiPremV1MigrationController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/serverController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/selfhostedConfigController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/clientManagementController.h
${CLIENT_ROOT_DIR}/core/controllers/configController.h
${CLIENT_ROOT_DIR}/core/controllers/dnsController.h
${CLIENT_ROOT_DIR}/core/controllers/splitTunnelingController.h
${CLIENT_ROOT_DIR}/core/controllers/settingsController.h
${CLIENT_ROOT_DIR}/core/controllers/connectionController.h
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
${CLIENT_ROOT_DIR}/core/controllers/serverController.h
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.h
${CLIENT_ROOT_DIR}/protocols/protocols_defs.h
${CLIENT_ROOT_DIR}/protocols/qml_register_protocols.h
@@ -38,7 +28,6 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/../common/logger/logger.h
${CLIENT_ROOT_DIR}/utils/qmlUtils.h
${CLIENT_ROOT_DIR}/core/api/apiUtils.h
${CLIENT_ROOT_DIR}/core/utils/fileUtils.h
)
# Mozilla headres
@@ -50,7 +39,7 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
)
if(NOT IOS)
if(NOT IOS AND NOT MACOS_NE)
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
)
@@ -65,24 +54,14 @@ endif()
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/migrations.cpp
${CLIENT_ROOT_DIR}/amnezia_application.cpp
${CLIENT_ROOT_DIR}/core/models/containers/containers_defs.cpp
${CLIENT_ROOT_DIR}/containers/containers_defs.cpp
${CLIENT_ROOT_DIR}/core/errorstrings.cpp
${CLIENT_ROOT_DIR}/core/scripts_registry.cpp
${CLIENT_ROOT_DIR}/core/server_defs.cpp
${CLIENT_ROOT_DIR}/core/qrCodeUtils.cpp
${CLIENT_ROOT_DIR}/core/controllers/coreController.cpp
${CLIENT_ROOT_DIR}/core/controllers/api/gatewayController.cpp
${CLIENT_ROOT_DIR}/core/controllers/api/apiConfigController.cpp
${CLIENT_ROOT_DIR}/core/controllers/api/apiSettingsController.cpp
${CLIENT_ROOT_DIR}/core/controllers/api/apiPremV1MigrationController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/serverController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/selfhostedConfigController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/clientManagementController.cpp
${CLIENT_ROOT_DIR}/core/controllers/configController.cpp
${CLIENT_ROOT_DIR}/core/controllers/dnsController.cpp
${CLIENT_ROOT_DIR}/core/controllers/splitTunnelingController.cpp
${CLIENT_ROOT_DIR}/core/controllers/settingsController.cpp
${CLIENT_ROOT_DIR}/core/controllers/connectionController.cpp
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
${CLIENT_ROOT_DIR}/core/controllers/serverController.cpp
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.cpp
${CLIENT_ROOT_DIR}/protocols/protocols_defs.cpp
${CLIENT_ROOT_DIR}/ui/qautostart.cpp
@@ -100,7 +79,6 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
${CLIENT_ROOT_DIR}/utils/qmlUtils.cpp
${CLIENT_ROOT_DIR}/core/api/apiUtils.cpp
${CLIENT_ROOT_DIR}/core/utils/fileUtils.cpp
)
# Mozilla sources
@@ -111,12 +89,26 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
)
if(NOT IOS)
if(NOT IOS AND NOT MACOS_NE)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
)
endif()
# Include native macOS platform helpers (dock/status-item)
if(APPLE AND NOT IOS)
list(APPEND HEADERS
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
${CLIENT_ROOT_DIR}/ui/macos_util.h
)
list(APPEND SOURCES
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
${CLIENT_ROOT_DIR}/ui/macos_util.mm
)
endif()
if(NOT ANDROID)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
@@ -137,41 +129,21 @@ file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/models/protocols/*.h
${CLIENT_ROOT_DIR}/ui/models/services/*.h
${CLIENT_ROOT_DIR}/ui/models/api/*.h
${CLIENT_ROOT_DIR}/ui/models/selfhosted/*.h
)
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/models/*.cpp
${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp
${CLIENT_ROOT_DIR}/ui/models/services/*.cpp
${CLIENT_ROOT_DIR}/ui/models/api/*.cpp
${CLIENT_ROOT_DIR}/ui/models/selfhosted/*.cpp
)
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/controllers/*.h
${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.h
)
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.cpp
)
file(GLOB CORE_MODELS_H CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/core/models/*.h
${CLIENT_ROOT_DIR}/core/models/containers/*.h
${CLIENT_ROOT_DIR}/core/models/protocols/*.h
${CLIENT_ROOT_DIR}/core/models/servers/*.h
)
file(GLOB CORE_MODELS_CPP CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/core/models/*.cpp
${CLIENT_ROOT_DIR}/core/models/containers/*.cpp
${CLIENT_ROOT_DIR}/core/models/protocols/*.cpp
${CLIENT_ROOT_DIR}/core/models/servers/*.cpp
)
set(HEADERS ${HEADERS}
@@ -180,7 +152,6 @@ set(HEADERS ${HEADERS}
${CONFIGURATORS_H}
${UI_MODELS_H}
${UI_CONTROLLERS_H}
${CORE_MODELS_H}
)
set(SOURCES ${SOURCES}
${COMMON_FILES_CPP}
@@ -188,7 +159,6 @@ set(SOURCES ${SOURCES}
${CONFIGURATORS_CPP}
${UI_MODELS_CPP}
${UI_CONTROLLERS_CPP}
${CORE_MODELS_CPP}
)
if(WIN32)

View File

@@ -1,29 +1,24 @@
#include "awg_configurator.h"
#include "protocols/protocols_defs.h"
#include <QJsonDocument>
#include <QJsonObject>
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: WireguardConfigurator(settings, serverController, true, parent)
{
}
QSharedPointer<ProtocolConfig> AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
auto result = WireguardConfigurator::createConfig(credentials, container, protocolConfig, errorCode);
if (!result) {
return nullptr;
}
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(result);
if (!awgConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
QString config = awgConfig->clientProtocolConfig.nativeConfig;
QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode);
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
QString awgConfig = jsonConfig.value(config_key::config).toString();
QMap<QString, QString> configMap;
auto configLines = config.split("\n");
auto configLines = awgConfig.split("\n");
for (auto &line : configLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
@@ -36,17 +31,31 @@ QSharedPointer<ProtocolConfig> AwgConfigurator::createConfig(const ServerCredent
}
}
awgConfig->clientProtocolConfig.awgData.junkPacketCount = configMap.value(config_key::junkPacketCount);
awgConfig->clientProtocolConfig.awgData.junkPacketMinSize = configMap.value(config_key::junkPacketMinSize);
awgConfig->clientProtocolConfig.awgData.junkPacketMaxSize = configMap.value(config_key::junkPacketMaxSize);
awgConfig->clientProtocolConfig.awgData.initPacketJunkSize = configMap.value(config_key::initPacketJunkSize);
awgConfig->clientProtocolConfig.awgData.responsePacketJunkSize = configMap.value(config_key::responsePacketJunkSize);
awgConfig->clientProtocolConfig.awgData.initPacketMagicHeader = configMap.value(config_key::initPacketMagicHeader);
awgConfig->clientProtocolConfig.awgData.responsePacketMagicHeader = configMap.value(config_key::responsePacketMagicHeader);
awgConfig->clientProtocolConfig.awgData.underloadPacketMagicHeader = configMap.value(config_key::underloadPacketMagicHeader);
awgConfig->clientProtocolConfig.awgData.transportPacketMagicHeader = configMap.value(config_key::transportPacketMagicHeader);
jsonConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
jsonConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
jsonConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
jsonConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
jsonConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
jsonConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
awgConfig->clientProtocolConfig.wireGuardData.mtu = awgConfig->serverProtocolConfig.mtu;
// jsonConfig[config_key::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize);
// jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize);
return awgConfig;
// jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1);
// jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2);
// jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3);
// jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4);
// jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5);
// jsonConfig[config_key::controlledJunk1] = configMap.value(amnezia::config_key::controlledJunk1);
// jsonConfig[config_key::controlledJunk2] = configMap.value(amnezia::config_key::controlledJunk2);
// jsonConfig[config_key::controlledJunk3] = configMap.value(amnezia::config_key::controlledJunk3);
// jsonConfig[config_key::specialHandshakeTimeout] = configMap.value(amnezia::config_key::specialHandshakeTimeout);
jsonConfig[config_key::mtu] =
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
return QJsonDocument(jsonConfig).toJson();
}

View File

@@ -11,8 +11,8 @@ class AwgConfigurator : public WireguardConfigurator
public:
AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // AWGCONFIGURATOR_H

View File

@@ -4,47 +4,23 @@
#include <QJsonDocument>
#include <QJsonObject>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
ConfiguratorBase::Vars CloakConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
Vars vars = generateCommonVars(credentials, container);
auto cloakConfig = qSharedPointerCast<CloakProtocolConfig>(protocolConfig);
if (!cloakConfig) {
return vars;
}
vars.append({{"$CLOAK_SERVER_PORT", cloakConfig->serverProtocolConfig.port}});
vars.append({{"$FAKE_WEB_SITE_ADDRESS", cloakConfig->serverProtocolConfig.site}});
return vars;
}
QSharedPointer<ProtocolConfig> CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
{
auto cloakConfig = qSharedPointerCast<CloakProtocolConfig>(protocolConfig);
if (!cloakConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
QString cloakPublicKey =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckPublicKeyPath, errorCode);
cloakPublicKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
QString cloakBypassUid =
@@ -52,38 +28,24 @@ QSharedPointer<ProtocolConfig> CloakConfigurator::createConfig(const ServerCrede
cloakBypassUid.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
cloakConfig->clientProtocolConfig.transport = "direct";
cloakConfig->clientProtocolConfig.proxyMethod = "openvpn";
cloakConfig->clientProtocolConfig.encryptionMethod = "aes-gcm";
cloakConfig->clientProtocolConfig.uid = cloakBypassUid;
cloakConfig->clientProtocolConfig.publicKey = cloakPublicKey;
cloakConfig->clientProtocolConfig.serverName = cloakConfig->serverProtocolConfig.site;
cloakConfig->clientProtocolConfig.numConn = 1;
cloakConfig->clientProtocolConfig.browserSig = "chrome";
cloakConfig->clientProtocolConfig.streamTimeout = 300;
cloakConfig->clientProtocolConfig.remoteHost = credentials.hostName;
cloakConfig->clientProtocolConfig.remotePort = cloakConfig->serverProtocolConfig.port;
QJsonObject config;
config.insert("Transport", cloakConfig->clientProtocolConfig.transport);
config.insert("ProxyMethod", cloakConfig->clientProtocolConfig.proxyMethod);
config.insert("EncryptionMethod", cloakConfig->clientProtocolConfig.encryptionMethod);
config.insert("UID", cloakConfig->clientProtocolConfig.uid);
config.insert("PublicKey", cloakConfig->clientProtocolConfig.publicKey);
config.insert("ServerName", cloakConfig->clientProtocolConfig.serverName);
config.insert("NumConn", cloakConfig->clientProtocolConfig.numConn);
config.insert("BrowserSig", cloakConfig->clientProtocolConfig.browserSig);
config.insert("StreamTimeout", cloakConfig->clientProtocolConfig.streamTimeout);
config.insert("RemoteHost", cloakConfig->clientProtocolConfig.remoteHost);
config.insert("RemotePort", cloakConfig->clientProtocolConfig.remotePort);
config.insert("Transport", "direct");
config.insert("ProxyMethod", "openvpn");
config.insert("EncryptionMethod", "aes-gcm");
config.insert("UID", cloakBypassUid);
config.insert("PublicKey", cloakPublicKey);
config.insert("ServerName", "$FAKE_WEB_SITE_ADDRESS");
config.insert("NumConn", 1);
config.insert("BrowserSig", "chrome");
config.insert("StreamTimeout", 300);
config.insert("RemoteHost", credentials.hostName);
config.insert("RemotePort", "$CLOAK_SERVER_PORT");
QString textCfg = QJsonDocument(config).toJson();
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
cloakConfig->clientProtocolConfig.isEmpty = false;
cloakConfig->clientProtocolConfig.nativeConfig = textCfg;
return cloakConfig;
return textCfg;
}

View File

@@ -4,7 +4,6 @@
#include <QObject>
#include "configurator_base.h"
#include "core/models/protocols/cloakProtocolConfig.h"
using namespace amnezia;
@@ -14,11 +13,8 @@ class CloakConfigurator : public ConfiguratorBase
public:
CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // CLOAK_CONFIGURATOR_H

View File

@@ -1,58 +1,26 @@
#include "configurator_base.h"
#include "core/networkUtilities.h"
#include "core/models/protocols/protocolConfig.h"
#include <variant>
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: QObject { parent }, m_settings(settings), m_serverController(serverController)
{
}
void ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
void ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QSharedPointer<ProtocolConfig> &protocolConfig)
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString)
{
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
std::visit([&dns](const auto &config) -> void {
config->clientProtocolConfig.nativeConfig.replace("$PRIMARY_DNS", dns.first);
config->clientProtocolConfig.nativeConfig.replace("$SECONDARY_DNS", dns.second);
}, variant);
}
ConfiguratorBase::Vars ConfiguratorBase::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
{
return generateCommonVars(credentials, container);
}
ConfiguratorBase::Vars ConfiguratorBase::generateCommonVars(const ServerCredentials &credentials, DockerContainer container) const
{
Vars vars;
vars.append({{"$REMOTE_HOST", credentials.hostName}});
QString serverIp = (container != DockerContainer::Awg && container != DockerContainer::WireGuard && container != DockerContainer::Xray)
? NetworkUtilities::getIPAddress(credentials.hostName)
: credentials.hostName;
if (!serverIp.isEmpty()) {
vars.append({{"$SERVER_IP_ADDRESS", serverIp}});
}
vars.append({{"$CONTAINER_NAME", ContainerProps::containerToString(container)}});
vars.append({{"$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container)}});
vars.append({{"$PRIMARY_SERVER_DNS", m_settings->primaryDns()}});
vars.append({{"$SECONDARY_SERVER_DNS", m_settings->secondaryDns()}});
return vars;
protocolConfigString.replace("$PRIMARY_DNS", dns.first);
protocolConfigString.replace("$SECONDARY_DNS", dns.second);
}

View File

@@ -2,39 +2,28 @@
#define CONFIGURATORBASE_H
#include <QObject>
#include <QList>
#include <QPair>
#include <QSharedPointer>
#include "core/models/containers/containers_defs.h"
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/controllers/serverController.h"
#include "settings.h"
class ConfiguratorBase : public QObject
{
Q_OBJECT
public:
using Vars = QList<QPair<QString, QString>>;
explicit ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
virtual QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) = 0;
virtual QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) = 0;
virtual void processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig);
virtual void processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig);
virtual Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const;
virtual QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
virtual QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
protected:
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QSharedPointer<ProtocolConfig> &protocolConfig);
Vars generateCommonVars(const ServerCredentials &credentials, DockerContainer container) const;
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString);
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServerController> m_serverController;

View File

@@ -8,11 +8,10 @@
#include <QTemporaryFile>
#include <QUuid>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "protocols/protocols_defs.h"
#include "utilities.h"
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
@@ -20,28 +19,6 @@ Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const Q
{
}
ConfiguratorBase::Vars Ikev2Configurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
{
Vars vars = generateCommonVars(credentials, container);
vars.append({{"$IPSEC_VPN_L2TP_NET", "192.168.42.0/24"}});
vars.append({{"$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250"}});
vars.append({{"$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1"}});
vars.append({{"$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24"}});
vars.append({{"$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250"}});
vars.append({{"$IPSEC_VPN_SHA2_TRUNCBUG", "yes"}});
vars.append({{"$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes"}});
vars.append({{"$IPSEC_VPN_DISABLE_IKEV2", "no"}});
vars.append({{"$IPSEC_VPN_DISABLE_L2TP", "no"}});
vars.append({{"$IPSEC_VPN_DISABLE_XAUTH", "no"}});
vars.append({{"$IPSEC_VPN_C2C_TRAFFIC", "no"}});
return vars;
}
Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode)
{
@@ -77,58 +54,21 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
return connData;
}
QSharedPointer<ProtocolConfig> Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
Q_UNUSED(protocolConfig)
// IKEv2 uses a generic ProtocolConfig - no specific subclass needed
if (!protocolConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
Q_UNUSED(containerConfig)
ConnectionData connData = prepareIkev2Config(credentials, container, errorCode);
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
auto ikev2Config = qSharedPointerCast<Ikev2ProtocolConfig>(protocolConfig);
if (!ikev2Config) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
ikev2Config->clientProtocolConfig.isEmpty = false;
ikev2Config->clientProtocolConfig.hostName = connData.host;
ikev2Config->clientProtocolConfig.userName = connData.clientId;
ikev2Config->clientProtocolConfig.cert = QString(connData.clientCert.toBase64());
ikev2Config->clientProtocolConfig.password = connData.password;
// Generate the appropriate native config based on platform
QString nativeConfigStr;
switch (Utils::systemType()) {
case SystemType::iOS:
[[fallthrough]];
case SystemType::macOS:
nativeConfigStr = genMobileConfig(connData);
break;
case SystemType::Android:
nativeConfigStr = genIkev2Config(connData);
break;
default:
nativeConfigStr = genStrongSwanConfig(connData);
break;
}
ikev2Config->clientProtocolConfig.nativeConfig = nativeConfigStr;
return ikev2Config;
return genIkev2Config(connData);
}
QString Ikev2Configurator::genIkev2Config(const ConnectionData &connData)
{
// Create temporary JSON for Android platform (will be eliminated when android protocols are updated)
QJsonObject config;
config[config_key::hostName] = connData.host;
config[config_key::userName] = connData.clientId;

View File

@@ -5,8 +5,6 @@
#include <QProcessEnvironment>
#include "configurator_base.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/models/protocols/ikev2ProtocolConfig.h"
#include "core/defs.h"
class Ikev2Configurator : public ConfiguratorBase
@@ -23,16 +21,13 @@ public:
QString host; // host ip
};
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
QString genIkev2Config(const ConnectionData &connData);
QString genMobileConfig(const ConnectionData &connData);
QString genStrongSwanConfig(const ConnectionData &connData);
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
ConnectionData prepareIkev2Config(const ServerCredentials &credentials,
DockerContainer container, ErrorCode &errorCode);
};

View File

@@ -1,7 +1,8 @@
#include "openvpn_configurator.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QProcess>
#include <QString>
#include <QTemporaryDir>
@@ -13,13 +14,9 @@
#endif
#include "core/networkUtilities.h"
#include "core/models/protocols/protocolConfig.h"
#include <variant>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include "settings.h"
#include "utilities.h"
@@ -34,39 +31,6 @@ OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, con
{
}
ConfiguratorBase::Vars OpenVpnConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
{
Vars vars = generateCommonVars(credentials, container);
auto openVpnConfig = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig);
if (!openVpnConfig) {
return vars;
}
vars.append({{"$OPENVPN_SUBNET_IP", openVpnConfig->serverProtocolConfig.subnetAddress}});
vars.append({{"$OPENVPN_SUBNET_CIDR", protocols::openvpn::defaultSubnetCidr}});
vars.append({{"$OPENVPN_SUBNET_MASK", protocols::openvpn::defaultSubnetMask}});
vars.append({{"$OPENVPN_PORT", openVpnConfig->serverProtocolConfig.port}});
vars.append({{"$OPENVPN_TRANSPORT_PROTO", openVpnConfig->serverProtocolConfig.transportProto}});
vars.append({{"$OPENVPN_NCP_DISABLE", openVpnConfig->serverProtocolConfig.ncpDisable ? protocols::openvpn::ncpDisableString : ""}});
vars.append({{"$OPENVPN_CIPHER", openVpnConfig->serverProtocolConfig.cipher}});
vars.append({{"$OPENVPN_HASH", openVpnConfig->serverProtocolConfig.hash}});
vars.append({{"$OPENVPN_TLS_AUTH", openVpnConfig->serverProtocolConfig.tlsAuth ? protocols::openvpn::tlsAuthString : ""}});
if (!openVpnConfig->serverProtocolConfig.tlsAuth) {
vars.append({{"$OPENVPN_TA_KEY", ""}});
}
vars.append({{"$OPENVPN_ADDITIONAL_CLIENT_CONFIG", openVpnConfig->serverProtocolConfig.additionalClientConfig}});
vars.append({{"$OPENVPN_ADDITIONAL_SERVER_CONFIG", openVpnConfig->serverProtocolConfig.additionalServerConfig}});
return vars;
}
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
DockerContainer container, ErrorCode &errorCode)
{
@@ -108,29 +72,41 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
QSharedPointer<ProtocolConfig> OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
auto openVpnConfig = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig);
if (!openVpnConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
generateProtocolVars(credentials, container, protocolConfig));
m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
auto sanitizeStaticKey = [](const QString &key) {
QStringList lines = key.split('\n');
QStringList filtered;
filtered.reserve(lines.size());
for (const QString &line : lines) {
const QString trimmed = line.trimmed();
if (trimmed.startsWith('#')) {
continue;
}
filtered.append(line);
}
QString result = filtered.join('\n');
if (!result.endsWith('\n')) {
result.append('\n');
}
return result;
};
config.replace("$OPENVPN_CA_CERT", connData.caCert);
config.replace("$OPENVPN_CLIENT_CERT", connData.clientCert);
config.replace("$OPENVPN_PRIV_KEY", connData.privKey);
if (config.contains("$OPENVPN_TA_KEY")) {
config.replace("$OPENVPN_TA_KEY", connData.taKey);
config.replace("$OPENVPN_TA_KEY", sanitizeStaticKey(connData.taKey));
} else {
config.replace("<tls-auth>", "");
config.replace("</tls-auth>", "");
@@ -140,100 +116,91 @@ QSharedPointer<ProtocolConfig> OpenVpnConfigurator::createConfig(const ServerCre
config.replace("block-outside-dns", "");
#endif
openVpnConfig->clientProtocolConfig.isEmpty = false;
openVpnConfig->clientProtocolConfig.clientId = connData.clientId;
openVpnConfig->clientProtocolConfig.nativeConfig = config;
QJsonObject jConfig;
jConfig[config_key::config] = config;
return openVpnConfig;
jConfig[config_key::clientId] = connData.clientId;
return QJsonDocument(jConfig).toJson();
}
void OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
std::visit([this, &dns, isApiConfig](const auto &config) -> void {
if constexpr (std::is_same_v<std::decay_t<decltype(config)>, QSharedPointer<OpenVpnProtocolConfig>>) {
QString &nativeConfig = config->clientProtocolConfig.nativeConfig;
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
QString config = json[config_key::config].toString();
if (!isApiConfig) {
QRegularExpression regex("redirect-gateway.*");
nativeConfig.replace(regex, "");
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
nativeConfig.replace(dnsRegex, "");
}
if (!isApiConfig) {
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
if (!m_settings->isSitesSplitTunnelingEnabled()) {
nativeConfig.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
nativeConfig.append("block-ipv6\n");
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
config.replace(dnsRegex, "");
}
// no redirect-gateway
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
nativeConfig.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
// Prevent ipv6 leak
if (!m_settings->isSitesSplitTunnelingEnabled()) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
config.append("block-ipv6\n");
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
// Prevent ipv6 leak
#endif
nativeConfig.append("block-ipv6\n");
}
QStringList routeList = m_settings->vpnRoutes();
if (!routeList.isEmpty()) {
for (auto route : routeList) {
nativeConfig.append("\nroute " + route + " 255.255.255.255 vpn_gateway\n");
}
}
}
config.append("block-ipv6\n");
}
}
#ifndef MZ_WINDOWS
nativeConfig.replace("block-outside-dns", "");
config.replace("block-outside-dns", "");
#endif
#if (defined(MZ_MACOS) || defined(MZ_LINUX))
QString dnsConf = QString("\nscript-security 2\n"
"up %1/update-resolv-conf.sh\n"
"down %1/update-resolv-conf.sh\n")
.arg(qApp->applicationDirPath());
QString dnsConf = QString("\nscript-security 2\n"
"up %1/update-resolv-conf.sh\n"
"down %1/update-resolv-conf.sh\n")
.arg(qApp->applicationDirPath());
nativeConfig.append(dnsConf);
config.append(dnsConf);
#endif
}
}, variant);
json[config_key::config] = config;
return QJsonDocument(json).toJson();
}
void OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
std::visit([&dns](const auto &config) -> void {
if constexpr (std::is_same_v<std::decay_t<decltype(config)>, QSharedPointer<OpenVpnProtocolConfig>>) {
QString &nativeConfig = config->clientProtocolConfig.nativeConfig;
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
QString config = json[config_key::config].toString();
QRegularExpression regex("redirect-gateway.*");
nativeConfig.replace(regex, "");
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
nativeConfig.replace(dnsRegex, "");
}
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
config.replace(dnsRegex, "");
}
nativeConfig.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak
nativeConfig.append("block-ipv6\n");
// Prevent ipv6 leak
config.append("block-ipv6\n");
// remove block-outside-dns for all exported configs
nativeConfig.replace("block-outside-dns", "");
}
}, variant);
// remove block-outside-dns for all exported configs
config.replace("block-outside-dns", "");
json[config_key::config] = config;
return QJsonDocument(json).toJson();
}
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId)
@@ -250,7 +217,7 @@ ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerC
.arg(clientId);
QStringList scriptList { script_import, script_sign };
QString script = m_serverController->replaceVars(scriptList.join("\n"), generateProtocolVars(credentials, container));
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container));
return m_serverController->runScript(credentials, script);
}

View File

@@ -6,7 +6,6 @@
#include "configurator_base.h"
#include "core/defs.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
class OpenVpnConfigurator : public ConfiguratorBase
{
@@ -25,16 +24,13 @@ public:
QString host; // host ip
};
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
void processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig) override;
void processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig) override;
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData createCertRequest();

View File

@@ -4,10 +4,8 @@
#include <QJsonDocument>
#include <QJsonObject>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
QObject *parent)
@@ -15,59 +13,28 @@ ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> setti
{
}
ConfiguratorBase::Vars ShadowSocksConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
Vars vars = generateCommonVars(credentials, container);
auto shadowsocksConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig);
if (!shadowsocksConfig) {
return vars;
}
vars.append({{"$SHADOWSOCKS_SERVER_PORT", shadowsocksConfig->serverProtocolConfig.port}});
vars.append({{"$SHADOWSOCKS_LOCAL_PORT", protocols::shadowsocks::defaultLocalProxyPort}});
vars.append({{"$SHADOWSOCKS_CIPHER", shadowsocksConfig->serverProtocolConfig.cipher}});
return vars;
}
QSharedPointer<ProtocolConfig> ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
{
auto shadowsocksConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig);
if (!shadowsocksConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
QString ssKey =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::shadowsocks::ssKeyPath, errorCode);
ssKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
shadowsocksConfig->clientProtocolConfig.server = credentials.hostName;
shadowsocksConfig->clientProtocolConfig.serverPort = shadowsocksConfig->serverProtocolConfig.port;
shadowsocksConfig->clientProtocolConfig.localPort = protocols::shadowsocks::defaultLocalProxyPort;
shadowsocksConfig->clientProtocolConfig.password = ssKey;
shadowsocksConfig->clientProtocolConfig.timeout = 60;
shadowsocksConfig->clientProtocolConfig.method = shadowsocksConfig->serverProtocolConfig.cipher;
QJsonObject config;
config.insert("server", shadowsocksConfig->clientProtocolConfig.server);
config.insert("server_port", shadowsocksConfig->clientProtocolConfig.serverPort);
config.insert("local_port", shadowsocksConfig->clientProtocolConfig.localPort);
config.insert("password", shadowsocksConfig->clientProtocolConfig.password);
config.insert("timeout", shadowsocksConfig->clientProtocolConfig.timeout);
config.insert("method", shadowsocksConfig->clientProtocolConfig.method);
config.insert("server", credentials.hostName);
config.insert("server_port", "$SHADOWSOCKS_SERVER_PORT");
config.insert("local_port", "$SHADOWSOCKS_LOCAL_PORT");
config.insert("password", ssKey);
config.insert("timeout", 60);
config.insert("method", "$SHADOWSOCKS_CIPHER");
QString textCfg = QJsonDocument(config).toJson();
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
shadowsocksConfig->clientProtocolConfig.isEmpty = false;
shadowsocksConfig->clientProtocolConfig.nativeConfig = textCfg;
return shadowsocksConfig;
// qDebug().noquote() << textCfg;
return textCfg;
}

View File

@@ -4,7 +4,6 @@
#include <QObject>
#include "configurator_base.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/defs.h"
class ShadowSocksConfigurator : public ConfiguratorBase
@@ -13,11 +12,8 @@ class ShadowSocksConfigurator : public ConfiguratorBase
public:
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // SHADOWSOCKS_CONFIGURATOR_H

View File

@@ -8,7 +8,7 @@
#include <QTemporaryFile>
#include <QThread>
#include <qtimer.h>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
#include <QGuiApplication>
#else
#include <QApplication>
@@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
QString SshConfigurator::convertOpenSShKey(const QString &key)
{
#ifndef Q_OS_IOS
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
@@ -67,9 +67,10 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
#endif
}
// DEAD CODE.
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
{
#ifndef Q_OS_IOS
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
QProcess *p = new QProcess();
p->setProcessChannelMode(QProcess::SeparateChannels);
@@ -101,7 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
pathEnvVar.clear();
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
#elif defined(Q_OS_MACX)
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
#endif

View File

@@ -7,21 +7,16 @@
#include <QString>
#include <QTemporaryDir>
#include <QTemporaryFile>
#include <type_traits>
#include <variant>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include "settings.h"
#include "utilities.h"
@@ -42,47 +37,6 @@ WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort;
}
ConfiguratorBase::Vars WireguardConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
{
Vars vars = generateCommonVars(credentials, container);
if (m_isAwg) {
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
if (!awgConfig) {
return vars;
}
vars.append({{"$AWG_SUBNET_IP", awgConfig->serverProtocolConfig.subnetAddress}});
vars.append({{"$AWG_SERVER_PORT", awgConfig->serverProtocolConfig.port}});
const auto &awgData = awgConfig->serverProtocolConfig.awgData;
vars.append({{"$JUNK_PACKET_COUNT", awgData.junkPacketCount}});
vars.append({{"$JUNK_PACKET_MIN_SIZE", awgData.junkPacketMinSize}});
vars.append({{"$JUNK_PACKET_MAX_SIZE", awgData.junkPacketMaxSize}});
vars.append({{"$INIT_PACKET_JUNK_SIZE", awgData.initPacketJunkSize}});
vars.append({{"$RESPONSE_PACKET_JUNK_SIZE", awgData.responsePacketJunkSize}});
vars.append({{"$INIT_PACKET_MAGIC_HEADER", awgData.initPacketMagicHeader}});
vars.append({{"$RESPONSE_PACKET_MAGIC_HEADER", awgData.responsePacketMagicHeader}});
vars.append({{"$UNDERLOAD_PACKET_MAGIC_HEADER", awgData.underloadPacketMagicHeader}});
vars.append({{"$TRANSPORT_PACKET_MAGIC_HEADER", awgData.transportPacketMagicHeader}});
vars.append({{"$COOKIE_REPLY_PACKET_JUNK_SIZE", awgData.cookieReplyPacketJunkSize}});
vars.append({{"$TRANSPORT_PACKET_JUNK_SIZE", awgData.transportPacketJunkSize}});
} else {
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
if (!wgConfig) {
return vars;
}
vars.append({{"$WIREGUARD_SUBNET_IP", wgConfig->serverProtocolConfig.subnetAddress}});
vars.append({{"$WIREGUARD_SUBNET_CIDR", protocols::wireguard::defaultSubnetCidr}});
vars.append({{"$WIREGUARD_SUBNET_MASK", protocols::wireguard::defaultSubnetMask}});
vars.append({{"$WIREGUARD_SERVER_PORT", wgConfig->serverProtocolConfig.port}});
}
return vars;
}
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
{
// TODO review
@@ -137,20 +91,12 @@ QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig,
const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName;
// Extract port from appropriate protocol config
if (m_isAwg) {
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
connData.port = awgConfig ? awgConfig->serverProtocolConfig.port : m_defaultPort;
} else {
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
connData.port = wgConfig ? wgConfig->serverProtocolConfig.port : m_defaultPort;
}
connData.port = containerConfig.value(m_protocolName).toObject().value(config_key::port).toString(m_defaultPort);
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
errorCode = ErrorCode::InternalError;
@@ -174,16 +120,10 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
QHostAddress result;
QHostAddress lastIp;
if (ips.empty()) {
// Get subnet from protocol config
QString subnetAddress;
if (m_isAwg) {
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
subnetAddress = awgConfig ? awgConfig->serverProtocolConfig.subnetAddress : protocols::wireguard::defaultSubnetAddress;
} else {
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
subnetAddress = wgConfig ? wgConfig->serverProtocolConfig.subnetAddress : protocols::wireguard::defaultSubnetAddress;
}
lastIp.setAddress(subnetAddress);
lastIp.setAddress(containerConfig.value(m_protocolName)
.toObject()
.value(config_key::subnet_address)
.toString(protocols::wireguard::defaultSubnetAddress));
} else {
lastIp = ips.last();
}
@@ -233,39 +173,21 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
errorCode = m_serverController->runScript(
credentials,
m_serverController->replaceVars(script, generateProtocolVars(credentials, container)));
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
return connData;
}
QSharedPointer<ProtocolConfig> WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
QSharedPointer<ProtocolConfig> result;
if (m_isAwg) {
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
if (!awgConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
result = awgConfig;
} else {
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
if (!wgConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
result = wgConfig;
}
QString scriptData = amnezia::scriptData(m_configTemplate, container);
QString config = m_serverController->replaceVars(
scriptData, generateProtocolVars(credentials, container, protocolConfig));
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, protocolConfig, errorCode);
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey);
@@ -273,37 +195,40 @@ QSharedPointer<ProtocolConfig> WireguardConfigurator::createConfig(const ServerC
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
config.replace("$WIREGUARD_PSK", connData.pskKey);
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(result);
std::visit([&connData, &config](const auto &protocolConfig) -> void {
using ConfigType = std::decay_t<decltype(*protocolConfig)>;
if constexpr (std::is_same_v<ConfigType, AwgProtocolConfig> || std::is_same_v<ConfigType, WireGuardProtocolConfig>) {
protocolConfig->clientProtocolConfig.isEmpty = false;
protocolConfig->clientProtocolConfig.clientId = connData.clientPubKey;
protocolConfig->clientProtocolConfig.hostname = connData.host;
protocolConfig->clientProtocolConfig.port = connData.port.toInt();
protocolConfig->clientProtocolConfig.wireGuardData.clientPrivateKey = connData.clientPrivKey;
protocolConfig->clientProtocolConfig.wireGuardData.clientIp = connData.clientIP;
protocolConfig->clientProtocolConfig.wireGuardData.clientPublicKey = connData.clientPubKey;
protocolConfig->clientProtocolConfig.wireGuardData.pskKey = connData.pskKey;
protocolConfig->clientProtocolConfig.wireGuardData.serverPubKey = connData.serverPubKey;
protocolConfig->clientProtocolConfig.wireGuardData.mtu = protocolConfig->serverProtocolConfig.mtu;
protocolConfig->clientProtocolConfig.wireGuardData.persistentKeepAlive = "25";
protocolConfig->clientProtocolConfig.wireGuardData.allowedIps = QStringList{"0.0.0.0/0", "::/0"};
protocolConfig->clientProtocolConfig.nativeConfig = config;
}
}, variant);
const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
QJsonObject jConfig;
jConfig[config_key::config] = config;
return result;
jConfig[config_key::hostName] = connData.host;
jConfig[config_key::port] = connData.port.toInt();
jConfig[config_key::client_priv_key] = connData.clientPrivKey;
jConfig[config_key::client_ip] = connData.clientIP;
jConfig[config_key::client_pub_key] = connData.clientPubKey;
jConfig[config_key::psk_key] = connData.pskKey;
jConfig[config_key::server_pub_key] = connData.serverPubKey;
jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu);
jConfig[config_key::persistent_keep_alive] = "25";
QJsonArray allowedIps { "0.0.0.0/0", "::/0" };
jConfig[config_key::allowed_ips] = allowedIps;
jConfig[config_key::clientId] = connData.clientPubKey;
return QJsonDocument(jConfig).toJson();
}
void WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QSharedPointer<ProtocolConfig> &protocolConfig)
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
void WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QSharedPointer<ProtocolConfig> &protocolConfig)
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}

View File

@@ -6,8 +6,6 @@
#include <QProcessEnvironment>
#include "configurator_base.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/defs.h"
#include "core/scripts_registry.h"
@@ -29,23 +27,20 @@ public:
QString port;
};
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
void processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig) override;
void processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig) override;
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData genClientKeys();
private:
QList<QHostAddress> getIpsFromConf(const QString &input);
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode);
const QJsonObject &containerConfig, ErrorCode &errorCode);
bool m_isAwg;
QString m_serverConfigPath;

View File

@@ -6,11 +6,9 @@
#include <QUuid>
#include "logger.h"
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "protocols/protocols_defs.h"
namespace {
Logger logger("XrayConfigurator");
@@ -21,24 +19,8 @@ XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSh
{
}
ConfiguratorBase::Vars XrayConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
{
Vars vars = generateCommonVars(credentials, container);
auto xrayConfig = qSharedPointerCast<XrayProtocolConfig>(protocolConfig);
if (!xrayConfig) {
return vars;
}
vars.append({{"$XRAY_SITE_NAME", xrayConfig->serverProtocolConfig.site}});
vars.append({{"$XRAY_SERVER_PORT", xrayConfig->serverProtocolConfig.port}});
return vars;
}
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
// Generate new UUID for client
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
@@ -62,6 +44,7 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
QJsonObject serverConfig = doc.object();
// Validate server config structure
if (!serverConfig.contains("inbounds")) {
logger.error() << "Server config missing 'inbounds' field";
errorCode = ErrorCode::InternalError;
@@ -123,7 +106,7 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
errorCode = m_serverController->runScript(
credentials,
m_serverController->replaceVars(restartScript, generateProtocolVars(credentials, container))
m_serverController->replaceVars(restartScript, m_serverController->genVarsForScript(credentials, container))
);
if (errorCode != ErrorCode::NoError) {
@@ -134,30 +117,24 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
return clientId;
}
QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
auto xrayConfig = qSharedPointerCast<XrayProtocolConfig>(protocolConfig);
if (!xrayConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
// Get client ID from prepareServerConfig
QString xrayClientId = prepareServerConfig(credentials, container, protocolConfig, errorCode);
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
logger.error() << "Failed to prepare server config";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
generateProtocolVars(credentials, container, protocolConfig));
m_serverController->genVarsForScript(credentials, container, containerConfig));
if (config.isEmpty()) {
logger.error() << "Failed to get config template";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
QString xrayPublicKey =
@@ -165,7 +142,7 @@ QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCreden
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
logger.error() << "Failed to get public key";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
xrayPublicKey.replace("\n", "");
@@ -174,7 +151,7 @@ QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCreden
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
logger.error() << "Failed to get short ID";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
xrayShortId.replace("\n", "");
@@ -185,16 +162,12 @@ QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCreden
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
config.replace("$XRAY_CLIENT_ID", xrayClientId);
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
config.replace("$XRAY_SHORT_ID", xrayShortId);
xrayConfig->clientProtocolConfig.isEmpty = false;
xrayConfig->clientProtocolConfig.clientId = xrayClientId;
xrayConfig->clientProtocolConfig.nativeConfig = config;
return xrayConfig;
return config;
}

View File

@@ -4,7 +4,6 @@
#include <QObject>
#include "configurator_base.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "core/defs.h"
class XrayConfigurator : public ConfiguratorBase
@@ -13,15 +12,12 @@ class XrayConfigurator : public ConfiguratorBase
public:
XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
private:
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode);
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
};
#endif // XRAY_CONFIGURATOR_H

View File

@@ -261,6 +261,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
return true;
#elif defined(Q_OS_IOS)
// Standard iOS build (without Network Extension limitations)
switch (c) {
case DockerContainer::WireGuard: return true;
case DockerContainer::OpenVpn: return true;
@@ -269,7 +270,23 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Cloak: return true;
case DockerContainer::SSXray: return true;
// case DockerContainer::ShadowSocks: return true;
default: return false;
default:
return false;
}
#elif defined(MACOS_NE)
// macOS build using Network Extension hide OpenVPN-based containers
switch (c) {
case DockerContainer::WireGuard: return true;
case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true;
case DockerContainer::OpenVpn:
case DockerContainer::Cloak:
case DockerContainer::ShadowSocks:
return false;
default:
return false;
}
#elif defined(Q_OS_MAC)
switch (c) {

View File

@@ -14,6 +14,11 @@ namespace apiDefs
ExternalPremium
};
enum ConfigSource {
Telegram = 1,
AmneziaGateway
};
namespace key
{
constexpr QLatin1String configVersion("config_version");
@@ -42,12 +47,14 @@ namespace apiDefs
constexpr QLatin1String serverCountryName("server_country_name");
constexpr QLatin1String osVersion("os_version");
constexpr QLatin1String appLanguage("app_language");
constexpr QLatin1String availableCountries("available_countries");
constexpr QLatin1String activeDeviceCount("active_device_count");
constexpr QLatin1String maxDeviceCount("max_device_count");
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
constexpr QLatin1String issuedConfigs("issued_configs");
constexpr QLatin1String subscriptionDescription("subscription_description");
constexpr QLatin1String supportInfo("support_info");
constexpr QLatin1String email("email");
@@ -59,6 +66,16 @@ namespace apiDefs
constexpr QLatin1String id("id");
constexpr QLatin1String orderId("order_id");
constexpr QLatin1String migrationCode("migration_code");
constexpr QLatin1String transactionId("transaction_id");
constexpr QLatin1String userCountryCode("user_country_code");
constexpr QLatin1String serviceInfo("service_info");
constexpr QLatin1String isAdVisible("is_ad_visible");
constexpr QLatin1String adHeader("ad_header");
constexpr QLatin1String adDescription("ad_description");
constexpr QLatin1String adEndpoint("ad_endpoint");
}
const int requestTimeoutMsecs = 12 * 1000; // 12 secs

View File

@@ -23,7 +23,7 @@ namespace
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
{
QDateTime now = QDateTime::currentDateTime();
QDateTime now = QDateTime::currentDateTimeUtc();
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
return endDate < now;
}
@@ -32,8 +32,8 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
{
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
switch (configVersion) {
case amnezia::ServerConfigType::ApiV1: return true;
case amnezia::ServerConfigType::ApiV2: return true;
case apiDefs::ConfigSource::Telegram: return true;
case apiDefs::ConfigSource::AmneziaGateway: return true;
default: return false;
}
}
@@ -43,7 +43,7 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
switch (configVersion) {
case amnezia::ServerConfigType::ApiV1: {
case apiDefs::ConfigSource::Telegram: {
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
@@ -55,7 +55,7 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
return apiDefs::ConfigType::AmneziaFreeV2;
}
};
case amnezia::ServerConfigType::ApiV2: {
case apiDefs::ConfigSource::AmneziaGateway: {
constexpr QLatin1String servicePremium("amnezia-premium");
constexpr QLatin1String serviceFree("amnezia-free");
constexpr QLatin1String serviceExternalPremium("external-premium");
@@ -77,12 +77,14 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
};
}
amnezia::ServerConfigType apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
{
return static_cast<amnezia::ServerConfigType>(serverConfigObject.value(apiDefs::key::configVersion).toInt());
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(apiDefs::key::configVersion).toInt());
}
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody)
{
const int httpStatusCodeConflict = 409;
const int httpStatusCodeNotFound = 404;
@@ -90,21 +92,19 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
if (!sslErrors.empty()) {
qDebug().noquote() << sslErrors;
return amnezia::ErrorCode::ApiConfigSslError;
} else if (reply->error() == QNetworkReply::NoError) {
} else if (replyError == QNetworkReply::NoError) {
return amnezia::ErrorCode::NoError;
} else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << reply->error();
} else if (replyError == QNetworkReply::NetworkError::OperationCanceledError
|| replyError == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << replyError;
return amnezia::ErrorCode::ApiConfigTimeoutError;
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
qDebug() << reply->error();
} else if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
qDebug() << replyError;
return amnezia::ErrorCode::ApiUpdateRequestError;
} else {
QString err = reply->errorString();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << QString::fromUtf8(reply->readAll());
qDebug() << reply->error();
qDebug() << err;
qDebug() << QString::fromUtf8(responseBody);
qDebug() << replyError;
qDebug() << replyErrorString;
qDebug() << httpStatusCode;
if (httpStatusCode == httpStatusCodeConflict) {
return amnezia::ErrorCode::ApiConfigLimitError;
@@ -162,3 +162,51 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
}
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
{
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
return {};
}
QString vpnKeyText = "";
auto apiConfig = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
auto authData = serverConfigObject.value(QLatin1String("auth_data")).toObject();
const QString name = serverConfigObject.value(apiDefs::key::name).toString();
const QString description = serverConfigObject.value(apiDefs::key::description).toString();
const double configVersion = serverConfigObject.value(apiDefs::key::configVersion).toDouble();
const QString serviceType = apiConfig.value(apiDefs::key::serviceType).toString();
const QString serviceProtocol = apiConfig.value(QLatin1String("service_protocol")).toString();
const QString userCountryCode = apiConfig.value(QLatin1String("user_country_code")).toString();
const QString apiKey = authData.value(apiDefs::key::apiKey).toString();
QString vpnKeyStr = "{";
vpnKeyStr += "\"" + QString(apiDefs::key::name) + "\": \"" + name + "\", ";
vpnKeyStr += "\"" + QString(apiDefs::key::description) + "\": \"" + description + "\", ";
vpnKeyStr += "\"" + QString(apiDefs::key::configVersion) + "\": " + QString::number(static_cast<int>(configVersion)) + ", ";
vpnKeyStr += "\"" + QString(apiDefs::key::apiConfig) + "\": {";
vpnKeyStr += "\"" + QString(apiDefs::key::serviceType) + "\": \"" + serviceType + "\", ";
vpnKeyStr += "\"service_protocol\": \"" + serviceProtocol + "\", ";
vpnKeyStr += "\"user_country_code\": \"" + userCountryCode + "\"";
vpnKeyStr += "}, ";
vpnKeyStr += "\"auth_data\": {";
vpnKeyStr += "\"" + QString(apiDefs::key::apiKey) + "\": \"" + apiKey + "\"";
vpnKeyStr += "}";
vpnKeyStr += "}";
QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8();
vpnKeyCompressed = qCompress(vpnKeyCompressed, 6);
vpnKeyCompressed = vpnKeyCompressed.mid(4);
QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed;
vpnKeyText = QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
return vpnKeyText;
}

View File

@@ -16,11 +16,14 @@ namespace apiUtils
bool isPremiumServer(const QJsonObject &serverConfigObject);
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
amnezia::ServerConfigType getConfigSource(const QJsonObject &serverConfigObject);
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody);
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
QString getPremiumV2VpnKey(const QJsonObject &serverConfigObject);
}
#endif // APIUTILS_H

View File

@@ -1,57 +0,0 @@
#ifndef APICONFIGSCONTROLLER_H
#define APICONFIGSCONTROLLER_H
#include <QObject>
#include "configurators/openvpn_configurator.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/servers_model.h"
class ApiConfigsController : public QObject
{
Q_OBJECT
public:
ApiConfigsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiServicesModel> &apiServicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
QList<QString> getQrCodes();
int getQrCodesCount();
QString getVpnKey();
public slots:
ErrorCode exportNativeConfig(const QString &serverCountryCode, const QString &fileName);
ErrorCode revokeNativeConfig(const QString &serverCountryCode);
void prepareVpnKeyExport();
void copyVpnKeyToClipboard();
ErrorCode fillAvailableServices();
ErrorCode importServiceFromGateway();
ErrorCode updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig = false);
ErrorCode updateServiceFromTelegram(const int serverIndex);
ErrorCode deactivateDevice();
ErrorCode deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
bool isConfigValid();
void setCurrentProtocol(const QString &protocolName);
bool isVlessProtocol();
private:
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody);
bool isServerFromApiAlreadyExists(const QString &userCountryCode,
const QString &serviceType,
const QString &serviceProtocol) const;
bool isApiKeyExpired(int serverIndex) const;
void removeApiConfig(int serverIndex);
QList<QString> m_qrCodes;
QString m_vpnKey;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ApiServicesModel> m_apiServicesModel;
std::shared_ptr<Settings> m_settings;
};
#endif // APICONFIGSCONTROLLER_H

View File

@@ -1,364 +0,0 @@
#include "gatewayController.h"
#include <algorithm>
#include <random>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QUrl>
#include "QBlockCipher.h"
#include "QRsa.h"
#include "amnezia_application.h"
#include "core/api/apiUtils.h"
#include "core/networkUtilities.h"
#include "utilities.h"
#ifdef AMNEZIA_DESKTOP
#include "core/ipcclient.h"
#endif
namespace
{
namespace configKey
{
constexpr char aesKey[] = "aes_key";
constexpr char aesIv[] = "aes_iv";
constexpr char aesSalt[] = "aes_salt";
constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload";
}
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
constexpr QLatin1String errorResponsePattern3("Account not found.");
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
}
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent)
: QObject(parent),
m_gatewayEndpoint(gatewayEndpoint),
m_isDevEnvironment(isDevEnvironment),
m_requestTimeoutMsecs(requestTimeoutMsecs),
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
{
}
ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBody)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
}
}
#endif
QNetworkReply *reply;
reply = amnApp->networkManager()->get(request);
QEventLoop wait;
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
responseBody = reply->readAll();
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
auto requestFunction = [&request, &responseBody](const QString &url) {
request.setUrl(url);
return amnApp->networkManager()->get(request);
};
auto replyProcessingFunction = [&responseBody, &reply, &sslErrors, this](QNetworkReply *nestedReply,
const QList<QSslError> &nestedSslErrors) {
responseBody = nestedReply->readAll();
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, responseBody, false)) {
sslErrors = nestedSslErrors;
reply = nestedReply;
return true;
}
return false;
};
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
}
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
reply->deleteLater();
return errorCode;
}
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(endpoint.arg(m_gatewayEndpoint));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
}
}
#endif
QSimpleCrypto::QBlockCipher blockCipher;
QByteArray key = blockCipher.generatePrivateSalt(32);
QByteArray iv = blockCipher.generatePrivateSalt(32);
QByteArray salt = blockCipher.generatePrivateSalt(8);
QJsonObject keyPayload;
keyPayload[configKey::aesKey] = QString(key.toBase64());
keyPayload[configKey::aesIv] = QString(iv.toBase64());
keyPayload[configKey::aesSalt] = QString(salt.toBase64());
QByteArray encryptedKeyPayload;
QByteArray encryptedApiPayload;
try {
QSimpleCrypto::QRsa rsa;
EVP_PKEY *publicKey = nullptr;
try {
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
} catch (...) {
Utils::logException();
qCritical() << "error loading public key from environment variables";
return ErrorCode::ApiMissingAgwPublicKey;
}
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
EVP_PKEY_free(publicKey);
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
} catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when encrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
}
QJsonObject requestBody;
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
QNetworkReply *reply = amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
QByteArray encryptedResponseBody = reply->readAll();
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
request.setUrl(url);
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
};
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
encryptedResponseBody = nestedReply->readAll();
reply = nestedReply;
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
sslErrors = nestedSslErrors;
return false;
}
return true;
};
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
}
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
reply->deleteLater();
if (errorCode) {
return errorCode;
}
try {
responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
return ErrorCode::NoError;
} catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when decrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
}
}
QStringList GatewayController::getProxyUrls()
{
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
QStringList proxyStorageUrls;
if (m_isDevEnvironment) {
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
for (const auto &proxyStorageUrl : proxyStorageUrls) {
request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (reply->error() == QNetworkReply::NetworkError::NoError) {
auto encryptedResponseBody = reply->readAll();
reply->deleteLater();
EVP_PKEY *privateKey = nullptr;
QByteArray responseBody;
try {
if (!m_isDevEnvironment) {
QCryptographicHash hash(QCryptographicHash::Sha512);
hash.addData(key);
QByteArray hashResult = hash.result().toHex();
QByteArray key = QByteArray::fromHex(hashResult.left(64));
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
QSimpleCrypto::QBlockCipher blockCipher;
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
} else {
responseBody = encryptedResponseBody;
}
} catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
continue;
}
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
QStringList endpoints;
for (const auto &endpoint : endpointsArray) {
endpoints.push_back(endpoint.toString());
}
return endpoints;
} else {
apiUtils::checkNetworkReplyErrors(sslErrors, reply);
qDebug() << "go to the next storage endpoint";
reply->deleteLater();
}
}
return {};
}
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
const QByteArray &iv, const QByteArray &salt)
{
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << "timeout occurred";
qDebug() << reply->error();
return true;
} else if (responseBody.contains("html")) {
qDebug() << "the response contains an html tag";
return true;
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|| responseBody.contains(errorResponsePattern3)) {
return false;
} else {
qDebug() << reply->error();
return true;
}
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
if (responseBody.contains(updateRequestResponsePattern)) {
return false;
} else {
qDebug() << reply->error();
return true;
}
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
qDebug() << reply->error();
return true;
} else if (checkEncryption) {
try {
QSimpleCrypto::QBlockCipher blockCipher;
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
} catch (...) {
qDebug() << "failed to decrypt the data";
return true;
}
}
return false;
}
void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *reply,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction)
{
QStringList proxyUrls = getProxyUrls();
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
QEventLoop wait;
QList<QSslError> sslErrors;
QByteArray responseBody;
for (const QString &proxyUrl : proxyUrls) {
qDebug() << "go to the next proxy endpoint";
reply->deleteLater(); // delete the previous reply
reply = requestFunction(endpoint.arg(proxyUrl));
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (replyProcessingFunction(reply, sslErrors)) {
break;
}
}
}

View File

@@ -1,37 +0,0 @@
#ifndef GATEWAYCONTROLLER_H
#define GATEWAYCONTROLLER_H
#include <QNetworkReply>
#include <QObject>
#include "core/defs.h"
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"
#endif
class GatewayController : public QObject
{
Q_OBJECT
public:
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
private:
QStringList getProxyUrls();
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
const QByteArray &iv = "", const QByteArray &salt = "");
void bypassProxy(const QString &endpoint, QNetworkReply *reply, std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
int m_requestTimeoutMsecs;
QString m_gatewayEndpoint;
bool m_isDevEnvironment = false;
bool m_isStrictKillSwitchEnabled = false;
};
#endif // GATEWAYCONTROLLER_H

View File

@@ -1,172 +0,0 @@
#include "configController.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/servers/apiV1ServerConfig.h"
#include "core/models/servers/apiV2ServerConfig.h"
#include "core/models/servers/selfHostedServerConfig.h"
#include "core/networkUtilities.h"
#include "protocols/protocols_defs.h"
#include "settings.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/models/protocols/cloakProtocolConfig.h"
ConfigController::ConfigController(std::shared_ptr<Settings> settings, QObject *parent)
: QObject(parent), m_settings(settings)
{
}
void ConfigController::addServer(const QSharedPointer<ServerConfig> &serverConfig)
{
m_settings->addServer(serverConfig->toJson());
emit serverAdded(m_settings->serversCount() - 1);
}
void ConfigController::editServer(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex)
{
updateServerInSettings(serverConfig, serverIndex);
emit serverEdited(serverIndex);
}
void ConfigController::removeServer(int serverIndex)
{
m_settings->removeServer(serverIndex);
emit serverRemoved(serverIndex);
}
void ConfigController::setDefaultServer(int serverIndex)
{
m_settings->setDefaultServer(serverIndex);
emit defaultServerChanged(serverIndex);
}
void ConfigController::setDefaultContainer(int serverIndex, int containerIndex)
{
auto servers = m_settings->serversArray();
if (serverIndex >= servers.size()) return;
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
auto container = static_cast<DockerContainer>(containerIndex);
serverConfig->defaultContainer = ContainerProps::containerToString(container);
updateServerInSettings(serverConfig, serverIndex);
emit defaultContainerChanged(serverIndex, container);
}
bool ConfigController::isServerFromApiAlreadyExists(quint16 crc) const
{
auto servers = m_settings->serversArray();
for (const auto &server : servers) {
auto serverConfig = ServerConfig::createServerConfig(server.toObject());
if (static_cast<quint16>(serverConfig->crc) == crc) {
return true;
}
}
return false;
}
// Removed API-specific helpers; moved to ApiConfigsController
QStringList ConfigController::getAllInstalledServicesName(int serverIndex) const
{
auto servers = m_settings->serversArray();
if (serverIndex >= servers.size()) return {};
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
QStringList serviceNames;
for (auto it = serverConfig->containerConfigs.constBegin();
it != serverConfig->containerConfigs.constEnd(); ++it) {
const QString &containerName = it.key();
serviceNames.append(containerName);
}
return serviceNames;
}
void ConfigController::clearCachedProfile(int serverIndex, DockerContainer container)
{
auto servers = m_settings->serversArray();
if (serverIndex >= servers.size()) return;
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
QString containerName = ContainerProps::containerToString(container);
if (serverConfig->containerConfigs.contains(containerName)) {
auto &containerConfig = serverConfig->containerConfigs[containerName];
containerConfig.clearProfile();
updateServerInSettings(serverConfig, serverIndex);
}
}
void ConfigController::updateServerInSettings(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex)
{
m_settings->editServer(serverIndex, serverConfig->toJson());
}
bool ConfigController::isDefaultServerDefaultContainerHasSplitTunneling() const
{
int defaultServerIndex = m_settings->defaultServerIndex();
auto servers = m_settings->serversArray();
if (defaultServerIndex >= servers.size()) return false;
auto serverConfig = ServerConfig::createServerConfig(servers.at(defaultServerIndex).toObject());
if (!serverConfig->containerConfigs.contains(serverConfig->defaultContainer)) {
return false;
}
const auto &containerConfig = serverConfig->containerConfigs[serverConfig->defaultContainer];
return checkSplitTunnelingInContainer(containerConfig, serverConfig->defaultContainer);
}
bool ConfigController::checkSplitTunnelingInContainer(const ContainerConfig &containerConfig, const QString &defaultContainer) const
{
const DockerContainer containerType = ContainerProps::containerFromString(defaultContainer);
auto isWireguardHasSplit = [](const QString &nativeConfig, const QStringList &allowedIps) -> bool {
if (nativeConfig.contains("AllowedIPs") && !nativeConfig.contains("AllowedIPs = 0.0.0.0/0, ::/0")) {
return true;
}
if (!allowedIps.isEmpty() && !allowedIps.contains("0.0.0.0/0")) {
return true;
}
return false;
};
if (containerType == DockerContainer::Awg || containerType == DockerContainer::WireGuard) {
const auto protocolConfig = containerConfig.protocolConfigs.value(defaultContainer);
if (!protocolConfig) return false;
auto variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
return std::visit(
[&](const auto &ptr) -> bool {
using T = std::decay_t<decltype(ptr)>;
if constexpr (std::is_same_v<T, QSharedPointer<AwgProtocolConfig>> || std::is_same_v<T, QSharedPointer<WireGuardProtocolConfig>>) {
return isWireguardHasSplit(ptr->clientProtocolConfig.nativeConfig,
ptr->clientProtocolConfig.wireGuardData.allowedIps);
}
return false;
},
variant);
}
if (containerType == DockerContainer::Cloak || containerType == DockerContainer::OpenVpn || containerType == DockerContainer::ShadowSocks) {
const auto &protocolConfig = containerConfig.protocolConfigs.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
if (!protocolConfig) return false;
auto variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
return std::visit(
[&](const auto &ptr) -> bool {
using T = std::decay_t<decltype(ptr)>;
if constexpr (std::is_same_v<T, QSharedPointer<OpenVpnProtocolConfig>> ||
std::is_same_v<T, QSharedPointer<ShadowsocksProtocolConfig>> ||
std::is_same_v<T, QSharedPointer<CloakProtocolConfig>>) {
const auto nativeConfig = ptr->clientProtocolConfig.nativeConfig;
return (!nativeConfig.isEmpty() && !nativeConfig.contains("redirect-gateway"));
}
return false;
},
variant);
}
return false;
}

View File

@@ -1,59 +0,0 @@
#ifndef CONFIGCONTROLLER_H
#define CONFIGCONTROLLER_H
#include <QObject>
#include <QSharedPointer>
#include "core/defs.h"
#include "core/models/servers/serverConfig.h"
#include "core/models/containers/containers_defs.h"
class Settings;
using namespace amnezia;
class ConfigController : public QObject
{
Q_OBJECT
public:
explicit ConfigController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
virtual ~ConfigController() = default;
// Basic server management
virtual void addServer(const QSharedPointer<ServerConfig> &serverConfig);
virtual void editServer(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex);
virtual void removeServer(int serverIndex);
// Default settings management
void setDefaultServer(int serverIndex);
void setDefaultContainer(int serverIndex, int containerIndex);
// API server utilities
bool isServerFromApiAlreadyExists(quint16 crc) const;
// General utilities
QStringList getAllInstalledServicesName(int serverIndex) const;
void clearCachedProfile(int serverIndex, DockerContainer container);
// Split tunneling detection
bool isDefaultServerDefaultContainerHasSplitTunneling() const;
protected:
std::shared_ptr<Settings> m_settings;
// Protected helper methods for derived classes
void updateServerInSettings(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex);
bool checkSplitTunnelingInContainer(const ContainerConfig &containerConfig, const QString &defaultContainer) const;
signals:
// Common server management signals
void serverAdded(int serverIndex);
void serverEdited(int serverIndex);
void serverRemoved(int serverIndex);
void defaultServerChanged(int serverIndex);
void defaultContainerChanged(int serverIndex, DockerContainer container);
};
#endif // CONFIGCONTROLLER_H

View File

@@ -1,93 +0,0 @@
#include "connectionController.h"
#include <QtConcurrent>
#include "core/controllers/vpnConfigurationController.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "settings.h"
#include "logger.h"
#include "utilities.h"
namespace
{
Logger logger("ConnectionController");
}
ConnectionController::ConnectionController(const QSharedPointer<VpnConnection> &vpnConnection,
std::shared_ptr<Settings> settings,
QObject *parent)
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings)
{
}
QFuture<QJsonObject> ConnectionController::prepareVpnConfiguration(const QSharedPointer<ServerConfig> &serverConfig,
DockerContainer container,
const QPair<QString, QString> &dns) const
{
return QtConcurrent::run([this, serverConfig, container, dns]() -> QJsonObject {
logger.info() << "Preparing VPN configuration for container" << ContainerProps::containerToString(container);
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
QString containerName = ContainerProps::containerToString(container);
const ContainerConfig &containerConfig = serverConfig->containerConfigs.value(containerName);
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns,
serverConfig,
containerConfig,
container);
emit configurationPrepared(vpnConfiguration);
return vpnConfiguration;
});
}
QFuture<ErrorCode> ConnectionController::openConnection(const int serverIndex,
const QSharedPointer<ServerConfig> &serverConfig,
const DockerContainer container,
const ServerCredentials &credentials,
const QPair<QString, QString> &dns)
{
return QtConcurrent::run([this, serverIndex, serverConfig, container, credentials, dns]() -> ErrorCode {
if (!isServerSupported(container)) {
emit connectionError(ErrorCode::NotSupportedOnThisPlatform);
return ErrorCode::NotSupportedOnThisPlatform;
}
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
const QString containerName = ContainerProps::containerToString(container);
const ContainerConfig &containerConfig = serverConfig->containerConfigs.value(containerName);
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container);
emit connectionProgress(QString("Connecting to %1...").arg(ContainerProps::containerToString(container)));
QMetaObject::invokeMethod(m_vpnConnection.get(), "connectToVpn", Qt::QueuedConnection,
Q_ARG(int, serverIndex),
Q_ARG(ServerCredentials, credentials),
Q_ARG(DockerContainer, container),
Q_ARG(QJsonObject, vpnConfiguration));
return ErrorCode::NoError;
});
}
QFuture<ErrorCode> ConnectionController::closeConnection()
{
return QtConcurrent::run([this]() -> ErrorCode {
QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectFromVpn", Qt::QueuedConnection);
return ErrorCode::NoError;
});
}
bool ConnectionController::isServerSupported(DockerContainer container) const
{
return ContainerProps::isSupportedByCurrentPlatform(container);
}
ErrorCode ConnectionController::getLastConnectionError() const
{
return m_vpnConnection ? m_vpnConnection->lastError() : ErrorCode::NoError;
}

View File

@@ -1,57 +0,0 @@
#ifndef CONNECTIONCONTROLLER_H
#define CONNECTIONCONTROLLER_H
#include <QObject>
#include <QFuture>
#include <QSharedPointer>
#include "core/defs.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/servers/serverConfig.h"
#include "protocols/vpnprotocol.h"
#include "vpnconnection.h"
class Settings;
class ServerController;
class VpnConfigurationsController;
using namespace amnezia;
class ConnectionController : public QObject
{
Q_OBJECT
public:
explicit ConnectionController(const QSharedPointer<VpnConnection> &vpnConnection,
std::shared_ptr<Settings> settings,
QObject *parent = nullptr);
QFuture<QJsonObject> prepareVpnConfiguration(const QSharedPointer<ServerConfig> &serverConfig,
DockerContainer container,
const QPair<QString, QString> &dns) const;
QFuture<ErrorCode> openConnection(const int serverIndex,
const QSharedPointer<ServerConfig> &serverConfig,
const DockerContainer container,
const ServerCredentials &credentials,
const QPair<QString, QString> &dns);
QFuture<ErrorCode> closeConnection();
ErrorCode getLastConnectionError() const;
signals:
void configurationPrepared(const QJsonObject &vpnConfiguration);
void connectionEstablished();
void connectionTerminated();
void connectionError(ErrorCode errorCode);
void connectionProgress(const QString &message);
private:
bool isServerSupported(DockerContainer container) const;
QSharedPointer<VpnConnection> m_vpnConnection;
std::shared_ptr<Settings> m_settings;
};
#endif // CONNECTIONCONTROLLER_H

View File

@@ -3,8 +3,6 @@
#include <QDirIterator>
#include <QTranslator>
#include "core/models/clientInfo.h"
#if defined(Q_OS_ANDROID)
#include "core/installedAppsImageProvider.h"
#include "platforms/android/android_controller.h"
@@ -19,9 +17,8 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
QQmlApplicationEngine *engine, QObject *parent)
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine)
{
initCoreControllers();
initModels();
initUIControllers();
initControllers();
initSignalHandlers();
initAndroidController();
@@ -29,9 +26,8 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
initNotificationHandler();
auto locale = m_settings->getAppLanguage();
m_translator.reset(new QTranslator());
updateTranslator(locale);
updateTranslator(m_settings->getAppLanguage());
}
void CoreController::initModels()
@@ -45,48 +41,51 @@ void CoreController::initModels()
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
m_openVpnConfigModel = QSharedPointer<OpenVpnConfigModel>::create(this);
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
m_shadowSocksConfigModel = QSharedPointer<ShadowSocksConfigModel>::create(this);
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
m_cloakConfigModel = QSharedPointer<CloakConfigModel>::create(this);
m_cloakConfigModel.reset(new CloakConfigModel(this));
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
m_wireGuardConfigModel = QSharedPointer<WireGuardConfigModel>::create(this);
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
m_awgConfigModel = QSharedPointer<AwgConfigModel>::create(this);
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
m_xrayConfigModel = QSharedPointer<XrayConfigModel>::create(this);
m_xrayConfigModel.reset(new XrayConfigModel(this));
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel = QSharedPointer<Ikev2ConfigModel>::create(this);
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
#endif
m_sftpConfigModel = QSharedPointer<SftpConfigModel>::create(this);
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_socks5ConfigModel = QSharedPointer<Socks5ProxyConfigModel>::create(this);
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
m_protocolsModel.reset(new ProtocolsModel(m_openVpnConfigModel, m_shadowSocksConfigModel, m_cloakConfigModel, m_wireGuardConfigModel,
m_awgConfigModel, m_xrayConfigModel,
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel,
#endif
m_sftpConfigModel, m_socks5ConfigModel, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
auto clientManagementController = QSharedPointer<ClientManagementController>::create(m_settings, this);
m_clientManagementModel.reset(new ClientManagementModel(clientManagementController, this));
m_clientManagementUIController.reset(new ClientManagementUIController(clientManagementController, this));
m_engine->rootContext()->setContextProperty("ClientManagementUIController", m_clientManagementUIController.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
m_apiServicesModel.reset(new ApiServicesModel(this));
@@ -101,128 +100,65 @@ void CoreController::initModels()
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
m_sitesModel.reset(new SitesModel(m_splitTunnelingController, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_allowedDnsModel.reset(new AllowedDnsModel(m_dnsController, this));
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_splitTunnelingController, this));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
m_languageModel.reset(new LanguageModel(m_settingsController, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
m_newsModel.reset(new NewsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("NewsModel", m_newsModel.get());
}
void CoreController::initCoreControllers()
void CoreController::initControllers()
{
m_settingsController = QSharedPointer<SettingsController>::create(m_settings, this);
m_dnsController = QSharedPointer<DnsController>::create(m_settings, this);
m_splitTunnelingController = QSharedPointer<SplitTunnelingController>::create(m_settings, m_vpnConnection, this);
m_exportController = QSharedPointer<ExportController>::create(m_settings, this);
m_installController = QSharedPointer<InstallController>::create(m_settings, this);
}
void CoreController::initUIControllers()
{
auto coreConnectionController = QSharedPointer<ConnectionController>::create(m_vpnConnection, m_settings, this);
m_connectionController.reset(
new ConnectionUIController(m_serversModel, m_containersModel, m_clientManagementModel, coreConnectionController));
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
m_pageController.reset(new PageController(m_serversModel, m_settingsController));
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_focusController.reset(new FocusController(m_engine, this));
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
auto clientManagementController = m_clientManagementUIController->getClientManagementController();
m_exportUIController.reset(new ExportUIController(m_serversModel, m_containersModel, m_clientManagementModel, m_exportController, clientManagementController));
m_engine->rootContext()->setContextProperty("ExportController", m_exportUIController.get());
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
m_installUIController.reset(new InstallUIController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_installController, m_apiConfigsCoreController, clientManagementController));
m_engine->rootContext()->setContextProperty("InstallController", m_installUIController.get());
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
connect(m_installUIController.get(), &InstallUIController::currentContainerUpdated, m_connectionController.get(),
&ConnectionUIController::onCurrentContainerUpdated);
connect(m_installController.get(), &InstallController::profileCleared,
m_protocolsModel.get(), &ProtocolsModel::updateModel);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settingsController));
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_settingsUIController.reset(
new SettingsUIController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settingsController));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsUIController.get());
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_siteSplitUIController.reset(new SiteSplitUIController(m_splitTunnelingController, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_siteSplitUIController.get());
m_settingsController.reset(
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
m_allowedDnsUIController.reset(new AllowedDnsUIController(m_dnsController, m_allowedDnsModel));
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsUIController.get());
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_appSplitUIController.reset(new AppSplitUIController(m_splitTunnelingController, m_appSplitTunnelingModel));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitUIController.get());
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
m_systemController.reset(new SystemController());
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_apiSettingsCoreController = QSharedPointer<ApiSettingsController>::create(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings);
m_apiConfigsCoreController = QSharedPointer<ApiConfigsController>::create(m_serversModel, m_apiServicesModel, m_settings);
m_apiPremV1MigrationCoreController = QSharedPointer<ApiPremV1MigrationController>::create(m_serversModel, m_settings, this);
m_apiSettingsController.reset(
new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
m_apiSettingsUIController.reset(new ApiSettingsUIController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_apiSettingsCoreController));
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsUIController.get());
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
m_apiConfigUIController.reset(new ApiConfigUIController(m_serversModel, m_apiServicesModel, m_apiConfigsCoreController));
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigUIController.get());
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
m_apiPremV1MigrationUIController.reset(new ApiPremV1MigrationUIController(m_serversModel, m_apiPremV1MigrationCoreController));
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationUIController.get());
setupControllerSignalConnections();
}
void CoreController::setupControllerSignalConnections()
{
auto clientManagementController = m_clientManagementUIController->getClientManagementController();
connect(m_exportController.data(), &ExportController::clientAppendRequested,
clientManagementController.data(),
[clientManagementController](const DockerContainer container, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QString &clientName,
const QSharedPointer<ServerController> &serverController) {
QList<ClientInfo> clientsList;
ErrorCode result = clientManagementController->appendClient(container, credentials, containerConfig,
clientName, serverController, clientsList);
emit clientManagementController->clientAppendCompleted(result);
});
connect(m_exportController.data(), &ExportController::nativeConfigClientAppendRequested,
clientManagementController.data(),
[clientManagementController](const QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName,
const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController) {
QList<ClientInfo> clientsList;
auto nonConstProtocolConfig = QSharedPointer<ProtocolConfig>(protocolConfig);
ErrorCode result = clientManagementController->appendClient(nonConstProtocolConfig, clientName, container,
credentials, serverController, clientsList);
emit clientManagementController->nativeConfigClientAppendCompleted(result);
});
connect(clientManagementController.data(), &ClientManagementController::clientAppendCompleted,
m_exportController.data(), &ExportController::onClientAppendCompleted);
connect(clientManagementController.data(), &ClientManagementController::nativeConfigClientAppendCompleted,
m_exportController.data(), &ExportController::onNativeConfigClientAppendCompleted);
connect(m_installController.data(), &InstallController::clientAppendRequested,
clientManagementController.data(),
[clientManagementController](const DockerContainer container, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QString &clientName,
const QSharedPointer<ServerController> &serverController) {
QList<ClientInfo> clientsList;
clientManagementController->appendClient(container, credentials, containerConfig,
clientName, serverController, clientsList);
});
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
}
void CoreController::initAndroidController()
@@ -274,7 +210,7 @@ void CoreController::initAppleController()
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
emit m_pageController->goToPageHome();
m_pageController->goToPageSettingsBackup();
emit m_settingsUIController->importBackupFromOutside(filePath);
emit m_settingsController->importBackupFromOutside(filePath);
});
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
@@ -302,7 +238,7 @@ void CoreController::initSignalHandlers()
void CoreController::initNotificationHandler()
{
#ifndef Q_OS_ANDROID
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
@@ -310,11 +246,14 @@ void CoreController::initNotificationHandler()
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
static_cast<void (ConnectionUIController::*)()>(&ConnectionUIController::openConnection));
static_cast<void (ConnectionController::*)()>(&ConnectionController::openConnection));
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionUIController::closeConnection);
&ConnectionController::closeConnection);
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
#endif
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
#endif
}
void CoreController::updateTranslator(const QLocale &locale)
@@ -329,6 +268,7 @@ void CoreController::updateTranslator(const QLocale &locale)
availableTranslations << it.next();
}
// This code allow to load translation for the language only, without country code
const QString lang = locale.name().split("_").first();
const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang;
QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name());
@@ -350,16 +290,17 @@ void CoreController::updateTranslator(const QLocale &locale)
m_engine->retranslate();
emit translationsUpdated();
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
}
void CoreController::initErrorMessagesHandler()
{
connect(m_connectionController.get(), &ConnectionUIController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
emit m_pageController->showErrorMessage(errorCode);
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
});
connect(m_apiConfigUIController.get(), &ApiConfigUIController::errorOccurred, m_pageController.get(),
connect(m_apiConfigsController.get(), &ApiConfigsController::errorOccurred, m_pageController.get(),
qOverload<ErrorCode>(&PageController::showErrorMessage));
}
@@ -381,6 +322,11 @@ void CoreController::initContainerModelUpdateHandler()
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::gatewayStacksExpanded, this, [this]() {
if (m_serversModel->hasServersFromGatewayApi()) {
m_apiNewsController->fetchNews(false);
}
});
m_serversModel->resetModel();
}
@@ -392,39 +338,42 @@ void CoreController::initAdminConfigRevokedHandler()
void CoreController::initPassphraseRequestHandler()
{
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
&PageController::showPassphraseRequestDrawer);
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
&InstallController::setEncryptedPassphrase);
}
void CoreController::initTranslationsUpdatedHandler()
{
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &CoreController::updateTranslator);
connect(this, &CoreController::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
connect(this, &CoreController::translationsUpdated, m_connectionController.get(), &ConnectionUIController::onTranslationsUpdated);
connect(this, &CoreController::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
}
void CoreController::initAutoConnectHandler()
{
if (m_settingsUIController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}
}
void CoreController::initAmneziaDnsToggledHandler()
{
connect(m_settingsUIController.get(), &SettingsUIController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
}
void CoreController::initPrepareConfigHandler()
{
connect(m_connectionController.get(), &ConnectionUIController::prepareConfig, this, [this]() {
connect(m_connectionController.get(), &ConnectionController::prepareConfig, this, [this]() {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
if (!m_apiConfigUIController->isConfigValid()) {
if (!m_apiConfigsController->isConfigValid()) {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
return;
}
if (!m_installController->isConfigValid(m_serversModel->getProcessedServerCredentials())) {
if (!m_installController->isConfigValid()) {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
return;
}
@@ -435,26 +384,26 @@ void CoreController::initPrepareConfigHandler()
void CoreController::initImportPremiumV2VpnKeyHandler()
{
connect(m_apiPremV1MigrationUIController.get(), &ApiPremV1MigrationUIController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) {
connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) {
m_importController->extractConfigFromData(vpnKey);
m_importController->importConfig();
emit m_apiPremV1MigrationUIController->migrationFinished();
emit m_apiPremV1MigrationController->migrationFinished();
});
}
void CoreController::initShowMigrationDrawerHandler()
{
QTimer::singleShot(1000, this, [this]() {
if (m_apiPremV1MigrationUIController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationUIController->hasConfigsToMigration()) {
m_apiPremV1MigrationUIController->showMigrationDrawer();
if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) {
m_apiPremV1MigrationController->showMigrationDrawer();
}
});
}
void CoreController::initStrictKillSwitchHandler()
{
connect(m_settingsUIController.get(), &SettingsUIController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
&VpnConnection::onKillSwitchModeChanged);
}

View File

@@ -5,31 +5,25 @@
#include <QQmlContext>
#include <QThread>
#include "ui/controllers/api/apiConfigUIController.h"
#include "ui/controllers/api/apiSettingsUIController.h"
#include "ui/controllers/api/apiPremV1MigrationUIController.h"
#include "core/controllers/api/apiConfigController.h"
#include "core/controllers/api/apiSettingsController.h"
#include "core/controllers/api/apiPremV1MigrationController.h"
#include "core/controllers/selfhosted/clientManagementController.h"
#include "ui/controllers/appSplitUIController.h"
#include "ui/controllers/allowedDnsUIController.h"
#include "ui/controllers/connectionUIController.h"
#include "core/controllers/selfhosted/exportController.h"
#include "core/controllers/selfhosted/installController.h"
#include "ui/controllers/selfhosted/exportUIController.h"
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/systemtray_notificationhandler.h"
#endif
#include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h"
#include "ui/controllers/api/apiPremV1MigrationController.h"
#include "ui/controllers/api/apiNewsController.h"
#include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/allowedDnsController.h"
#include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h"
#include "ui/controllers/focusController.h"
#include "ui/controllers/importController.h"
#include "ui/controllers/selfhosted/installUIController.h"
#include "ui/controllers/installController.h"
#include "ui/controllers/pageController.h"
#include "ui/controllers/settingsUIController.h"
#include "ui/controllers/siteSplitUIController.h"
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "core/utils/fileUtils.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/dnsController.h"
#include "core/controllers/connectionController.h"
#include "core/controllers/splitTunnelingController.h"
#include "ui/models/allowed_dns_model.h"
#include "ui/models/containers_model.h"
@@ -43,8 +37,7 @@
#include "ui/models/api/apiDevicesModel.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/appSplitTunnelingModel.h"
#include "ui/models/selfhosted/clientManagementModel.h"
#include "ui/controllers/selfhosted/clientManagementUIController.h"
#include "ui/models/clientManagementModel.h"
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/shadowsocksConfigModel.h"
@@ -55,8 +48,9 @@
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/newsModel.h"
#ifndef Q_OS_ANDROID
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/notificationhandler.h"
#endif
@@ -73,15 +67,14 @@ public:
signals:
void translationsUpdated();
void websiteUrlChanged(const QString &newUrl);
private:
void initModels();
void initCoreControllers();
void initUIControllers();
void initSignalHandlers();
void initControllers();
void initAndroidController();
void initAppleController();
void setupControllerSignalConnections();
void initSignalHandlers();
void initNotificationHandler();
@@ -101,42 +94,33 @@ private:
void initShowMigrationDrawerHandler();
void initStrictKillSwitchHandler();
QQmlApplicationEngine *m_engine {};
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
std::shared_ptr<Settings> m_settings;
QSharedPointer<VpnConnection> m_vpnConnection;
QSharedPointer<QTranslator> m_translator;
#ifndef Q_OS_ANDROID
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
QScopedPointer<NotificationHandler> m_notificationHandler;
#endif
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
QScopedPointer<ConnectionUIController> m_connectionController;
QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<FocusController> m_focusController;
QSharedPointer<PageController> m_pageController;
QSharedPointer<ExportController> m_exportController;
QSharedPointer<InstallController> m_installController;
QScopedPointer<ExportUIController> m_exportUIController;
QScopedPointer<InstallUIController> m_installUIController;
QSharedPointer<PageController> m_pageController; // TODO
QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController;
QScopedPointer<SettingsUIController> m_settingsUIController;
QScopedPointer<SiteSplitUIController> m_siteSplitUIController;
QScopedPointer<ExportController> m_exportController;
QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<AppSplitUIController> m_appSplitUIController;
QScopedPointer<AllowedDnsUIController> m_allowedDnsUIController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QScopedPointer<AllowedDnsController> m_allowedDnsController;
QSharedPointer<ApiSettingsController> m_apiSettingsCoreController;
QSharedPointer<ApiConfigsController> m_apiConfigsCoreController;
QSharedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationCoreController;
QScopedPointer<ApiSettingsUIController> m_apiSettingsUIController;
QScopedPointer<ApiConfigUIController> m_apiConfigUIController;
QScopedPointer<ApiPremV1MigrationUIController> m_apiPremV1MigrationUIController;
QSharedPointer<SettingsController> m_settingsController;
QSharedPointer<DnsController> m_dnsController;
QSharedPointer<SplitTunnelingController> m_splitTunnelingController;
QScopedPointer<ApiSettingsController> m_apiSettingsController;
QScopedPointer<ApiConfigsController> m_apiConfigsController;
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
QScopedPointer<ApiNewsController> m_apiNewsController;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
@@ -144,27 +128,27 @@ private:
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<NewsModel> m_newsModel;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QSharedPointer<ClientManagementUIController> m_clientManagementUIController;
QSharedPointer<ApiServicesModel> m_apiServicesModel;
QSharedPointer<ApiCountryModel> m_apiCountryModel;
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
QSharedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QSharedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QSharedPointer<CloakConfigModel> m_cloakConfigModel;
QSharedPointer<XrayConfigModel> m_xrayConfigModel;
QSharedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QSharedPointer<AwgConfigModel> m_awgConfigModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
QScopedPointer<XrayConfigModel> m_xrayConfigModel;
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QScopedPointer<AwgConfigModel> m_awgConfigModel;
#ifdef Q_OS_WINDOWS
QSharedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
#endif
QSharedPointer<SftpConfigModel> m_sftpConfigModel;
QSharedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
};
#endif // CORECONTROLLER_H

View File

@@ -1,66 +0,0 @@
#include "dnsController.h"
#include "core/networkUtilities.h"
DnsController::DnsController(std::shared_ptr<Settings> settings, QObject *parent)
: QObject(parent), m_settings(settings)
{
}
bool DnsController::addDns(const QString &ip)
{
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
return false;
}
QStringList currentDnsServers = m_settings->allowedDnsServers();
if (currentDnsServers.contains(ip)) {
return false;
}
currentDnsServers.append(ip);
m_settings->setAllowedDnsServers(currentDnsServers);
emit dnsAdded(ip);
return true;
}
bool DnsController::addDnsList(const QStringList &dnsServers, bool replaceExisting)
{
QStringList currentDnsServers;
if (!replaceExisting) {
currentDnsServers = m_settings->allowedDnsServers();
}
for (const QString &ip : dnsServers) {
if (!currentDnsServers.contains(ip)) {
currentDnsServers.append(ip);
}
}
m_settings->setAllowedDnsServers(currentDnsServers);
emit dnsListAdded(dnsServers);
return true;
}
bool DnsController::removeDns(const QString &ip)
{
QStringList currentDnsServers = m_settings->allowedDnsServers();
if (!currentDnsServers.contains(ip)) {
return false;
}
currentDnsServers.removeAll(ip);
m_settings->setAllowedDnsServers(currentDnsServers);
emit dnsRemoved(ip);
return true;
}
QStringList DnsController::getAllowedDnsServers() const
{
return m_settings->allowedDnsServers();
}

View File

@@ -1,32 +0,0 @@
#ifndef DNSCONTROLLER_H
#define DNSCONTROLLER_H
#include <QObject>
#include <QStringList>
#include <QSharedPointer>
#include "settings.h"
class DnsController : public QObject
{
Q_OBJECT
public:
explicit DnsController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
// DNS management
bool addDns(const QString &ip);
bool addDnsList(const QStringList &dnsServers, bool replaceExisting = false);
bool removeDns(const QString &ip);
QStringList getAllowedDnsServers() const;
signals:
void dnsAdded(const QString &ip);
void dnsListAdded(const QStringList &dnsServers);
void dnsRemoved(const QString &ip);
private:
std::shared_ptr<Settings> m_settings;
};
#endif // DNSCONTROLLER_H

View File

@@ -0,0 +1,610 @@
#include "gatewayController.h"
#include <algorithm>
#include <functional>
#include <random>
#include <QCryptographicHash>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QPromise>
#include <QUrl>
#include "QBlockCipher.h"
#include "QRsa.h"
#include "amnezia_application.h"
#include "core/api/apiUtils.h"
#include "core/networkUtilities.h"
#include "utilities.h"
#ifdef AMNEZIA_DESKTOP
#include "core/ipcclient.h"
#endif
namespace
{
namespace configKey
{
constexpr char aesKey[] = "aes_key";
constexpr char aesIv[] = "aes_iv";
constexpr char aesSalt[] = "aes_salt";
constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload";
}
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
constexpr QLatin1String errorResponsePattern3("Account not found.");
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
}
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent)
: QObject(parent),
m_gatewayEndpoint(gatewayEndpoint),
m_isDevEnvironment(isDevEnvironment),
m_requestTimeoutMsecs(requestTimeoutMsecs),
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
{
}
GatewayController::EncryptedRequestData GatewayController::prepareRequest(const QString &endpoint, const QJsonObject &apiPayload)
{
EncryptedRequestData encRequestData;
encRequestData.errorCode = ErrorCode::NoError;
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
encRequestData.request.setTransferTimeout(m_requestTimeoutMsecs);
encRequestData.request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
encRequestData.request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
encRequestData.request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(encRequestData.request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
}
}
#endif
QSimpleCrypto::QBlockCipher blockCipher;
encRequestData.key = blockCipher.generatePrivateSalt(32);
encRequestData.iv = blockCipher.generatePrivateSalt(32);
encRequestData.salt = blockCipher.generatePrivateSalt(8);
QJsonObject keyPayload;
keyPayload[configKey::aesKey] = QString(encRequestData.key.toBase64());
keyPayload[configKey::aesIv] = QString(encRequestData.iv.toBase64());
keyPayload[configKey::aesSalt] = QString(encRequestData.salt.toBase64());
QByteArray encryptedKeyPayload;
QByteArray encryptedApiPayload;
try {
QSimpleCrypto::QRsa rsa;
EVP_PKEY *publicKey = nullptr;
try {
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
} catch (...) {
Utils::logException();
qCritical() << "error loading public key from environment variables";
encRequestData.errorCode = ErrorCode::ApiMissingAgwPublicKey;
return encRequestData;
}
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
EVP_PKEY_free(publicKey);
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), encRequestData.key, encRequestData.iv,
"", encRequestData.salt);
} catch (...) {
Utils::logException();
qCritical() << "error when encrypting the request body";
encRequestData.errorCode = ErrorCode::ApiConfigDecryptionError;
return encRequestData;
}
QJsonObject requestBody;
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
encRequestData.requestBody = QJsonDocument(requestBody).toJson();
return encRequestData;
}
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
{
EncryptedRequestData encRequestData = prepareRequest(endpoint, apiPayload);
if (encRequestData.errorCode != ErrorCode::NoError) {
return encRequestData.errorCode;
}
QNetworkReply *reply = amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(QEventLoop::ExcludeUserInputEvents);
QByteArray encryptedResponseBody = reply->readAll();
QString replyErrorString = reply->errorString();
auto replyError = reply->error();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
reply->deleteLater();
if (sslErrors.isEmpty()
&& shouldBypassProxy(replyError, encryptedResponseBody, true, encRequestData.key, encRequestData.iv, encRequestData.salt)) {
auto requestFunction = [&encRequestData, &encryptedResponseBody](const QString &url) {
encRequestData.request.setUrl(url);
return amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
};
auto replyProcessingFunction = [&encryptedResponseBody, &replyErrorString, &replyError, &httpStatusCode, &sslErrors,
&encRequestData, this](QNetworkReply *reply, const QList<QSslError> &nestedSslErrors) {
encryptedResponseBody = reply->readAll();
replyErrorString = reply->errorString();
replyError = reply->error();
httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (!sslErrors.isEmpty()
|| shouldBypassProxy(replyError, encryptedResponseBody, true, encRequestData.key, encRequestData.iv, encRequestData.salt)) {
sslErrors = nestedSslErrors;
return false;
}
return true;
};
auto serviceType = apiPayload.value(apiDefs::key::serviceType).toString("");
auto userCountryCode = apiPayload.value(apiDefs::key::userCountryCode).toString("");
bypassProxy(endpoint, serviceType, userCountryCode, requestFunction, replyProcessingFunction);
}
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, replyErrorString, replyError, httpStatusCode, encryptedResponseBody);
if (errorCode) {
return errorCode;
}
try {
QSimpleCrypto::QBlockCipher blockCipher;
responseBody =
blockCipher.decryptAesBlockCipher(encryptedResponseBody, encRequestData.key, encRequestData.iv, "", encRequestData.salt);
return ErrorCode::NoError;
} catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when decrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
}
}
QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString &endpoint, const QJsonObject apiPayload)
{
auto promise = QSharedPointer<QPromise<QPair<ErrorCode, QByteArray>>>::create();
promise->start();
EncryptedRequestData encRequestData = prepareRequest(endpoint, apiPayload);
if (encRequestData.errorCode != ErrorCode::NoError) {
promise->addResult(qMakePair(encRequestData.errorCode, QByteArray()));
promise->finish();
return promise->future();
}
QNetworkReply *reply = amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
auto sslErrors = QSharedPointer<QList<QSslError>>::create();
connect(reply, &QNetworkReply::sslErrors, [sslErrors](const QList<QSslError> &errors) { *sslErrors = errors; });
connect(reply, &QNetworkReply::finished, reply, [promise, sslErrors, encRequestData, endpoint, apiPayload, reply, this]() mutable {
QByteArray encryptedResponseBody = reply->readAll();
QString replyErrorString = reply->errorString();
auto replyError = reply->error();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
reply->deleteLater();
auto processResponse = [promise, encRequestData](const QByteArray &ecryptedResponseBody, const QList<QSslError> &sslErrors,
QNetworkReply::NetworkError replyError, const QString &replyErrorString,
int httpStatusCode) {
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, replyErrorString, replyError, httpStatusCode, ecryptedResponseBody);
if (errorCode) {
promise->addResult(qMakePair(errorCode, QByteArray()));
promise->finish();
return;
}
QSimpleCrypto::QBlockCipher blockCipher;
try {
QByteArray responseBody = blockCipher.decryptAesBlockCipher(ecryptedResponseBody, encRequestData.key, encRequestData.iv, "",
encRequestData.salt);
promise->addResult(qMakePair(ErrorCode::NoError, responseBody));
promise->finish();
} catch (...) {
Utils::logException();
qCritical() << "error when decrypting the request body";
promise->addResult(qMakePair(ErrorCode::ApiConfigDecryptionError, QByteArray()));
promise->finish();
}
};
if (sslErrors->isEmpty()
&& shouldBypassProxy(replyError, encryptedResponseBody, true, encRequestData.key, encRequestData.iv, encRequestData.salt)) {
auto serviceType = apiPayload.value(apiDefs::key::serviceType).toString("");
auto userCountryCode = apiPayload.value(apiDefs::key::userCountryCode).toString("");
QStringList baseUrls;
if (m_isDevEnvironment) {
baseUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
QStringList proxyStorageUrls;
if (!serviceType.isEmpty()) {
for (const auto &baseUrl : baseUrls) {
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
proxyStorageUrls.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)
+ ".json");
}
}
for (const auto &baseUrl : baseUrls)
proxyStorageUrls.push_back(baseUrl + "endpoints.json");
getProxyUrlsAsync(proxyStorageUrls, 0, [this, encRequestData, endpoint, processResponse](const QStringList &proxyUrls) {
getProxyUrlAsync(proxyUrls, 0, [this, encRequestData, endpoint, processResponse](const QString &proxyUrls) {
bypassProxyAsync(endpoint, proxyUrls, encRequestData, processResponse);
});
});
} else {
processResponse(encryptedResponseBody, *sslErrors, replyError, replyErrorString, httpStatusCode);
}
});
return promise->future();
}
QStringList GatewayController::getProxyUrls(const QString &serviceType, const QString &userCountryCode)
{
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
QStringList baseUrls;
if (m_isDevEnvironment) {
baseUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QStringList proxyStorageUrls;
if (!serviceType.isEmpty()) {
for (const auto &baseUrl : baseUrls) {
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
proxyStorageUrls.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + ".json");
}
}
for (const auto &baseUrl : baseUrls) {
proxyStorageUrls.push_back(baseUrl + "endpoints.json");
}
for (const auto &proxyStorageUrl : proxyStorageUrls) {
request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(QEventLoop::ExcludeUserInputEvents);
if (reply->error() == QNetworkReply::NetworkError::NoError) {
auto encryptedResponseBody = reply->readAll();
reply->deleteLater();
EVP_PKEY *privateKey = nullptr;
QByteArray responseBody;
try {
if (!m_isDevEnvironment) {
QCryptographicHash hash(QCryptographicHash::Sha512);
hash.addData(key);
QByteArray hashResult = hash.result().toHex();
QByteArray key = QByteArray::fromHex(hashResult.left(64));
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
QSimpleCrypto::QBlockCipher blockCipher;
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
} else {
responseBody = encryptedResponseBody;
}
} catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
continue;
}
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
QStringList endpoints;
for (const auto &endpoint : endpointsArray) {
endpoints.push_back(endpoint.toString());
}
return endpoints;
} else {
auto replyError = reply->error();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << replyError;
qDebug() << httpStatusCode;
qDebug() << "go to the next storage endpoint";
reply->deleteLater();
}
}
return {};
}
bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody,
bool checkEncryption, const QByteArray &key, const QByteArray &iv, const QByteArray &salt)
{
if (replyError == QNetworkReply::NetworkError::OperationCanceledError || replyError == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << "timeout occurred";
qDebug() << replyError;
return true;
} else if (responseBody.contains("html")) {
qDebug() << "the response contains an html tag";
return true;
} else if (replyError == QNetworkReply::NetworkError::ContentNotFoundError) {
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|| responseBody.contains(errorResponsePattern3)) {
return false;
} else {
qDebug() << replyError;
return true;
}
} else if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
if (responseBody.contains(updateRequestResponsePattern)) {
return false;
} else {
qDebug() << replyError;
return true;
}
} else if (replyError != QNetworkReply::NetworkError::NoError) {
qDebug() << replyError;
return true;
} else if (checkEncryption) {
try {
QSimpleCrypto::QBlockCipher blockCipher;
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
} catch (...) {
qDebug() << "failed to decrypt the data";
return true;
}
}
return false;
}
void GatewayController::bypassProxy(const QString &endpoint, const QString &serviceType, const QString &userCountryCode,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction)
{
QStringList proxyUrls = getProxyUrls(serviceType, userCountryCode);
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
QByteArray responseBody;
auto bypassFunction = [this](const QString &endpoint, const QString &proxyUrl,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply * reply, const QList<QSslError> &sslErrors)> replyProcessingFunction) {
QEventLoop wait;
QList<QSslError> sslErrors;
qDebug() << "go to the next proxy endpoint";
QNetworkReply *reply = requestFunction(endpoint.arg(proxyUrl));
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(QEventLoop::ExcludeUserInputEvents);
auto result = replyProcessingFunction(reply, sslErrors);
reply->deleteLater();
return result;
};
if (m_proxyUrl.isEmpty()) {
QNetworkRequest request;
request.setTransferTimeout(1000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
for (const QString &proxyUrl : proxyUrls) {
request.setUrl(proxyUrl + "lmbd-health");
reply = amnApp->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(QEventLoop::ExcludeUserInputEvents);
if (reply->error() == QNetworkReply::NetworkError::NoError) {
reply->deleteLater();
m_proxyUrl = proxyUrl;
if (!m_proxyUrl.isEmpty()) {
break;
}
} else {
reply->deleteLater();
}
}
}
if (!m_proxyUrl.isEmpty()) {
if (bypassFunction(endpoint, m_proxyUrl, requestFunction, replyProcessingFunction)) {
return;
}
}
for (const QString &proxyUrl : proxyUrls) {
if (bypassFunction(endpoint, proxyUrl, requestFunction, replyProcessingFunction)) {
m_proxyUrl = proxyUrl;
break;
}
}
}
void GatewayController::getProxyUrlsAsync(const QStringList proxyStorageUrls, const int currentProxyStorageIndex,
std::function<void(const QStringList &)> onComplete)
{
if (currentProxyStorageIndex >= proxyStorageUrls.size()) {
onComplete({});
return;
}
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(proxyStorageUrls[currentProxyStorageIndex]);
QNetworkReply *reply = amnApp->networkManager()->get(request);
// connect(reply, &QNetworkReply::sslErrors, this, [state](const QList<QSslError> &e) { *(state->sslErrors) = e; });
connect(reply, &QNetworkReply::finished, this, [this, proxyStorageUrls, currentProxyStorageIndex, onComplete, reply]() {
if (reply->error() == QNetworkReply::NoError) {
QByteArray encrypted = reply->readAll();
reply->deleteLater();
QByteArray responseBody;
try {
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
if (!m_isDevEnvironment) {
QCryptographicHash hash(QCryptographicHash::Sha512);
hash.addData(key);
QByteArray h = hash.result().toHex();
QByteArray decKey = QByteArray::fromHex(h.left(64));
QByteArray iv = QByteArray::fromHex(h.mid(64, 32));
QByteArray ba = QByteArray::fromBase64(encrypted);
QSimpleCrypto::QBlockCipher cipher;
responseBody = cipher.decryptAesBlockCipher(ba, decKey, iv);
} else {
responseBody = encrypted;
}
} catch (...) {
Utils::logException();
qCritical() << "error decrypting payload";
QMetaObject::invokeMethod(
this, [=]() { getProxyUrlsAsync(proxyStorageUrls, currentProxyStorageIndex + 1, onComplete); }, Qt::QueuedConnection);
return;
}
QJsonArray endpointsArray = QJsonDocument::fromJson(responseBody).array();
QStringList endpoints;
for (const QJsonValue &endpoint : endpointsArray)
endpoints.push_back(endpoint.toString());
QStringList shuffled = endpoints;
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(shuffled.begin(), shuffled.end(), generator);
onComplete(shuffled);
return;
}
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << httpStatusCode;
qDebug() << "go to the next storage endpoint";
reply->deleteLater();
QMetaObject::invokeMethod(
this, [=]() { getProxyUrlsAsync(proxyStorageUrls, currentProxyStorageIndex + 1, onComplete); }, Qt::QueuedConnection);
});
}
void GatewayController::getProxyUrlAsync(const QStringList proxyUrls, const int currentProxyIndex, std::function<void(const QString &)> onComplete)
{
if (currentProxyIndex >= proxyUrls.size()) {
onComplete("");
return;
}
QNetworkRequest request;
request.setTransferTimeout(1000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(proxyUrls[currentProxyIndex] + "lmbd-health");
QNetworkReply *reply = amnApp->networkManager()->get(request);
// connect(reply, &QNetworkReply::sslErrors, this, [state](const QList<QSslError> &e) {
// *(state->sslErrors) = e;
// });
connect(reply, &QNetworkReply::finished, this, [this, proxyUrls, currentProxyIndex, onComplete, reply]() {
reply->deleteLater();
if (reply->error() == QNetworkReply::NoError) {
m_proxyUrl = proxyUrls[currentProxyIndex];
onComplete(m_proxyUrl);
return;
}
qDebug() << "go to the next proxy endpoint";
QMetaObject::invokeMethod(this, [=]() { getProxyUrlAsync(proxyUrls, currentProxyIndex + 1, onComplete); }, Qt::QueuedConnection);
});
}
void GatewayController::bypassProxyAsync(
const QString &endpoint, const QString &proxyUrl, EncryptedRequestData encRequestData,
std::function<void(const QByteArray &, const QList<QSslError> &, QNetworkReply::NetworkError, const QString &, int)> onComplete)
{
auto sslErrors = QSharedPointer<QList<QSslError>>::create();
if (proxyUrl.isEmpty()) {
onComplete(QByteArray(), *sslErrors, QNetworkReply::InternalServerError, "empty proxy url", 0);
return;
}
QNetworkRequest request = encRequestData.request;
request.setUrl(endpoint.arg(proxyUrl));
QNetworkReply *reply = amnApp->networkManager()->post(request, encRequestData.requestBody);
connect(reply, &QNetworkReply::sslErrors, this, [sslErrors](const QList<QSslError> &errors) { *sslErrors = errors; });
connect(reply, &QNetworkReply::finished, this, [sslErrors, onComplete, reply]() {
QByteArray encryptedResponseBody = reply->readAll();
QString replyErrorString = reply->errorString();
auto replyError = reply->error();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
reply->deleteLater();
onComplete(encryptedResponseBody, *sslErrors, replyError, replyErrorString, httpStatusCode);
});
}

View File

@@ -0,0 +1,63 @@
#ifndef GATEWAYCONTROLLER_H
#define GATEWAYCONTROLLER_H
#include <QFuture>
#include <QNetworkReply>
#include <QObject>
#include <QPair>
#include <QPromise>
#include <QSharedPointer>
#include "core/defs.h"
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"
#endif
class GatewayController : public QObject
{
Q_OBJECT
public:
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
QFuture<QPair<amnezia::ErrorCode, QByteArray>> postAsync(const QString &endpoint, const QJsonObject apiPayload);
private:
struct EncryptedRequestData
{
QNetworkRequest request;
QByteArray requestBody;
QByteArray key;
QByteArray iv;
QByteArray salt;
amnezia::ErrorCode errorCode;
};
EncryptedRequestData prepareRequest(const QString &endpoint, const QJsonObject &apiPayload);
QStringList getProxyUrls(const QString &serviceType, const QString &userCountryCode);
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody, bool checkEncryption,
const QByteArray &key = "", const QByteArray &iv = "", const QByteArray &salt = "");
void bypassProxy(const QString &endpoint, const QString &serviceType, const QString &userCountryCode,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
void getProxyUrlsAsync(const QStringList proxyStorageUrls, const int currentProxyStorageIndex,
std::function<void(const QStringList &)> onComplete);
void getProxyUrlAsync(const QStringList proxyUrls, const int currentProxyIndex, std::function<void(const QString &)> onComplete);
void bypassProxyAsync(
const QString &endpoint, const QString &proxyUrl, EncryptedRequestData encRequestData,
std::function<void(const QByteArray &, const QList<QSslError> &, QNetworkReply::NetworkError, const QString &, int)> onComplete);
int m_requestTimeoutMsecs;
QString m_gatewayEndpoint;
bool m_isDevEnvironment = false;
bool m_isStrictKillSwitchEnabled = false;
inline static QString m_proxyUrl;
};
#endif // GATEWAYCONTROLLER_H

View File

@@ -1,825 +0,0 @@
#include "clientManagementController.h"
#include <QJsonDocument>
#include <QJsonObject>
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "core/models/protocols/ikev2ProtocolConfig.h"
#include "core/models/protocols/sftpProtocolConfig.h"
#include "core/models/protocols/socks5ProtocolConfig.h"
#include "core/models/protocols/torWebsiteProtocolConfig.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/models/clientInfo.h"
#include <variant>
#include "settings.h"
#include "logger.h"
#include "protocols/protocols_defs.h"
using namespace amnezia;
using namespace amnezia::config_key;
namespace
{
Logger logger("ClientManagementController");
namespace configKey
{
constexpr char clientId[] = "clientId";
constexpr char clientName[] = "clientName";
constexpr char container[] = "container";
constexpr char userData[] = "userData";
constexpr char creationDate[] = "creationDate";
constexpr char latestHandshake[] = "latestHandshake";
constexpr char dataReceived[] = "dataReceived";
constexpr char dataSent[] = "dataSent";
constexpr char allowedIps[] = "allowedIps";
}
}
ClientManagementController::ClientManagementController(std::shared_ptr<Settings> settings, QObject *parent)
: QObject(parent), m_settings(settings)
{
}
QString ClientManagementController::getClientsTableFilePath(const DockerContainer container)
{
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container));
}
return clientsTableFile;
}
void ClientManagementController::migration(const QByteArray &clientsTableString, QList<ClientInfo> &clientsList)
{
QJsonObject clientsTableObj = QJsonDocument::fromJson(clientsTableString).object();
for (auto &clientId : clientsTableObj.keys()) {
ClientInfo client;
client.clientId = clientId;
client.clientName = clientsTableObj.value(clientId).toObject().value(configKey::clientName).toString();
client.creationDate = QDateTime::currentDateTime();
clientsList.append(client);
}
}
ErrorCode ClientManagementController::updateClientsData(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
{
clientsList.clear();
ErrorCode error = ErrorCode::NoError;
QString clientsTableFile = getClientsTableFilePath(container);
const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the clientsTable file from the server";
return error;
}
QJsonArray clientsTable = QJsonDocument::fromJson(clientsTableString).array();
if (clientsTable.isEmpty()) {
migration(clientsTableString, clientsList);
const QByteArray newClientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
error = serverController->uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
} else {
clientsList = clientsFromJsonArray(clientsTable);
}
int count = 0;
switch (container) {
case DockerContainer::OpenVpn:
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak:
error = getOpenVpnClients(container, credentials, serverController, count, clientsList);
break;
case DockerContainer::WireGuard:
case DockerContainer::Awg:
error = getWireGuardClients(container, credentials, serverController, count, clientsList);
break;
case DockerContainer::Xray:
error = getXrayClients(container, credentials, serverController, count, clientsList);
break;
default:
error = ErrorCode::NoError;
break;
}
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get clients for container" << ContainerProps::containerTypeToString(container);
return error;
}
emit clientsDataUpdated(clientsList);
return ErrorCode::NoError;
}
// UI-facing methods - create ServerController internally
ErrorCode ClientManagementController::updateClientsData(const DockerContainer container, const ServerCredentials &credentials)
{
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
QList<ClientInfo> clientsList;
return updateClientsData(container, credentials, serverController, clientsList);
}
ErrorCode ClientManagementController::appendClient(const DockerContainer container, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QString &clientName,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
{
Proto protocol;
switch (container) {
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak:
protocol = Proto::OpenVpn;
break;
case DockerContainer::OpenVpn:
case DockerContainer::WireGuard:
case DockerContainer::Awg:
case DockerContainer::Xray:
protocol = ContainerProps::defaultProtocol(container);
break;
default:
return ErrorCode::NoError;
}
QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
return appendClient(protocolConfig, clientName, container, credentials, serverController, clientsList);
}
ErrorCode ClientManagementController::appendClient(QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList)
{
QString clientId;
if (container == DockerContainer::Xray) {
if (!protocolConfig) {
return ErrorCode::InternalError;
}
QJsonObject protocolConfigJson = protocolConfig->toJson();
if (!protocolConfigJson.contains("outbounds")) {
return ErrorCode::InternalError;
}
QJsonArray outbounds = protocolConfigJson.value("outbounds").toArray();
if (outbounds.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject outbound = outbounds[0].toObject();
if (!outbound.contains("settings")) {
return ErrorCode::InternalError;
}
QJsonObject settings = outbound["settings"].toObject();
if (!settings.contains("vnext")) {
return ErrorCode::InternalError;
}
QJsonArray vnext = settings["vnext"].toArray();
if (vnext.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject vnextObj = vnext[0].toObject();
if (!vnextObj.contains("users")) {
return ErrorCode::InternalError;
}
QJsonArray users = vnextObj["users"].toArray();
if (users.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject user = users[0].toObject();
if (!user.contains("id")) {
return ErrorCode::InternalError;
}
clientId = user["id"].toString();
} else {
if (!protocolConfig) {
return ErrorCode::InternalError;
}
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
std::visit([&clientId](const auto &config) -> void {
clientId = config->clientProtocolConfig.clientId;
}, variant);
}
return appendClient(clientId, clientName, container, credentials, serverController, clientsList);
}
ErrorCode ClientManagementController::appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList)
{
ErrorCode error = ErrorCode::NoError;
QList<ClientInfo> currentClients;
error = updateClientsData(container, credentials, serverController, currentClients);
if (error != ErrorCode::NoError) {
return error;
}
for (int i = 0; i < currentClients.size(); ++i) {
if (currentClients[i].clientId == clientId) {
return renameClient(i, clientName, container, credentials, serverController, currentClients, true);
}
}
ClientInfo newClient(clientId, clientName);
newClient.container = container;
currentClients.append(newClient);
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(currentClients)).toJson();
QString clientsTableFile = getClientsTableFilePath(container);
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
clientsList = currentClients;
emit clientAdded(clientId, clientName);
return ErrorCode::NoError;
}
ErrorCode ClientManagementController::renameClient(const int row, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, bool addTimeStamp)
{
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
QList<ClientInfo> clientsList;
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
return renameClient(row, clientName, container, credentials, serverController, clientsList, addTimeStamp);
}
ErrorCode ClientManagementController::renameClient(const int row, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList, bool addTimeStamp)
{
if (row < 0 || row >= clientsList.size()) {
return ErrorCode::InternalError;
}
clientsList[row].clientName = clientName;
if (addTimeStamp) {
clientsList[row].creationDate = QDateTime::currentDateTime();
}
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
QString clientsTableFile = getClientsTableFilePath(container);
ErrorCode error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
emit clientRenamed(row, clientName);
return ErrorCode::NoError;
}
ErrorCode ClientManagementController::getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList)
{
ErrorCode error = ErrorCode::NoError;
const QString wireGuardConfigFile = QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg");
const QString wireguardConfigString = serverController->getTextFileFromContainer(container, credentials, wireGuardConfigFile, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the wg conf file from the server";
return error;
}
auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts);
QStringList wireguardKeys;
for (const auto &line : configLines) {
auto configPair = line.split(" = ", Qt::SkipEmptyParts);
if (configPair.front() == "PublicKey") {
wireguardKeys.push_back(configPair.back());
}
}
for (auto &wireguardKey : wireguardKeys) {
if (!isClientExists(wireguardKey, clientsList)) {
ClientInfo client(wireguardKey, QString("Client %1").arg(count));
client.container = container;
clientsList.append(client);
count++;
}
}
return error;
}
ErrorCode ClientManagementController::getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList)
{
ErrorCode error = ErrorCode::NoError;
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
const QString configString = serverController->getTextFileFromContainer(container, credentials, serverConfigPath, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the xray server config file from the server";
return error;
}
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
if (serverConfig.isNull()) {
logger.error() << "Failed to parse xray server config JSON";
return ErrorCode::InternalError;
}
if (!serverConfig.object().contains("inbounds") || serverConfig.object()["inbounds"].toArray().isEmpty()) {
logger.error() << "Invalid xray server config structure";
return ErrorCode::InternalError;
}
const QJsonObject inbound = serverConfig.object()["inbounds"].toArray()[0].toObject();
if (!inbound.contains("settings")) {
logger.error() << "Missing settings in xray inbound config";
return ErrorCode::InternalError;
}
const QJsonObject settings = inbound["settings"].toObject();
if (!settings.contains("clients")) {
logger.error() << "Missing clients in xray settings config";
return ErrorCode::InternalError;
}
const QJsonArray clients = settings["clients"].toArray();
for (const auto &clientValue : clients) {
const QJsonObject clientObj = clientValue.toObject();
if (!clientObj.contains("id")) {
logger.error() << "Missing id in xray client config";
continue;
}
QString clientId = clientObj["id"].toString();
QString xrayDefaultUuid = serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, error);
xrayDefaultUuid.replace("\n", "");
if (!isClientExists(clientId, clientsList) && clientId != xrayDefaultUuid) {
ClientInfo client(clientId, QString("Client %1").arg(count));
client.container = container;
clientsList.append(client);
count++;
}
}
return error;
}
ErrorCode ClientManagementController::getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList)
{
Q_UNUSED(container);
Q_UNUSED(credentials);
Q_UNUSED(serverController);
count = clientsList.size();
return ErrorCode::NoError;
}
ErrorCode ClientManagementController::wgShow(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data)
{
if (container != DockerContainer::WireGuard && container != DockerContainer::Awg) {
return ErrorCode::NoError;
}
ErrorCode error = ErrorCode::NoError;
QString stdOut;
std::function<ErrorCode(const QString &, libssh::Client &)> cbReadStdOut = [&](const QString &data, libssh::Client &){
stdOut += data + "\n";
return ErrorCode::NoError;
};
const QString command = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%1'").arg("wg show all");
QString script = serverController->replaceVars(command, serverController->generateVarsForContainer(credentials, container));
error = serverController->runScript(credentials, script, cbReadStdOut);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to execute wg show command";
return error;
}
if (stdOut.isEmpty()) {
return error;
}
const auto getStrValue = [](const auto str) { return str.mid(str.indexOf(":") + 1).trimmed(); };
const auto parts = stdOut.split('\n');
const auto peerList = parts.filter("peer:");
const auto latestHandshakeList = parts.filter("latest handshake:");
const auto transferredDataList = parts.filter("transfer:");
const auto allowedIpsList = parts.filter("allowed ips:");
if (allowedIpsList.isEmpty() || latestHandshakeList.isEmpty() || transferredDataList.isEmpty() || peerList.isEmpty()) {
return error;
}
const auto changeHandshakeFormat = [](QString &latestHandshake) {
const std::vector<std::pair<QString, QString>> replaceMap = { { " days", "d" }, { " hours", "h" }, { " minutes", "m" },
{ " seconds", "s" }, { " day", "d" }, { " hour", "h" },
{ " minute", "m" }, { " second", "s" } };
for (const auto &item : replaceMap) {
latestHandshake.replace(item.first, item.second);
}
};
for (int i = 0; i < peerList.size() && i < transferredDataList.size() && i < latestHandshakeList.size() && i < allowedIpsList.size(); ++i) {
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
auto latestHandshake = getStrValue(latestHandshakeList[i]);
auto serverBytesReceived = transferredData.front().trimmed();
auto serverBytesSent = transferredData.back().trimmed();
auto allowedIps = getStrValue(allowedIpsList[i]);
changeHandshakeFormat(latestHandshake);
serverBytesReceived.chop(QStringLiteral(" received").length());
serverBytesSent.chop(QStringLiteral(" sent").length());
data.push_back({ getStrValue(peerList[i]), latestHandshake, serverBytesSent, serverBytesReceived, allowedIps });
}
return error;
}
ErrorCode ClientManagementController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController)
{
QList<ClientInfo> clientsList;
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
return revokeOpenVpn(row, container, credentials, serverIndex, serverController, clientsList);
}
ErrorCode ClientManagementController::revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController)
{
QList<ClientInfo> clientsList;
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
return revokeWireGuard(row, container, credentials, serverController, clientsList);
}
ErrorCode ClientManagementController::revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController)
{
QList<ClientInfo> clientsList;
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
return revokeXray(row, container, credentials, serverController, clientsList);
}
ErrorCode ClientManagementController::revokeClient(const int row, const DockerContainer container,
const ServerCredentials &credentials, const int serverIndex)
{
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
QList<ClientInfo> clientsList;
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
return revokeClient(row, container, credentials, serverIndex, serverController, clientsList);
}
ErrorCode ClientManagementController::revokeClient(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList)
{
if (row < 0 || row >= clientsList.size()) {
return ErrorCode::InternalError;
}
QString clientId = clientsList[row].clientId;
ErrorCode errorCode = ErrorCode::NoError;
switch(container) {
case DockerContainer::OpenVpn:
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak: {
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, serverController, clientsList);
break;
}
case DockerContainer::WireGuard:
case DockerContainer::Awg: {
errorCode = revokeWireGuard(row, container, credentials, serverController, clientsList);
break;
}
case DockerContainer::Xray: {
errorCode = revokeXray(row, container, credentials, serverController, clientsList);
break;
}
default: {
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
}
if (errorCode == ErrorCode::NoError) {
emit clientRevoked(row);
}
return errorCode;
}
ErrorCode ClientManagementController::revokeClient(const ContainerConfig &containerConfig, const DockerContainer container,
const ServerCredentials &credentials, const int serverIndex,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
{
Proto protocol;
switch(container) {
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak: {
protocol = Proto::OpenVpn;
break;
}
case DockerContainer::OpenVpn:
case DockerContainer::WireGuard:
case DockerContainer::Awg:
case DockerContainer::Xray: {
protocol = ContainerProps::defaultProtocol(container);
break;
}
default: {
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
}
auto protocolConfig = ContainerProps::getProtocolConfigFromContainer(protocol, containerConfig);
QString clientId;
if (container == DockerContainer::Xray) {
if (!protocolConfig) {
return ErrorCode::InternalError;
}
QJsonObject protocolConfigJson = protocolConfig->toJson();
if (!protocolConfigJson.contains("outbounds")) {
return ErrorCode::InternalError;
}
QJsonArray outbounds = protocolConfigJson.value("outbounds").toArray();
if (outbounds.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject outbound = outbounds[0].toObject();
if (!outbound.contains("settings")) {
return ErrorCode::InternalError;
}
QJsonObject settings = outbound["settings"].toObject();
if (!settings.contains("vnext")) {
return ErrorCode::InternalError;
}
QJsonArray vnext = settings["vnext"].toArray();
if (vnext.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject vnextObj = vnext[0].toObject();
if (!vnextObj.contains("users")) {
return ErrorCode::InternalError;
}
QJsonArray users = vnextObj["users"].toArray();
if (users.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject user = users[0].toObject();
if (!user.contains("id")) {
return ErrorCode::InternalError;
}
clientId = user["id"].toString();
} else {
if (!protocolConfig) {
return ErrorCode::InternalError;
}
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
std::visit([&clientId](const auto &config) -> void {
clientId = config->clientProtocolConfig.clientId;
}, variant);
}
for (int i = 0; i < clientsList.size(); i++) {
if (clientsList[i].clientId == clientId) {
return revokeClient(i, container, credentials, serverIndex, serverController, clientsList);
}
}
return ErrorCode::InternalError;
}
ErrorCode ClientManagementController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList)
{
if (row < 0 || row >= clientsList.size()) {
return ErrorCode::InternalError;
}
QString clientId = clientsList[row].clientId;
ErrorCode error = ErrorCode::NoError;
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
};
const QString revokeClientScript = "sudo docker exec -i $CONTAINER_NAME bash -c './easyrsa --batch revoke %1'";
QString script = serverController->replaceVars(revokeClientScript.arg(clientId),
serverController->generateVarsForContainer(credentials, container));
error = serverController->runScript(credentials, script, cbReadStdOut);
if (error != ErrorCode::NoError) {
return error;
}
clientsList.removeAt(row);
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
QString clientsTableFile = getClientsTableFilePath(container);
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
return ErrorCode::NoError;
}
ErrorCode ClientManagementController::revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
{
if (row < 0 || row >= clientsList.size()) {
return ErrorCode::InternalError;
}
QString clientId = clientsList[row].clientId;
ErrorCode error = ErrorCode::NoError;
const QString wireGuardConfigFile = QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg");
const QString wireguardConfigString = serverController->getTextFileFromContainer(container, credentials, wireGuardConfigFile, error);
if (error != ErrorCode::NoError) {
return error;
}
QStringList configLines = wireguardConfigString.split("\n");
QStringList newConfigLines;
bool skipPeer = false;
for (const QString &line : configLines) {
if (line.startsWith("[Peer]")) {
skipPeer = false;
}
if (line.contains("PublicKey") && line.contains(clientId)) {
skipPeer = true;
continue;
}
if (!skipPeer) {
newConfigLines.append(line);
}
}
QString newConfig = newConfigLines.join("\n");
error = serverController->uploadTextFileToContainer(container, credentials, newConfig.toUtf8(), wireGuardConfigFile);
if (error != ErrorCode::NoError) {
return error;
}
clientsList.removeAt(row);
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
QString clientsTableFile = getClientsTableFilePath(container);
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
return ErrorCode::NoError;
}
ErrorCode ClientManagementController::revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
{
if (row < 0 || row >= clientsList.size()) {
return ErrorCode::InternalError;
}
QString clientId = clientsList[row].clientId;
ErrorCode error = ErrorCode::NoError;
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
const QString configString = serverController->getTextFileFromContainer(container, credentials, serverConfigPath, error);
if (error != ErrorCode::NoError) {
return error;
}
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
if (serverConfig.isNull()) {
return ErrorCode::InternalError;
}
QJsonObject configObj = serverConfig.object();
QJsonArray inbounds = configObj["inbounds"].toArray();
for (int i = 0; i < inbounds.size(); i++) {
QJsonObject inbound = inbounds[i].toObject();
QJsonObject settings = inbound["settings"].toObject();
QJsonArray clients = settings["clients"].toArray();
for (int j = 0; j < clients.size(); j++) {
QJsonObject clientObj = clients[j].toObject();
if (clientObj["id"].toString() == clientId) {
clients.removeAt(j);
settings["clients"] = clients;
inbound["settings"] = settings;
inbounds[i] = inbound;
break;
}
}
}
configObj["inbounds"] = inbounds;
QJsonDocument newServerConfig(configObj);
error = serverController->uploadTextFileToContainer(container, credentials, newServerConfig.toJson(), serverConfigPath);
if (error != ErrorCode::NoError) {
return error;
}
clientsList.removeAt(row);
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
QString clientsTableFile = getClientsTableFilePath(container);
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
return ErrorCode::NoError;
}
QList<ClientInfo> ClientManagementController::clientsFromJsonArray(const QJsonArray &jsonArray)
{
QList<ClientInfo> clientsList;
for (const auto &value : jsonArray) {
clientsList.append(ClientInfo(value.toObject()));
}
return clientsList;
}
QJsonArray ClientManagementController::clientsToJsonArray(const QList<ClientInfo> &clientsList)
{
QJsonArray jsonArray;
for (const auto &client : clientsList) {
jsonArray.append(client.toJson());
}
return jsonArray;
}
bool ClientManagementController::isClientExists(const QString &clientId, const QList<ClientInfo> &clientsList)
{
for (const auto &client : clientsList) {
if (client.clientId == clientId) {
return true;
}
}
return false;
}

View File

@@ -1,133 +0,0 @@
#ifndef CLIENTMANAGEMENTCONTROLLER_H
#define CLIENTMANAGEMENTCONTROLLER_H
#include <QObject>
#include <QSharedPointer>
#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <vector>
#include "core/defs.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/models/clientInfo.h"
class ServerController;
class Settings;
using namespace amnezia;
struct WgShowData
{
QString clientId;
QString latestHandshake;
QString dataReceived;
QString dataSent;
QString allowedIps;
};
class ClientManagementController : public QObject
{
Q_OBJECT
public:
explicit ClientManagementController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
std::shared_ptr<Settings> getSettings() const { return m_settings; }
// UI-facing methods (no ServerController parameter - created internally)
ErrorCode updateClientsData(const DockerContainer container, const ServerCredentials &credentials);
ErrorCode renameClient(const int row, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, bool addTimeStamp = false);
ErrorCode revokeClient(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex);
// Core methods using ClientInfo model
ErrorCode updateClientsData(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
ErrorCode appendClient(const DockerContainer container, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QString &clientName,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
ErrorCode appendClient(QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList);
ErrorCode appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList);
ErrorCode renameClient(const int row, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList, bool addTimeStamp = false);
ErrorCode revokeClient(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList);
ErrorCode revokeClient(const ContainerConfig &containerConfig, const DockerContainer container,
const ServerCredentials &credentials, const int serverIndex,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
// WireGuard specific operations
ErrorCode wgShow(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data);
signals:
void adminConfigRevoked(const DockerContainer container);
void clientsDataUpdated(const QList<ClientInfo> &clientsList);
void clientAdded(const QString &clientId, const QString &clientName);
void clientRenamed(const int row, const QString &newName);
void clientRevoked(const int row);
void clientAppendCompleted(ErrorCode errorCode);
void nativeConfigClientAppendCompleted(ErrorCode errorCode);
private:
// Protocol-specific client management
ErrorCode getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList);
ErrorCode getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList);
ErrorCode getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList);
// Protocol-specific client revocation
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController,
QList<ClientInfo> &clientsList);
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
ErrorCode revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
// Internal wrappers to fetch clients list then delegate to detailed versions
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController);
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController);
ErrorCode revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController);
// Helper methods
bool isClientExists(const QString &clientId, const QList<ClientInfo> &clientsList);
void migration(const QByteArray &clientsTableString, QList<ClientInfo> &clientsList);
QString getClientsTableFilePath(const DockerContainer container);
// JSON serialization for persistence only
static QList<ClientInfo> clientsFromJsonArray(const QJsonArray &jsonArray);
static QJsonArray clientsToJsonArray(const QList<ClientInfo> &clientsList);
std::shared_ptr<Settings> m_settings;
};
#endif // CLIENTMANAGEMENTCONTROLLER_H

View File

@@ -1,353 +0,0 @@
#include "exportController.h"
#include <QBuffer>
#include <QDataStream>
#include <QJsonDocument>
#include "core/controllers/vpnConfigurationController.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/controllers/selfhosted/clientManagementController.h"
#include "core/qrCodeUtils.h"
#include <QEventLoop>
#include <QTimer>
ExportController::ExportController(std::shared_ptr<Settings> settings,
QObject *parent)
: QObject(parent),
m_settings(settings),
m_lastClientAppendResult(ErrorCode::NoError),
m_lastNativeConfigAppendResult(ErrorCode::NoError),
m_waitingForClientAppend(false),
m_waitingForNativeConfigAppend(false)
{
}
ExportConfigResult ExportController::generateFullAccessConfig(const QSharedPointer<ServerConfig> &serverConfig)
{
ExportConfigResult result;
result.errorCode = ErrorCode::NoError;
// Create a copy of the ServerConfig and clean last_config from protocol configs
auto modifiedServerConfig = QSharedPointer<ServerConfig>::create(*serverConfig);
for (auto &containerConfig : modifiedServerConfig->containerConfigs) {
for (auto &protocolConfig : containerConfig.protocolConfigs) {
// Protocol configs will automatically exclude last_config when serialized to JSON for export
// No need to manually remove it here as the toJson() method handles this
}
}
QByteArray compressedConfig = QJsonDocument(modifiedServerConfig->toJson()).toJson();
compressedConfig = qCompress(compressedConfig, 8);
result.config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
result.qrCodes = generateQrCodeSeries(compressedConfig);
return result;
}
ExportConfigResult ExportController::generateConnectionConfig(const QString &clientName,
const ServerCredentials &credentials,
const DockerContainer container,
const ContainerConfig &containerConfig,
const QSharedPointer<ServerConfig> &serverConfig,
const QPair<QString, QString> &dnsSettings)
{
ExportConfigResult result;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
// Use the provided ContainerConfig directly
ContainerConfig modifiedContainerConfig = containerConfig;
result.errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, modifiedContainerConfig);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
m_waitingForClientAppend = true;
emit clientAppendRequested(container, credentials, modifiedContainerConfig, clientName, serverController);
QEventLoop loop;
QTimer timer;
timer.setSingleShot(true);
timer.setInterval(30000);
connect(this, &ExportController::onClientAppendCompleted, &loop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start();
loop.exec();
m_waitingForClientAppend = false;
result.errorCode = m_lastClientAppendResult;
if (result.errorCode != ErrorCode::NoError) {
return result;
}
// Create a modified ServerConfig for export with only the specific container
auto exportServerConfig = QSharedPointer<ServerConfig>::create(*serverConfig);
// Remove credentials (they are not needed in export)
exportServerConfig->containerConfigs.clear();
// Add only the specific container being exported
QString containerName = ContainerProps::containerToString(container);
exportServerConfig->containerConfigs.insert(containerName, modifiedContainerConfig);
exportServerConfig->defaultContainer = containerName;
exportServerConfig->dns1 = dnsSettings.first;
exportServerConfig->dns2 = dnsSettings.second;
QByteArray compressedConfig = QJsonDocument(exportServerConfig->toJson()).toJson();
compressedConfig = qCompress(compressedConfig, 8);
result.config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
result.qrCodes = generateQrCodeSeries(compressedConfig);
return result;
}
ExportConfigResult ExportController::generateOpenVpnConfig(const QString &clientName,
const ServerCredentials &credentials,
const DockerContainer container,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig)
{
ExportConfigResult result;
QJsonObject nativeConfig;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
Proto protocol = Proto::OpenVpn;
if (container == DockerContainer::Cloak || container == DockerContainer::ShadowSocks) {
protocol = Proto::OpenVpn;
} else {
protocol = ContainerProps::defaultProtocol(container);
}
result.errorCode = generateNativeConfig(container, clientName, protocol, credentials,
containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
result.qrCodes = generateQrCodeSeries(result.config.toUtf8());
return result;
}
ExportConfigResult ExportController::generateWireGuardConfig(const QString &clientName,
const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig)
{
ExportConfigResult result;
QJsonObject nativeConfig;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
result.errorCode = generateNativeConfig(DockerContainer::WireGuard, clientName, Proto::WireGuard,
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
result.qrCodes << generateQrCode(result.config.toUtf8());
return result;
}
ExportConfigResult ExportController::generateAwgConfig(const QString &clientName,
const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig)
{
ExportConfigResult result;
QJsonObject nativeConfig;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
result.errorCode = generateNativeConfig(DockerContainer::Awg, clientName, Proto::Awg,
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
result.qrCodes << generateQrCode(result.config.toUtf8());
return result;
}
ExportConfigResult ExportController::generateShadowSocksConfig(const ServerCredentials &credentials,
const DockerContainer container,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig)
{
ExportConfigResult result;
QJsonObject nativeConfig;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
Proto protocol = Proto::ShadowSocks;
if (container == DockerContainer::Cloak) {
protocol = Proto::ShadowSocks;
} else {
protocol = ContainerProps::defaultProtocol(container);
}
result.errorCode = generateNativeConfig(container, "", protocol, credentials,
containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
result.nativeConfigString = QString("%1:%2@%3:%4")
.arg(nativeConfig.value("method").toString(),
nativeConfig.value("password").toString(),
nativeConfig.value("server").toString(),
nativeConfig.value("server_port").toString());
result.nativeConfigString = "ss://" + result.nativeConfigString.toUtf8().toBase64();
result.qrCodes << generateQrCode(result.nativeConfigString.toUtf8());
return result;
}
ExportConfigResult ExportController::generateCloakConfig(const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig)
{
ExportConfigResult result;
QJsonObject nativeConfig;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
result.errorCode = generateNativeConfig(DockerContainer::Cloak, "", Proto::Cloak,
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
nativeConfig.remove(config_key::transport_proto);
nativeConfig.insert("ProxyMethod", "shadowsocks");
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
return result;
}
ExportConfigResult ExportController::generateXrayConfig(const QString &clientName,
const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig)
{
ExportConfigResult result;
QJsonObject nativeConfig;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
result.errorCode = generateNativeConfig(DockerContainer::Xray, clientName, Proto::Xray,
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
return result;
}
ErrorCode ExportController::generateNativeConfig(const DockerContainer container, const QString &clientName,
const Proto &protocol, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QPair<QString, QString> &dnsSettings,
bool isApiConfig, QJsonObject &jsonNativeConfig,
const QSharedPointer<ServerController> &serverController)
{
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
// Use the provided ContainerConfig directly
ContainerConfig modifiedContainerConfig = containerConfig;
QString protocolConfigString;
ErrorCode errorCode = vpnConfigurationController.createProtocolConfigString(isApiConfig, dnsSettings, credentials,
container, modifiedContainerConfig,
protocol, protocolConfigString);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
jsonNativeConfig = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = modifiedContainerConfig.protocolConfigs.value(protocolName);
m_waitingForNativeConfigAppend = true;
emit nativeConfigClientAppendRequested(protocolConfig, clientName, container, credentials, serverController);
QEventLoop loop;
QTimer timer;
timer.setSingleShot(true);
timer.setInterval(30000);
connect(this, &ExportController::onNativeConfigClientAppendCompleted, &loop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start();
loop.exec();
m_waitingForNativeConfigAppend = false;
errorCode = m_lastNativeConfigAppendResult;
}
return errorCode;
}
void ExportController::onClientAppendCompleted(ErrorCode errorCode)
{
if (m_waitingForClientAppend) {
m_lastClientAppendResult = errorCode;
}
}
void ExportController::onNativeConfigClientAppendCompleted(ErrorCode errorCode)
{
if (m_waitingForNativeConfigAppend) {
m_lastNativeConfigAppendResult = errorCode;
}
}
QList<QString> ExportController::generateQrCodeSeries(const QByteArray &data)
{
return qrCodeUtils::generateQrCodeImageSeries(data);
}
QString ExportController::generateQrCode(const QByteArray &data)
{
auto qr = qrCodeUtils::generateQrCode(data);
return qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
}

View File

@@ -1,117 +0,0 @@
#ifndef EXPORT_CORE_CONTROLLER_H
#define EXPORT_CORE_CONTROLLER_H
#include <QObject>
#include <QSharedPointer>
#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <QString>
#include "core/defs.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/models/servers/serverConfig.h"
class ServerController;
class Settings;
using namespace amnezia;
struct ExportConfigResult
{
QString config;
QString nativeConfigString;
QList<QString> qrCodes;
ErrorCode errorCode;
};
class ExportController : public QObject
{
Q_OBJECT
public:
explicit ExportController(std::shared_ptr<Settings> settings,
QObject *parent = nullptr);
signals:
void clientAppendRequested(const DockerContainer container, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QString &clientName,
const QSharedPointer<ServerController> &serverController);
void nativeConfigClientAppendRequested(const QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName,
const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController);
void configGenerated(const ExportConfigResult &result);
void clientManagementError(ErrorCode errorCode);
public slots:
void onClientAppendCompleted(ErrorCode errorCode);
void onNativeConfigClientAppendCompleted(ErrorCode errorCode);
ExportConfigResult generateFullAccessConfig(const QSharedPointer<ServerConfig> &serverConfig);
ExportConfigResult generateConnectionConfig(const QString &clientName,
const ServerCredentials &credentials,
const DockerContainer container,
const ContainerConfig &containerConfig,
const QSharedPointer<ServerConfig> &serverConfig,
const QPair<QString, QString> &dnsSettings);
ExportConfigResult generateOpenVpnConfig(const QString &clientName,
const ServerCredentials &credentials,
const DockerContainer container,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig = false);
ExportConfigResult generateWireGuardConfig(const QString &clientName,
const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig = false);
ExportConfigResult generateAwgConfig(const QString &clientName,
const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig = false);
ExportConfigResult generateShadowSocksConfig(const ServerCredentials &credentials,
const DockerContainer container,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig = false);
ExportConfigResult generateCloakConfig(const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig = false);
ExportConfigResult generateXrayConfig(const QString &clientName,
const ServerCredentials &credentials,
const ContainerConfig &containerConfig,
const QPair<QString, QString> &dnsSettings,
bool isApiConfig = false);
private:
ErrorCode generateNativeConfig(const DockerContainer container, const QString &clientName,
const Proto &protocol, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QPair<QString, QString> &dnsSettings,
bool isApiConfig, QJsonObject &jsonNativeConfig,
const QSharedPointer<ServerController> &serverController);
QList<QString> generateQrCodeSeries(const QByteArray &data);
QString generateQrCode(const QByteArray &data);
std::shared_ptr<Settings> m_settings;
ErrorCode m_lastClientAppendResult;
ErrorCode m_lastNativeConfigAppendResult;
bool m_waitingForClientAppend;
bool m_waitingForNativeConfigAppend;
};
#endif // EXPORT_CORE_CONTROLLER_H

View File

@@ -1,397 +0,0 @@
#include "installController.h"
#include <QDesktopServices>
#include <QDir>
#include <QEventLoop>
#include <QJsonDocument>
#include <QRandomGenerator>
#include <QStandardPaths>
#include <QtConcurrent>
#include "core/api/apiUtils.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/controllers/vpnConfigurationController.h"
#include "core/models/servers/selfHostedServerConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/networkUtilities.h"
#include "logger.h"
#include "utilities.h"
namespace
{
Logger logger("CoreInstallController");
}
InstallController::InstallController(std::shared_ptr<Settings> settings,
QObject *parent)
: QObject(parent),
m_settings(settings)
{
}
InstallResult InstallController::installContainer(DockerContainer container, int port, TransportProto transportProto,
const ServerCredentials &serverCredentials, bool shouldCreateServer,
const QString &privateKeyPassphrase)
{
InstallResult result;
result.errorCode = ErrorCode::NoError;
result.isServiceInstall = (ContainerProps::containerService(container) == ServiceType::Other);
result.isInstalledContainerFound = false;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
ContainerConfig containerConfig = generateContainerConfig(container, port, transportProto);
if (shouldCreateServer && isServerAlreadyExists(serverCredentials)) {
result.errorCode = ErrorCode::InternalError;
return result;
}
QMap<DockerContainer, ContainerConfig> installedContainers;
ErrorCode errorCode = getAlreadyInstalledContainers(serverCredentials, serverController, installedContainers);
if (errorCode != ErrorCode::NoError) {
result.errorCode = errorCode;
return result;
}
QSharedPointer<ServerConfig> serverConfig;
if (shouldCreateServer) {
errorCode = installServer(container, installedContainers, serverCredentials,
serverController, result.message, serverConfig);
} else {
if (installedContainers.contains(container)) {
result.errorCode = ErrorCode::InternalError;
return result;
}
errorCode = installContainer(container, installedContainers, serverCredentials,
serverController, result.message);
}
if (errorCode != ErrorCode::NoError) {
result.errorCode = errorCode;
return result;
}
result.errorCode = ErrorCode::NoError;
return result;
}
InstallResult InstallController::scanServerForInstalledContainers(const ServerCredentials &serverCredentials)
{
InstallResult result;
result.errorCode = ErrorCode::NoError;
result.isInstalledContainerFound = false;
result.isServiceInstall = false;
QMap<DockerContainer, ContainerConfig> installedContainers;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
result.errorCode = getAlreadyInstalledContainers(serverCredentials, serverController, installedContainers);
if (result.errorCode == ErrorCode::NoError) {
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
auto container = iterator.key();
ContainerConfig containerConfig = iterator.value();
if (ContainerProps::isSupportedByCurrentPlatform(container)) {
result.errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, container, containerConfig);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
emit clientAppendRequested(container, serverCredentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()),
serverController);
}
result.isInstalledContainerFound = true;
}
}
return result;
}
ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController,
QMap<DockerContainer, ContainerConfig> &installedContainers)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
QString script = QString("sudo docker ps --format '{{.Names}} {{.Ports}}'");
ErrorCode errorCode = serverController->runScript(credentials, script, cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
auto containersInfo = stdOut.split("\n");
for (auto &containerInfo : containersInfo) {
if (containerInfo.isEmpty()) {
continue;
}
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z0-9]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
QRegularExpressionMatch containerAndPortMatch = containerAndPortRegExp.match(containerInfo);
if (containerAndPortMatch.hasMatch()) {
QString containerName = containerAndPortMatch.captured(1);
QString port = containerAndPortMatch.captured(2);
QString transportProto = containerAndPortMatch.captured(3);
DockerContainer container = ContainerProps::containerFromString(containerName);
if (container != DockerContainer::None) {
ContainerConfig containerConfig;
containerConfig.containerName = ContainerProps::containerToString(container);
auto containerProto = ContainerProps::defaultProtocol(container);
auto containerProtoString = ProtocolProps::protoToString(containerProto);
// Create appropriate protocol config based on container type
QSharedPointer<ProtocolConfig> protocolConfig;
// Create a temporary QJsonObject to construct the protocol config
QJsonObject protoConfigObject;
protoConfigObject.insert(config_key::port, port);
protoConfigObject.insert(config_key::transport_proto, transportProto);
// Create the appropriate protocol config based on type
switch (containerProto) {
case Proto::OpenVpn:
protocolConfig = QSharedPointer<OpenVpnProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::WireGuard:
protocolConfig = QSharedPointer<WireGuardProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::Awg:
protocolConfig = QSharedPointer<AwgProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::Cloak:
protocolConfig = QSharedPointer<CloakProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::ShadowSocks:
protocolConfig = QSharedPointer<ShadowsocksProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::Xray:
case Proto::SSXray:
protocolConfig = QSharedPointer<XrayProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::Ikev2:
protocolConfig = QSharedPointer<Ikev2ProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::Sftp:
protocolConfig = QSharedPointer<SftpProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
case Proto::Socks5Proxy:
protocolConfig = QSharedPointer<Socks5ProtocolConfig>::create(protoConfigObject, containerProtoString);
break;
default:
continue; // Skip unknown protocols
}
if (protocolConfig) {
containerConfig.protocolConfigs.insert(containerProtoString, protocolConfig);
installedContainers.insert(container, containerConfig);
}
}
}
}
return ErrorCode::NoError;
}
ErrorCode InstallController::installServer(const DockerContainer container,
const QMap<DockerContainer, ContainerConfig> &installedContainers,
const ServerCredentials &serverCredentials,
const QSharedPointer<ServerController> &serverController,
QString &finishMessage, QSharedPointer<ServerConfig> &serverConfig)
{
if (installedContainers.size() > 1) {
finishMessage += tr("\nAdded containers that were already installed on the server");
}
// Create a SelfHostedServerConfig and populate it properly
serverConfig = QSharedPointer<SelfHostedServerConfig>::create(QJsonObject());
auto selfHostedConfig = qSharedPointerCast<SelfHostedServerConfig>(serverConfig);
selfHostedConfig->hostName = serverCredentials.hostName;
selfHostedConfig->serverCredentials = serverCredentials;
selfHostedConfig->name = m_settings->nextAvailableServerName();
selfHostedConfig->defaultContainer = ContainerProps::containerToString(container);
selfHostedConfig->type = amnezia::ServerConfigType::SelfHosted;
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
auto containerConfig = iterator.value();
QString containerName = ContainerProps::containerToString(iterator.key());
if (ContainerProps::isSupportedByCurrentPlatform(iterator.key())) {
auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(),
containerConfig);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
}
selfHostedConfig->containerConfigs.insert(containerName, containerConfig);
}
int serverIndex = m_settings->nextAvailableServerIndex();
m_settings->setServerConfig(serverIndex, serverConfig->toJson());
m_settings->setDefaultServerIndex(serverIndex);
finishMessage = tr("Server '%1' was successfully added").arg(serverCredentials.hostName);
return ErrorCode::NoError;
}
ErrorCode InstallController::installContainer(const DockerContainer container,
const QMap<DockerContainer, ContainerConfig> &installedContainers,
const ServerCredentials &serverCredentials,
const QSharedPointer<ServerController> &serverController,
QString &finishMessage)
{
bool isInstalledContainerAddedToGui = false;
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
QList<QJsonObject> allContainerConfigs;
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
QJsonObject containerConfig = iterator.value();
if (ContainerProps::isSupportedByCurrentPlatform(container)) {
auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(), containerConfig);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
allContainerConfigs.append(containerConfig);
} else {
allContainerConfigs.append(containerConfig);
}
if (container != iterator.key()) {
isInstalledContainerAddedToGui = true;
}
}
if (isInstalledContainerAddedToGui) {
finishMessage += tr("\nAlready installed containers were found on the server. "
"All installed containers have been added to the application");
}
finishMessage = tr("Container '%1' was successfully added").arg(ContainerProps::containerHumanNames().value(container));
return ErrorCode::NoError;
}
bool InstallController::isServerAlreadyExists(const ServerCredentials &serverCredentials) const
{
for (int i = 0; i < m_settings->serversCount(); i++) {
QJsonObject serverConfig = m_settings->serverConfig(i);
ServerCredentials existingCredentials = ServerCredentials::fromServerConfig(serverConfig);
if (serverCredentials.hostName == existingCredentials.hostName &&
serverCredentials.port == existingCredentials.port) {
return true;
}
}
return false;
}
ContainerConfig InstallController::generateContainerConfig(const DockerContainer container, int port,
const TransportProto transportProto)
{
ContainerConfig containerConfig;
containerConfig.containerName = ContainerProps::containerToString(container);
auto mainProto = ContainerProps::defaultProtocol(container);
for (auto protocol : ContainerProps::protocolsForContainer(container)) {
QSharedPointer<ProtocolConfig> protocolConfig;
if (protocol == mainProto) {
// Create the appropriate protocol config based on the protocol type
if (protocol == Proto::Awg && container == DockerContainer::Awg) {
// Use the VpnConfigurationsController to create proper AwgProtocolConfig
// For now, create a basic protocol config and set values
protocolConfig = QSharedPointer<AwgProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
// Set server protocol config
awgConfig->serverProtocolConfig.port = QString::number(port);
awgConfig->serverProtocolConfig.transportProto = ProtocolProps::transportProtoToString(transportProto, protocol);
// Generate AWG-specific parameters
awgConfig->serverProtocolConfig.junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
awgConfig->serverProtocolConfig.junkPacketMinSize = QString::number(10);
awgConfig->serverProtocolConfig.junkPacketMaxSize = QString::number(50);
int s1 = QRandomGenerator::global()->bounded(15, 150);
int s2 = QRandomGenerator::global()->bounded(15, 150);
QSet<int> usedValues;
usedValues.insert(s1);
while (usedValues.contains(s2) || s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) {
s2 = QRandomGenerator::global()->bounded(15, 150);
}
usedValues.insert(s2);
awgConfig->serverProtocolConfig.initPacketJunkSize = QString::number(s1);
awgConfig->serverProtocolConfig.responsePacketJunkSize = QString::number(s2);
QSet<QString> headersValue;
while (headersValue.size() != 4) {
auto max = (std::numeric_limits<qint32>::max)();
headersValue.insert(QString::number(QRandomGenerator::global()->bounded(5, max)));
}
auto headersValueList = headersValue.values();
awgConfig->serverProtocolConfig.initPacketMagicHeader = headersValueList.at(0);
awgConfig->serverProtocolConfig.responsePacketMagicHeader = headersValueList.at(1);
awgConfig->serverProtocolConfig.underloadPacketMagicHeader = headersValueList.at(2);
awgConfig->serverProtocolConfig.transportPacketMagicHeader = headersValueList.at(3);
} else {
// For other protocols, create basic protocol configs
// This will need to be expanded for each protocol type
protocolConfig = QSharedPointer<ProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
// For now, store basic port and transport info as JSON until all protocol configs are properly structured
QJsonObject tempConfig;
tempConfig.insert(config_key::port, QString::number(port));
tempConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(transportProto, protocol));
if (container == DockerContainer::Sftp) {
tempConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
tempConfig.insert(config_key::password, Utils::getRandomString(16));
} else if (container == DockerContainer::Socks5Proxy) {
tempConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName);
tempConfig.insert(config_key::password, Utils::getRandomString(16));
}
// Store this in the protocol config's internal JSON for now
// TODO: Replace with proper protocol config structure
protocolConfig = QSharedPointer<ProtocolConfig>::create(tempConfig, ProtocolProps::protoToString(protocol));
}
} else {
// Non-main protocols get basic configs
protocolConfig = QSharedPointer<ProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
}
containerConfig.protocolConfigs.insert(ProtocolProps::protoToString(protocol), protocolConfig);
}
return containerConfig;
}
ErrorCode InstallController::checkSshConnection(const ServerCredentials &serverCredentials, const QString &privateKeyPassphrase)
{
Q_UNUSED(privateKeyPassphrase);
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
ErrorCode errorCode = ErrorCode::NoError;
serverController->checkSshConnection(serverCredentials, errorCode);
return errorCode;
}

View File

@@ -1,111 +0,0 @@
#ifndef INSTALL_CORE_CONTROLLER_H
#define INSTALL_CORE_CONTROLLER_H
#include <QObject>
#include <QSharedPointer>
#include <QJsonArray>
#include <QJsonObject>
#include <QMap>
#include <QRegularExpression>
#include "core/defs.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "core/models/servers/serverConfig.h"
class ServerController;
class Settings;
using namespace amnezia;
struct InstallResult
{
ErrorCode errorCode;
QString message;
bool isServiceInstall;
bool isInstalledContainerFound;
};
class InstallController : public QObject
{
Q_OBJECT
public:
explicit InstallController(std::shared_ptr<Settings> settings,
QObject *parent = nullptr);
// Main installation operations
InstallResult installContainer(DockerContainer container, int port, TransportProto transportProto,
const ServerCredentials &serverCredentials, bool shouldCreateServer,
const QString &privateKeyPassphrase = QString());
InstallResult scanServerForInstalledContainers(const ServerCredentials &serverCredentials);
InstallResult updateContainer(const DockerContainer container, const ServerCredentials &serverCredentials,
const ContainerConfig &containerConfig);
// Server management operations
ErrorCode removeProcessedServer(const ServerCredentials &serverCredentials);
ErrorCode rebootProcessedServer(const ServerCredentials &serverCredentials);
ErrorCode removeAllContainers(const ServerCredentials &serverCredentials);
ErrorCode removeProcessedContainer(const DockerContainer container, const ServerCredentials &serverCredentials);
ErrorCode removeApiConfig(const QSharedPointer<ServerConfig> &serverConfig);
ErrorCode clearCachedProfile(const ServerCredentials &serverCredentials);
// Utility methods
ErrorCode mountSftpDrive(const ServerCredentials &serverCredentials, const QString &port,
const QString &password, const QString &username);
QString getNextAvailableServerName() const;
ErrorCode addEmptyServer(const ServerCredentials &serverCredentials);
bool isConfigValid(const ServerCredentials &serverCredentials) const;
ErrorCode checkSshConnection(const ServerCredentials &serverCredentials, const QString &privateKeyPassphrase);
signals:
void clientAppendRequested(const DockerContainer container, const ServerCredentials &credentials,
const ContainerConfig &containerConfig, const QString &clientName,
const QSharedPointer<ServerController> &serverController);
void containerInstalled(const InstallResult &result);
void serverScanned(const InstallResult &result);
void containerUpdated(const InstallResult &result);
void serverRebooted(const QString &message);
void serverRemoved(const QString &message);
void allContainersRemoved(const QString &message);
void containerRemoved(const QString &message);
void installationError(ErrorCode errorCode);
void wrongInstallationUser(const QString &message);
void serverIsBusy(bool isBusy);
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
void noInstalledContainers();
private:
// Installation helpers
ErrorCode installServer(const DockerContainer container,
const QMap<DockerContainer, ContainerConfig> &installedContainers,
const ServerCredentials &serverCredentials,
const QSharedPointer<ServerController> &serverController,
QString &finishMessage, QSharedPointer<ServerConfig> &serverConfig);
ErrorCode installContainer(const DockerContainer container,
const QMap<DockerContainer, ContainerConfig> &installedContainers,
const ServerCredentials &serverCredentials,
const QSharedPointer<ServerController> &serverController,
QString &finishMessage);
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController,
QMap<DockerContainer, ContainerConfig> &installedContainers);
ContainerConfig generateContainerConfig(const DockerContainer container, int port,
const TransportProto transportProto);
bool isServerAlreadyExists(const ServerCredentials &serverCredentials) const;
std::shared_ptr<Settings> m_settings;
};
#endif // INSTALL_CORE_CONTROLLER_H

View File

@@ -1,140 +0,0 @@
#include "selfhostedConfigController.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "core/networkUtilities.h"
#include "settings.h"
#include "logger.h"
namespace
{
Logger logger("SelfhostedConfigController");
}
SelfhostedConfigController::SelfhostedConfigController(std::shared_ptr<Settings> settings, QObject *parent)
: ConfigController(settings, parent)
{
m_isAmneziaDnsEnabled = m_settings->useAmneziaDns();
}
void SelfhostedConfigController::toggleAmneziaDns(bool enabled)
{
m_isAmneziaDnsEnabled = enabled;
m_settings->setUseAmneziaDns(enabled);
emit amneziaDnsToggled(enabled);
}
QPair<QString, QString> SelfhostedConfigController::getDnsPair(int serverIndex) const
{
auto servers = m_settings->serversArray();
if (serverIndex >= servers.size()) return {};
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
QPair<QString, QString> dns;
bool isDnsContainerInstalled = isAmneziaDnsContainerInstalled(serverIndex);
dns.first = serverConfig->dns1;
dns.second = serverConfig->dns2;
if (isDnsContainerInstalled && m_isAmneziaDnsEnabled) {
dns.first = serverConfig->hostName;
dns.second = "";
}
return dns;
}
bool SelfhostedConfigController::isAmneziaDnsContainerInstalled(int serverIndex) const
{
auto servers = m_settings->serversArray();
if (serverIndex >= servers.size()) return false;
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
for (const auto &container : serverConfig->containerConfigs) {
auto containerType = ContainerProps::containerFromString(container.containerName);
if (containerType == DockerContainer::Dns) {
return true;
}
}
return false;
}
QMap<QString, QSharedPointer<ProtocolConfig>> SelfhostedConfigController::getProtocolConfigs(const QVector<QSharedPointer<ProtocolConfig>> &protocols)
{
QMap<QString, QSharedPointer<ProtocolConfig>> protocolConfigs;
for (const auto &config : protocols) {
Proto protocol = ProtocolProps::protoFromString(config->protocolName);
if (isProtocolSupported(protocol)) {
protocolConfigs.insert(config->protocolName, config);
}
}
return protocolConfigs;
}
void SelfhostedConfigController::updateProtocolConfiguration(Proto protocol, const QSharedPointer<ProtocolConfig> &protocolConfig)
{
if (!isProtocolSupported(protocol)) {
logger.warning() << "Protocol not supported:" << ProtocolProps::protoToString(protocol);
return;
}
logger.info() << "Updating protocol configuration for:" << ProtocolProps::protoToString(protocol);
emit protocolConfigUpdated(protocol, protocolConfig);
}
QSharedPointer<ProtocolConfig> SelfhostedConfigController::createProtocolConfig(Proto protocol)
{
if (!isProtocolSupported(protocol)) {
logger.warning() << "Attempting to create unsupported protocol config:" << ProtocolProps::protoToString(protocol);
return nullptr;
}
switch (protocol) {
case Proto::Awg:
return QSharedPointer<AwgProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::Awg));
case Proto::Cloak:
return QSharedPointer<CloakProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::Cloak));
case Proto::OpenVpn:
return QSharedPointer<OpenVpnProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::OpenVpn));
case Proto::ShadowSocks:
return QSharedPointer<ShadowsocksProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::ShadowSocks));
case Proto::WireGuard:
return QSharedPointer<WireGuardProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::WireGuard));
case Proto::Xray:
return QSharedPointer<XrayProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::Xray));
default:
logger.warning() << "Unknown protocol in createProtocolConfig:" << ProtocolProps::protoToString(protocol);
return nullptr;
}
}
bool SelfhostedConfigController::isProtocolSupported(Proto protocol) const
{
switch (protocol) {
case Proto::Awg:
case Proto::Cloak:
case Proto::OpenVpn:
case Proto::ShadowSocks:
case Proto::WireGuard:
case Proto::Xray:
return true;
default:
return false;
}
}

View File

@@ -1,45 +0,0 @@
#ifndef SELFHOSTEDCONFIGCONTROLLER_H
#define SELFHOSTEDCONFIGCONTROLLER_H
#include "../configController.h"
#include "core/models/containers/containerConfig.h"
#include "core/models/protocols/protocolConfig.h"
#include <QMap>
using namespace amnezia;
class SelfhostedConfigController : public ConfigController
{
Q_OBJECT
public:
explicit SelfhostedConfigController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
// Self-hosted specific functionality
// Amnezia DNS management
void toggleAmneziaDns(bool enabled);
QPair<QString, QString> getDnsPair(int serverIndex) const;
bool isAmneziaDnsContainerInstalled(int serverIndex) const;
// Protocol management (from ProtocolConfigController)
QMap<QString, QSharedPointer<ProtocolConfig>> getProtocolConfigs(const QVector<QSharedPointer<ProtocolConfig>> &protocols);
void updateProtocolConfiguration(Proto protocol, const QSharedPointer<ProtocolConfig> &protocolConfig);
QSharedPointer<ProtocolConfig> createProtocolConfig(Proto protocol);
bool isProtocolSupported(Proto protocol) const;
private:
bool m_isAmneziaDnsEnabled;
// Helper methods
bool checkSplitTunnelingInContainer(const ContainerConfig &containerConfig,
const QString &defaultContainer) const;
signals:
// Self-hosted specific signals
void amneziaDnsToggled(bool enabled);
void protocolConfigUpdated(Proto protocol, const QSharedPointer<ProtocolConfig> &config);
};
#endif // SELFHOSTEDCONFIGCONTROLLER_H

View File

@@ -8,15 +8,6 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "core/models/protocols/sftpProtocolConfig.h"
#include "core/models/protocols/socks5ProtocolConfig.h"
#include <QPointer>
#include <QTemporaryFile>
#include <QThread>
@@ -31,22 +22,14 @@
#include <chrono>
#include <thread>
#include "core/models/containers/containers_defs.h"
#include "containers/containers_defs.h"
#include "core/networkUtilities.h"
#include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "logger.h"
#include "settings.h"
#include "utilities.h"
#include "configurators/awg_configurator.h"
#include "configurators/cloak_configurator.h"
#include "configurators/ikev2_configurator.h"
#include "configurators/openvpn_configurator.h"
#include "configurators/shadowsocks_configurator.h"
#include "configurators/wireguard_configurator.h"
#include "configurators/xray_configurator.h"
#include "core/models/protocols/sftpProtocolConfig.h"
#include "core/models/protocols/socks5ProtocolConfig.h"
#include "vpnConfigurationController.h"
namespace
{
@@ -123,10 +106,10 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
QString runner =
QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash"));
e = runScript(credentials, replaceVars(runner, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
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, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
runScript(credentials, replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
return e;
}
@@ -149,14 +132,14 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
// mkdir
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"").arg(path);
e = runScript(credentials, replaceVars(mkdir, generateVarsForContainer(credentials, container)));
e = runScript(credentials, replaceVars(mkdir, genVarsForScript(credentials, container)));
if (e)
return e;
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
generateVarsForContainer(credentials, container)),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
@@ -164,7 +147,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
generateVarsForContainer(credentials, container)),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
@@ -172,7 +155,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
generateVarsForContainer(credentials, container)),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
@@ -184,7 +167,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return ErrorCode::ServerContainerMissingError;
}
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), generateVarsForContainer(credentials, container)));
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
return e;
}
@@ -253,10 +236,10 @@ ErrorCode ServerController::removeAllContainers(const ServerCredentials &credent
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
{
return runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), generateVarsForContainer(credentials, container)));
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), genVarsForScript(credentials, container)));
}
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate)
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate)
{
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
ErrorCode e = ErrorCode::NoError;
@@ -316,8 +299,8 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
return startupContainerWorker(credentials, container, config);
}
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig)
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
QJsonObject &newConfig)
{
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
@@ -333,20 +316,86 @@ ErrorCode ServerController::updateContainer(const ServerCredentials &credentials
}
}
bool ServerController::isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig)
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
{
const auto &mainProto = ContainerProps::defaultProtocol(container);
const QString protocolName = ProtocolProps::protoToString(mainProto);
Proto mainProto = ContainerProps::defaultProtocol(container);
const auto oldProtocolConfig = oldConfig.protocolConfigs.value(protocolName);
const auto newProtocolConfig = newConfig.protocolConfigs.value(protocolName);
if (!oldProtocolConfig || !newProtocolConfig) {
return true; // If either config is missing, reinstall is required
const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
if (container == DockerContainer::OpenVpn) {
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto)
!= newProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto))
return true;
if (oldProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort))
return true;
}
// Use the existing isServerSettingsEqual method from ProtocolConfig
return !oldProtocolConfig->isServerSettingsEqual(newProtocolConfig);
if (container == DockerContainer::Cloak) {
if (oldProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort))
return true;
}
if (container == DockerContainer::ShadowSocks) {
if (oldProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
return true;
}
if (container == DockerContainer::Awg) {
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|| (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
!= newProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize))
|| (oldProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize)
!= newProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize))
|| (oldProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize)
!= newProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize))
|| (oldProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize)
!= newProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize))
|| (oldProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader)
!= newProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader))
|| (oldProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader)
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
// || (oldProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)
// != newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize))
// || (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)
// != newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize))
return true;
}
if (container == DockerContainer::WireGuard) {
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|| (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
return true;
}
if (container == DockerContainer::Socks5Proxy) {
return true;
}
if (container == DockerContainer::Xray) {
if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) {
return true;
}
}
return false;
}
ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container)
@@ -366,7 +415,7 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
};
ErrorCode error =
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), generateVarsForContainer(credentials, DockerContainer::None)),
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr);
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
@@ -378,17 +427,17 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
return error;
}
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
// create folder on host
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), generateVarsForContainer(credentials, container)));
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
}
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
QString dockerFilePath = amnezia::server::getDockerfileFolder(container) + "/Dockerfile";
QString scriptString = QString("sudo rm %1").arg(dockerFilePath);
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, generateVarsForContainer(credentials, container)));
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, genVarsForScript(credentials, container)));
if (errorCode)
return errorCode;
@@ -409,7 +458,7 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
ErrorCode error =
runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::build_container), generateVarsForContainer(credentials, container, config)),
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
cbReadStdOut, cbReadStdErr);
if (stdOut.contains("doesn't work on cgroups v2"))
@@ -422,7 +471,7 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
return error;
}
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config)
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -432,7 +481,7 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
ErrorCode e = runScript(credentials,
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
generateVarsForContainer(credentials, container, config)),
genVarsForScript(credentials, container, config)),
cbReadStdOut);
if (stdOut.contains("address already in use"))
@@ -445,7 +494,7 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
return e;
}
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config)
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -459,16 +508,15 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr
ErrorCode e = runContainerScript(credentials, container,
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
generateVarsForContainer(credentials, container, config)),
genVarsForScript(credentials, container, config)),
cbReadStdOut, cbReadStdErr);
// ensure header is included where needed; call into controller utility if accessible
VpnConfigurationsController::updateContainerConfigAfterInstallation(container, config, stdOut);
return e;
}
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
@@ -476,7 +524,7 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
return ErrorCode::NoError;
}
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, generateVarsForContainer(credentials, container, config)),
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, genVarsForScript(credentials, container, config)),
"/opt/amnezia/start.sh");
if (e)
return e;
@@ -484,105 +532,138 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
return runScript(credentials,
replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && "
"/opt/amnezia/start.sh\"",
generateVarsForContainer(credentials, container, config)));
genVarsForScript(credentials, container, config)));
}
ServerController::Vars ServerController::generateVarsForContainer(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config)
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &config)
{
// For VPN containers, use configurator pattern
if (ContainerProps::containerService(container) != ServiceType::Other) {
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QScopedPointer<ConfiguratorBase> configurator;
// Create the appropriate configurator for this protocol
switch (protocol) {
case Proto::OpenVpn:
configurator.reset(new OpenVpnConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
break;
case Proto::ShadowSocks:
configurator.reset(new ShadowSocksConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
break;
case Proto::Cloak:
configurator.reset(new CloakConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
break;
case Proto::WireGuard:
configurator.reset(new WireguardConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){}), false));
break;
case Proto::Awg:
configurator.reset(new AwgConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
break;
case Proto::Ikev2:
configurator.reset(new Ikev2Configurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
break;
case Proto::Xray:
case Proto::SSXray:
configurator.reset(new XrayConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
break;
default:
continue;
}
if (configurator) {
QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = config.protocolConfigs.value(protocolName);
return configurator->generateProtocolVars(credentials, container, protocolConfig);
}
}
const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
const QJsonObject &xrayConfig = config.value(ProtocolProps::protoToString(Proto::Xray)).toObject();
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
const QJsonObject &socks5ProxyConfig = config.value(ProtocolProps::protoToString(Proto::Socks5Proxy)).toObject();
Vars vars;
vars.append({ { "$REMOTE_HOST", credentials.hostName } });
// OpenVPN vars
vars.append({ { "$OPENVPN_SUBNET_IP",
openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) } });
vars.append({ { "$OPENVPN_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) } });
vars.append({ { "$OPENVPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) } });
vars.append({ { "$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) } });
vars.append({ { "$OPENVPN_TRANSPORT_PROTO",
openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) } });
bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
vars.append({ { "$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" } });
vars.append({ { "$OPENVPN_CIPHER", openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) } });
vars.append({ { "$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) } });
bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
vars.append({ { "$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" } });
if (!isTlsAuth) {
// erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig
vars.append({ { "$OPENVPN_TA_KEY", "" } });
}
// Handle non-VPN services (SFTP, Socks5) with direct variable generation
Vars vars;
// Common variables that apply to all containers
vars.append({{"$REMOTE_HOST", credentials.hostName}});
vars.append({{"$CONTAINER_NAME", ContainerProps::containerToString(container)}});
vars.append({{"$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container)}});
vars.append({{"$PRIMARY_SERVER_DNS", m_settings->primaryDns()}});
vars.append({{"$SECONDARY_SERVER_DNS", m_settings->secondaryDns()}});
vars.append({ { "$OPENVPN_ADDITIONAL_CLIENT_CONFIG",
openvpnConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig) } });
vars.append({ { "$OPENVPN_ADDITIONAL_SERVER_CONFIG",
openvpnConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig) } });
// ShadowSocks vars
vars.append({ { "$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) } });
vars.append({ { "$SHADOWSOCKS_LOCAL_PORT",
ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) } });
vars.append({ { "$SHADOWSOCKS_CIPHER", ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) } });
vars.append({ { "$CONTAINER_NAME", ContainerProps::containerToString(container) } });
vars.append({ { "$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container) } });
// Cloak vars
vars.append({ { "$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) } });
vars.append({ { "$FAKE_WEB_SITE_ADDRESS", cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) } });
// Xray vars
vars.append({ { "$XRAY_SITE_NAME", xrayConfig.value(config_key::site).toString(protocols::xray::defaultSite) } });
vars.append({ { "$XRAY_SERVER_PORT", xrayConfig.value(config_key::port).toString(protocols::xray::defaultPort) } });
// Wireguard vars
vars.append({ { "$WIREGUARD_SUBNET_IP",
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
vars.append({ { "$WIREGUARD_SUBNET_CIDR",
wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) } });
vars.append({ { "$WIREGUARD_SUBNET_MASK",
wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) } });
vars.append({ { "$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) } });
// IPsec vars
vars.append({ { "$IPSEC_VPN_L2TP_NET", "192.168.42.0/24" } });
vars.append({ { "$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250" } });
vars.append({ { "$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1" } });
vars.append({ { "$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24" } });
vars.append({ { "$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250" } });
vars.append({ { "$IPSEC_VPN_SHA2_TRUNCBUG", "yes" } });
vars.append({ { "$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes" } });
vars.append({ { "$IPSEC_VPN_DISABLE_IKEV2", "no" } });
vars.append({ { "$IPSEC_VPN_DISABLE_L2TP", "no" } });
vars.append({ { "$IPSEC_VPN_DISABLE_XAUTH", "no" } });
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
vars.append({ { "$SFTP_PORT", sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) } });
vars.append({ { "$SFTP_USER", sftpConfig.value(config_key::userName).toString() } });
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
// Amnezia wireguard vars
vars.append({ { "$AWG_SUBNET_IP",
amneziaWireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
vars.append({ { "$AWG_SERVER_PORT", amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
vars.append({ { "$JUNK_PACKET_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
vars.append({ { "$JUNK_PACKET_MIN_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMinSize).toString() } });
vars.append({ { "$JUNK_PACKET_MAX_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMaxSize).toString() } });
vars.append({ { "$INIT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::initPacketJunkSize).toString() } });
vars.append({ { "$RESPONSE_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::responsePacketJunkSize).toString() } });
vars.append({ { "$INIT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } });
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).toString() } });
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } });
vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } });
// Socks5 proxy vars
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
auto username = socks5ProxyConfig.value(config_key::userName).toString();
auto password = socks5ProxyConfig.value(config_key::password).toString();
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
vars.append({ { "$SOCKS5_USER", socks5user } });
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
QString serverIp = (container != DockerContainer::Awg && container != DockerContainer::WireGuard && container != DockerContainer::Xray)
? NetworkUtilities::getIPAddress(credentials.hostName)
: credentials.hostName;
if (!serverIp.isEmpty()) {
vars.append({{"$SERVER_IP_ADDRESS", serverIp}});
}
// Handle container-specific variables for non-VPN services
if (container == DockerContainer::Sftp) {
QString protocolName = ProtocolProps::protoToString(Proto::Sftp);
auto sftpConfig = qSharedPointerCast<SftpProtocolConfig>(config.protocolConfigs.value(protocolName));
if (sftpConfig) {
QString port = sftpConfig->serverProtocolConfig.port;
if (port.isEmpty()) {
port = QString::number(ProtocolProps::defaultPort(Proto::Sftp));
}
vars.append({{"$SFTP_PORT", port}});
vars.append({{"$SFTP_USER", sftpConfig->serverProtocolConfig.userName}});
vars.append({{"$SFTP_PASSWORD", sftpConfig->serverProtocolConfig.password}});
}
} else if (container == DockerContainer::Socks5Proxy) {
QString protocolName = ProtocolProps::protoToString(Proto::Socks5Proxy);
auto socks5Config = qSharedPointerCast<Socks5ProtocolConfig>(config.protocolConfigs.value(protocolName));
if (socks5Config) {
QString port = socks5Config->serverProtocolConfig.port;
if (port.isEmpty()) {
port = protocols::socks5Proxy::defaultPort;
}
vars.append({{"$SOCKS5_PROXY_PORT", port}});
const QString &username = socks5Config->serverProtocolConfig.userName;
const QString &password = socks5Config->serverProtocolConfig.password;
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
vars.append({{"$SOCKS5_USER", socks5user}});
vars.append({{"$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong"}});
}
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
} else {
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
}
return vars;
@@ -612,7 +693,7 @@ void ServerController::cancelInstallation()
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
{
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), generateVarsForContainer(credentials, DockerContainer::None)));
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
}
QString ServerController::replaceVars(const QString &script, const Vars &vars)
@@ -624,7 +705,7 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
return s;
}
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
if (container == DockerContainer::Dns) {
return ErrorCode::NoError;
@@ -641,37 +722,15 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
};
const Proto protocol = ContainerProps::defaultProtocol(container);
const QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = config.protocolConfigs.value(protocolName);
const QString containerString = ProtocolProps::protoToString(protocol);
const QJsonObject containerConfig = config.value(containerString).toObject();
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
QString port = QString::number(ProtocolProps::defaultPort(protocol)); // default
QString transportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol); // default
if (protocolConfig) {
if (auto openVpnConfig = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig)) {
port = openVpnConfig->serverProtocolConfig.port;
transportProto = openVpnConfig->serverProtocolConfig.transportProto;
} else if (auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig)) {
port = wgConfig->serverProtocolConfig.port;
transportProto = wgConfig->serverProtocolConfig.transportProto;
} else if (auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig)) {
port = awgConfig->serverProtocolConfig.port;
transportProto = awgConfig->serverProtocolConfig.transportProto;
} else if (auto xrayConfig = qSharedPointerCast<XrayProtocolConfig>(protocolConfig)) {
port = xrayConfig->serverProtocolConfig.port;
transportProto = xrayConfig->serverProtocolConfig.transportProto;
} else if (auto shadowsocksConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig)) {
port = shadowsocksConfig->serverProtocolConfig.port;
} else if (auto cloakConfig = qSharedPointerCast<CloakProtocolConfig>(protocolConfig)) {
port = cloakConfig->serverProtocolConfig.port;
} else if (auto sftpConfig = qSharedPointerCast<SftpProtocolConfig>(protocolConfig)) {
port = sftpConfig->serverProtocolConfig.port;
} else if (auto socks5Config = qSharedPointerCast<Socks5ProtocolConfig>(protocolConfig)) {
port = socks5Config->serverProtocolConfig.port;
}
}
QString defaultPort("%1");
QString port = containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
QString defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
// TODO reimplement with netstat
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
@@ -687,12 +746,12 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
tcpProtoScript.append(" | grep LISTEN");
ErrorCode errorCode =
runScript(credentials, replaceVars(tcpProtoScript, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
runScript(credentials, replaceVars(tcpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
errorCode = runScript(credentials, replaceVars(udpProtoScript, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
errorCode = runScript(credentials, replaceVars(udpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
@@ -709,7 +768,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
script = script.append(" | grep LISTEN");
}
ErrorCode errorCode = runScript(credentials, replaceVars(script, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
@@ -733,7 +792,7 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
};
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
ErrorCode error = runScript(credentials, replaceVars(scriptData, generateVarsForContainer(credentials, DockerContainer::None)), cbReadStdOut, cbReadStdErr);
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
@@ -771,7 +830,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
return ErrorCode::ServerCancelInstallation;
}
stdOut.clear();
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), generateVarsForContainer(credentials, DockerContainer::None)),
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr);
if (stdOut.contains("Packet manager not found"))

View File

@@ -4,8 +4,7 @@
#include <QJsonObject>
#include <QObject>
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "core/sshclient.h"
@@ -26,12 +25,12 @@ public:
ErrorCode rebootServer(const ServerCredentials &credentials);
ErrorCode removeAllContainers(const ServerCredentials &credentials);
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig);
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate = false);
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
QJsonObject &newConfig);
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config = ContainerConfig());
const QJsonObject &config = QJsonObject());
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
const QString &path,
@@ -40,8 +39,8 @@ public:
ErrorCode &errorCode);
QString replaceVars(const QString &script, const Vars &vars);
Vars generateVarsForContainer(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config = ContainerConfig());
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None,
const QJsonObject &config = QJsonObject());
ErrorCode runScript(const ServerCredentials &credentials, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
@@ -60,14 +59,14 @@ public:
private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config = ContainerConfig());
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config = ContainerConfig());
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config);
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config);
const QJsonObject &config = QJsonObject());
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config);
bool isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);

View File

@@ -1,238 +0,0 @@
#include "settingsController.h"
#include <QDateTime>
#include "settings.h"
#include "logger.h"
#include "ui/qautostart.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#endif
namespace
{
Logger logger("SettingsController");
}
SettingsController::SettingsController(std::shared_ptr<Settings> settings, QObject *parent)
: QObject(parent), m_settings(settings)
{
}
void SettingsController::resetAllSettings()
{
logger.info() << "Resetting all settings to defaults";
m_settings->clearSettings();
emit settingsReset();
}
void SettingsController::configureDns(const QString &primaryDns, const QString &secondaryDns)
{
m_settings->setPrimaryDns(primaryDns);
m_settings->setSecondaryDns(secondaryDns);
emit dnsConfigChanged();
}
void SettingsController::toggleAmneziaDns(bool enable)
{
m_settings->setUseAmneziaDns(enable);
emit dnsConfigChanged();
}
void SettingsController::configureLogging(bool enabled)
{
m_settings->setSaveLogs(enabled);
}
void SettingsController::checkLoggingExpiration()
{
if (m_settings->isSaveLogs()) {
QDateTime loggingDisableDate = m_settings->getLogEnableDate().addDays(14);
if (loggingDisableDate <= QDateTime::currentDateTime()) {
configureLogging(false);
clearLogs();
emit loggingExpired();
}
}
}
void SettingsController::clearLogs()
{
logger.info() << "Clearing application logs";
#ifdef Q_OS_ANDROID
AndroidController::instance()->clearLogs();
#else
Logger::clearLogs(false);
Logger::clearServiceLogs();
#endif
logger.info() << "Logs cleared successfully";
}
void SettingsController::configureKillSwitch(bool enable, bool strict)
{
m_settings->setKillSwitchEnabled(enable);
if (enable) {
m_settings->setStrictKillSwitchEnabled(strict);
} else {
m_settings->setStrictKillSwitchEnabled(false);
}
emit killSwitchConfigChanged();
}
void SettingsController::configureAutoStart(bool enable)
{
Autostart::setAutostart(enable);
emit autoStartConfigChanged();
}
void SettingsController::configureAutoConnect(bool enable)
{
m_settings->setAutoConnect(enable);
}
void SettingsController::configureStartMinimized(bool enable)
{
m_settings->setStartMinimized(enable);
}
void SettingsController::configureScreenshots(bool enable)
{
m_settings->setScreenshotsEnabled(enable);
}
QString SettingsController::getPrimaryDns() const
{
return m_settings->primaryDns();
}
QString SettingsController::getSecondaryDns() const
{
return m_settings->secondaryDns();
}
bool SettingsController::isAmneziaDnsEnabled() const
{
return m_settings->useAmneziaDns();
}
bool SettingsController::isLoggingEnabled() const
{
return m_settings->isSaveLogs();
}
bool SettingsController::isKillSwitchEnabled() const
{
return m_settings->isKillSwitchEnabled();
}
bool SettingsController::isStrictKillSwitchEnabled() const
{
return m_settings->isStrictKillSwitchEnabled();
}
bool SettingsController::isAutoStartEnabled() const
{
return Autostart::isAutostart();
}
bool SettingsController::isAutoConnectEnabled() const
{
return m_settings->isAutoConnect();
}
bool SettingsController::isStartMinimizedEnabled() const
{
return m_settings->isStartMinimized();
}
bool SettingsController::isScreenshotsEnabled() const
{
return m_settings->isScreenshotsEnabled();
}
QByteArray SettingsController::backupAppConfig() const
{
return m_settings->backupAppConfig();
}
bool SettingsController::restoreAppConfig(const QByteArray &data)
{
return m_settings->restoreAppConfig(data);
}
QString SettingsController::getInstallationUuid() const
{
return m_settings->getInstallationUuid(false);
}
QString SettingsController::nextAvailableServerName() const
{
return m_settings->nextAvailableServerName();
}
void SettingsController::resetGatewayEndpoint()
{
m_settings->resetGatewayEndpoint();
}
void SettingsController::setGatewayEndpoint(const QString &endpoint)
{
m_settings->setGatewayEndpoint(endpoint);
}
QString SettingsController::getGatewayEndpoint() const
{
if (m_settings->isDevGatewayEnv()) {
return "Dev endpoint";
}
return m_settings->getGatewayEndpoint();
}
bool SettingsController::isDevGatewayEnv() const
{
return m_settings->isDevGatewayEnv();
}
void SettingsController::toggleDevGatewayEnv(bool enabled)
{
m_settings->toggleDevGatewayEnv(enabled);
if (enabled) {
m_settings->setDevGatewayEndpoint();
} else {
m_settings->resetGatewayEndpoint();
}
}
void SettingsController::setDevGatewayEndpoint()
{
m_settings->setDevGatewayEndpoint();
}
bool SettingsController::isHomeAdLabelVisible() const
{
return m_settings->isHomeAdLabelVisible();
}
void SettingsController::disableHomeAdLabel()
{
m_settings->disableHomeAdLabel();
}
QDateTime SettingsController::getLogEnableDate() const
{
return m_settings->getLogEnableDate();
}
QLocale SettingsController::getAppLanguage() const
{
return m_settings->getAppLanguage();
}
void SettingsController::setAppLanguage(const QLocale &locale)
{
m_settings->setAppLanguage(locale);
}

View File

@@ -1,88 +0,0 @@
#ifndef SETTINGSCONTROLLER_H
#define SETTINGSCONTROLLER_H
#include <QObject>
#include <QFuture>
#include <QDateTime>
#include "core/defs.h"
class Settings;
using namespace amnezia;
class SettingsController : public QObject
{
Q_OBJECT
public:
explicit SettingsController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
void resetAllSettings();
void configureDns(const QString &primaryDns, const QString &secondaryDns);
void toggleAmneziaDns(bool enable);
void configureLogging(bool enable);
void checkLoggingExpiration();
void clearLogs();
void configureKillSwitch(bool enable, bool strict = false);
void configureAutoStart(bool enable);
void configureAutoConnect(bool enable);
void configureStartMinimized(bool enable);
void configureScreenshots(bool enable);
QString getPrimaryDns() const;
QString getSecondaryDns() const;
bool isAmneziaDnsEnabled() const;
bool isLoggingEnabled() const;
bool isKillSwitchEnabled() const;
bool isStrictKillSwitchEnabled() const;
bool isAutoStartEnabled() const;
bool isAutoConnectEnabled() const;
bool isStartMinimizedEnabled() const;
bool isScreenshotsEnabled() const;
// Backup/restore functionality
QByteArray backupAppConfig() const;
bool restoreAppConfig(const QByteArray &data);
// Installation UUID
QString getInstallationUuid() const;
// Server naming
QString nextAvailableServerName() const;
// Gateway endpoint functionality
void resetGatewayEndpoint();
void setGatewayEndpoint(const QString &endpoint);
QString getGatewayEndpoint() const;
bool isDevGatewayEnv() const;
void toggleDevGatewayEnv(bool enabled);
void setDevGatewayEndpoint();
// Home ad label
bool isHomeAdLabelVisible() const;
void disableHomeAdLabel();
// Log date
QDateTime getLogEnableDate() const;
QLocale getAppLanguage() const;
void setAppLanguage(const QLocale &locale);
signals:
void settingsReset();
void dnsConfigChanged();
void loggingConfigChanged();
void killSwitchConfigChanged();
void autoStartConfigChanged();
void loggingExpired();
private:
std::shared_ptr<Settings> m_settings;
};
#endif // SETTINGSCONTROLLER_H

View File

@@ -1,228 +0,0 @@
#include "splitTunnelingController.h"
#include "settings.h"
#include "core/networkUtilities.h"
#include <QFileInfo>
SplitTunnelingController::SplitTunnelingController(std::shared_ptr<Settings> settings,
QSharedPointer<VpnConnection> vpnConnection,
QObject *parent)
: QObject(parent), m_settings(settings), m_vpnConnection(vpnConnection)
{
}
// Apps split tunneling implementation
bool SplitTunnelingController::addApp(const InstalledAppInfo &appInfo)
{
InstalledAppInfo processedAppInfo = appInfo;
// Migrated from AppSplitTunnelingController::addApp - app name extraction
if (processedAppInfo.appName.isEmpty() && !processedAppInfo.appPath.isEmpty()) {
QFileInfo fileInfo(processedAppInfo.appPath);
processedAppInfo.appName = fileInfo.fileName();
}
auto currentApps = m_settings->getVpnApps(getAppsRouteMode());
if (currentApps.contains(processedAppInfo)) {
return false;
}
currentApps.append(processedAppInfo);
m_settings->setVpnApps(getAppsRouteMode(), currentApps);
emit appAdded(processedAppInfo);
return true;
}
bool SplitTunnelingController::removeApp(const InstalledAppInfo &appInfo)
{
auto currentApps = m_settings->getVpnApps(getAppsRouteMode());
if (!currentApps.contains(appInfo)) {
return false;
}
currentApps.removeAll(appInfo);
m_settings->setVpnApps(getAppsRouteMode(), currentApps);
emit appRemoved(appInfo);
return true;
}
QVector<InstalledAppInfo> SplitTunnelingController::getApps(Settings::AppsRouteMode routeMode) const
{
return m_settings->getVpnApps(routeMode);
}
Settings::AppsRouteMode SplitTunnelingController::getAppsRouteMode() const
{
return m_settings->getAppsRouteMode();
}
void SplitTunnelingController::setAppsRouteMode(Settings::AppsRouteMode routeMode)
{
m_settings->setAppsRouteMode(routeMode);
emit appsRouteModelChanged();
}
bool SplitTunnelingController::isAppsSplitTunnelingEnabled() const
{
return m_settings->isAppsSplitTunnelingEnabled();
}
void SplitTunnelingController::setAppsSplitTunnelingEnabled(bool enabled)
{
m_settings->setAppsSplitTunnelingEnabled(enabled);
emit appsSplitTunnelingToggled();
}
// Sites split tunneling implementation
bool SplitTunnelingController::addSite(const QString &hostname, const QString &ip)
{
QString processedHostname = hostname;
// Migrated from SitesController::addSite - hostname processing
if (!NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
processedHostname.replace("https://", "");
processedHostname.replace("http://", "");
processedHostname.replace("ftp://", "");
processedHostname = processedHostname.split("/", Qt::SkipEmptyParts).first();
}
if (!m_settings->addVpnSite(getSitesRouteMode(), processedHostname, ip)) {
return false;
}
// Migrated from SitesController::addSite - VPN route management
if (m_vpnConnection) {
if (!ip.isEmpty()) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << ip));
} else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(processedHostname)) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << processedHostname));
} else {
// Migrated from SitesController::addSite - DNS resolution
int lookupId = QHostInfo::lookupHost(processedHostname, this,
SLOT(handleHostnameResolved(QHostInfo)));
m_pendingResolutions[lookupId] = processedHostname;
}
}
emit siteAdded(processedHostname, ip);
return true;
}
bool SplitTunnelingController::addSites(const QMap<QString, QString> &sites, bool replaceExisting)
{
if (replaceExisting) {
m_settings->removeAllVpnSites(getSitesRouteMode());
}
m_settings->addVpnSites(getSitesRouteMode(), sites);
// Migrated from SitesController - VPN route management for batch adds
if (m_vpnConnection) {
QStringList ips;
auto i = sites.constBegin();
while (i != sites.constEnd()) {
const QString &hostname = i.key();
const QString &ip = i.value();
if (ip.isEmpty()) {
ips.append(hostname);
} else {
ips.append(ip);
}
++i;
}
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, ips));
}
auto i = sites.constBegin();
while (i != sites.constEnd()) {
emit siteAdded(i.key(), i.value());
++i;
}
return true;
}
bool SplitTunnelingController::removeSite(const QString &hostname)
{
if (!m_settings->removeVpnSite(getSitesRouteMode(), hostname)) {
return false;
}
// Migrated from SitesController::removeSite - VPN route management
if (m_vpnConnection) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname));
}
emit siteRemoved(hostname);
return true;
}
// Migrated from SitesController - DNS resolution handler
void SplitTunnelingController::handleHostnameResolved(const QHostInfo &hostInfo)
{
if (hostInfo.error() != QHostInfo::NoError) {
return;
}
QString hostname = m_pendingResolutions.take(hostInfo.lookupId());
if (hostname.isEmpty()) {
return;
}
for (const QHostAddress &addr : hostInfo.addresses()) {
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
if (m_vpnConnection) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << addr.toString()));
}
m_settings->addVpnSite(getSitesRouteMode(), hostname, addr.toString());
break;
}
}
}
QVector<QPair<QString, QString>> SplitTunnelingController::getSites(Settings::RouteMode routeMode) const
{
QVector<QPair<QString, QString>> sites;
const QVariantMap &sitesMap = m_settings->vpnSites(routeMode);
auto i = sitesMap.constBegin();
while (i != sitesMap.constEnd()) {
sites.append(qMakePair(i.key(), i.value().toString()));
++i;
}
return sites;
}
Settings::RouteMode SplitTunnelingController::getSitesRouteMode() const
{
return m_settings->routeMode();
}
void SplitTunnelingController::setSitesRouteMode(Settings::RouteMode routeMode)
{
m_settings->setRouteMode(routeMode);
emit sitesRouteModelChanged();
}
bool SplitTunnelingController::isSitesSplitTunnelingEnabled() const
{
return m_settings->isSitesSplitTunnelingEnabled();
}
void SplitTunnelingController::setSitesSplitTunnelingEnabled(bool enabled)
{
m_settings->setSitesSplitTunnelingEnabled(enabled);
emit sitesSplitTunnelingToggled();
}

View File

@@ -1,71 +0,0 @@
#ifndef SPLITTUNNELINGCONTROLLER_H
#define SPLITTUNNELINGCONTROLLER_H
#include <QObject>
#include <QVector>
#include <QMap>
#include <QStringList>
#include <QSharedPointer>
#include <QHostInfo>
#include "settings.h"
#include "core/defs.h"
#include "vpnconnection.h"
using namespace amnezia;
class SplitTunnelingController : public QObject
{
Q_OBJECT
public:
explicit SplitTunnelingController(std::shared_ptr<Settings> settings,
QSharedPointer<VpnConnection> vpnConnection = nullptr,
QObject *parent = nullptr);
// Apps split tunneling
bool addApp(const InstalledAppInfo &appInfo);
bool removeApp(const InstalledAppInfo &appInfo);
QVector<InstalledAppInfo> getApps(Settings::AppsRouteMode routeMode) const;
Settings::AppsRouteMode getAppsRouteMode() const;
void setAppsRouteMode(Settings::AppsRouteMode routeMode);
bool isAppsSplitTunnelingEnabled() const;
void setAppsSplitTunnelingEnabled(bool enabled);
// Sites split tunneling
bool addSite(const QString &hostname, const QString &ip = QString());
bool addSites(const QMap<QString, QString> &sites, bool replaceExisting = false);
bool removeSite(const QString &hostname);
QVector<QPair<QString, QString>> getSites(Settings::RouteMode routeMode) const;
Settings::RouteMode getSitesRouteMode() const;
void setSitesRouteMode(Settings::RouteMode routeMode);
bool isSitesSplitTunnelingEnabled() const;
void setSitesSplitTunnelingEnabled(bool enabled);
signals:
// Apps signals
void appsRouteModelChanged();
void appsSplitTunnelingToggled();
void appAdded(const InstalledAppInfo &appInfo);
void appRemoved(const InstalledAppInfo &appInfo);
// Sites signals
void sitesRouteModelChanged();
void sitesSplitTunnelingToggled();
void siteAdded(const QString &hostname, const QString &ip);
void siteRemoved(const QString &hostname);
private slots:
void handleHostnameResolved(const QHostInfo &hostInfo);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<VpnConnection> m_vpnConnection;
QMap<int, QString> m_pendingResolutions;
};
#endif // SPLITTUNNELINGCONTROLLER_H

View File

@@ -7,18 +7,6 @@
#include "configurators/shadowsocks_configurator.h"
#include "configurators/wireguard_configurator.h"
#include "configurators/xray_configurator.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "core/models/protocols/ikev2ProtocolConfig.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/models/protocols/torWebsiteProtocolConfig.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "core/models/protocols/sftpProtocolConfig.h"
#include "core/models/protocols/socks5ProtocolConfig.h"
#include "core/models/protocols/protocolConfig.h"
#include <variant>
VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr<Settings> &settings,
QSharedPointer<ServerController> serverController, QObject *parent)
@@ -41,33 +29,8 @@ QScopedPointer<ConfiguratorBase> VpnConfigurationsController::createConfigurator
}
}
QSharedPointer<ProtocolConfig> VpnConfigurationsController::createProtocolConfig(const Proto protocol)
{
switch (protocol) {
case Proto::OpenVpn:
return QSharedPointer<OpenVpnProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
case Proto::ShadowSocks:
return QSharedPointer<ShadowsocksProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
case Proto::Cloak:
return QSharedPointer<CloakProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
case Proto::WireGuard:
return QSharedPointer<WireGuardProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
case Proto::Awg:
return QSharedPointer<AwgProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
case Proto::Xray:
case Proto::SSXray:
return QSharedPointer<XrayProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
case Proto::Ikev2:
return QSharedPointer<Ikev2ProtocolConfig>::create(ProtocolProps::protoToString(protocol));
case Proto::TorWebSite:
return QSharedPointer<TorWebsiteProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
default:
return nullptr;
}
}
ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const ServerCredentials &credentials,
const DockerContainer container, ContainerConfig &containerConfig)
const DockerContainer container, QJsonObject &containerConfig)
{
ErrorCode errorCode = ErrorCode::NoError;
@@ -76,34 +39,25 @@ ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const Se
}
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
if (!protocolConfig) {
protocolConfig = createProtocolConfig(protocol);
if (!protocolConfig) {
errorCode = ErrorCode::InternalError;
return errorCode;
}
containerConfig.protocolConfigs.insert(protocolName, protocolConfig);
}
QJsonObject protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject();
auto configurator = createConfigurator(protocol);
auto result = configurator->createConfig(credentials, container, protocolConfig, errorCode);
if (errorCode != ErrorCode::NoError || !result) {
QString protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
containerConfig.protocolConfigs.insert(protocolName, result);
protocolConfig.insert(config_key::last_config, protocolConfigString);
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
}
return errorCode;
}
ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns,
const ServerCredentials &credentials, const DockerContainer container,
const ContainerConfig &containerConfig, const Proto protocol,
QString &protocolConfigString)
const ServerCredentials &credentials, const DockerContainer container,
const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString)
{
ErrorCode errorCode = ErrorCode::NoError;
@@ -111,30 +65,19 @@ ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isA
return errorCode;
}
QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
if (!protocolConfig) {
errorCode = ErrorCode::InternalError;
return errorCode;
}
auto configurator = createConfigurator(protocol);
auto result = configurator->createConfig(credentials, container, protocolConfig, errorCode);
if (errorCode != ErrorCode::NoError || !result) {
protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
configurator->processConfigWithExportSettings(dns, isApiConfig, result);
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(result);
std::visit([&protocolConfigString](const auto &config) -> void {
protocolConfigString = config->clientProtocolConfig.nativeConfig;
}, variant);
protocolConfigString = configurator->processConfigWithExportSettings(dns, isApiConfig, protocolConfigString);
return errorCode;
}
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QSharedPointer<ServerConfig> &serverConfig,
const ContainerConfig &containerConfig, const DockerContainer container)
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container)
{
QJsonObject vpnConfiguration {};
@@ -142,44 +85,22 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
return vpnConfiguration;
}
bool isApiConfig = static_cast<int>(serverConfig->type);
bool isApiConfig = serverConfig.value(config_key::configVersion).toInt();
for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) {
if (isApiConfig && container == DockerContainer::Cloak && proto == ProtocolEnumNS::Proto::ShadowSocks) {
continue;
}
QString protocolName = ProtocolProps::protoToString(proto);
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
QString protocolConfigString;
if (protocolConfig) {
auto configurator = createConfigurator(proto);
configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfig);
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
std::visit([&protocolConfigString](const auto &config) -> void {
protocolConfigString = config->clientProtocolConfig.nativeConfig;
}, variant);
} else {
protocolConfigString = "";
}
QString protocolConfigString =
containerConfig.value(ProtocolProps::protoToString(proto)).toObject().value(config_key::last_config).toString();
QJsonObject vpnConfigData;
if (proto == Proto::Xray || proto == Proto::SSXray) {
vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
} else {
vpnConfigData[config_key::config] = protocolConfigString;
if (protocolConfig) {
QJsonObject protocolJson = protocolConfig->toJson();
for (auto it = protocolJson.begin(); it != protocolJson.end(); ++it) {
if (it.key() != config_key::config && it.key() != config_key::last_config) {
vpnConfigData[it.key()] = it.value();
}
}
}
}
auto configurator = createConfigurator(proto);
protocolConfigString = configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfigString);
QJsonObject vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
if (container == DockerContainer::Awg || container == DockerContainer::WireGuard) {
// add mtu for old configs
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
vpnConfigData[config_key::mtu] =
container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
@@ -195,32 +116,30 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
vpnConfiguration[config_key::dns1] = dns.first;
vpnConfiguration[config_key::dns2] = dns.second;
vpnConfiguration[config_key::hostName] = serverConfig->hostName;
vpnConfiguration[config_key::description] = serverConfig->toJson().value(config_key::description).toString();
vpnConfiguration[config_key::configVersion] = static_cast<int>(serverConfig->type);
vpnConfiguration[config_key::hostName] = serverConfig.value(config_key::hostName).toString();
vpnConfiguration[config_key::description] = serverConfig.value(config_key::description).toString();
vpnConfiguration[config_key::configVersion] = serverConfig.value(config_key::configVersion).toInt();
// TODO: try to get hostName, port, description for 3rd party configs
// vpnConfiguration[config_key::port] = ...;
return vpnConfiguration;
}
void VpnConfigurationsController::updateContainerConfigAfterInstallation(const DockerContainer container, ContainerConfig &containerConfig,
const QString &stdOut)
void VpnConfigurationsController::updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig,
const QString &stdOut)
{
Proto mainProto = ContainerProps::defaultProtocol(container);
if (container == DockerContainer::TorWebSite) {
QString protocolName = ProtocolProps::protoToString(mainProto);
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
QJsonObject protocol = containerConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
qDebug() << "amnezia-tor onions" << stdOut;
QString onion = stdOut;
onion.replace("\n", "");
if (auto torConfig = qSharedPointerCast<TorWebsiteProtocolConfig>(protocolConfig)) {
torConfig->serverProtocolConfig.site = onion;
}
protocol.insert(config_key::site, onion);
containerConfig.insert(ProtocolProps::protoToString(mainProto), protocol);
}
}

View File

@@ -4,11 +4,8 @@
#include <QObject>
#include "configurators/configurator_base.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/models/servers/serverConfig.h"
#include "settings.h"
class VpnConfigurationsController : public QObject
@@ -20,19 +17,16 @@ public:
public slots:
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
ContainerConfig &containerConfig);
QJsonObject &containerConfig);
ErrorCode createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns, const ServerCredentials &credentials,
const DockerContainer container, const ContainerConfig &containerConfig, const Proto protocol,
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString);
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QSharedPointer<ServerConfig> &serverConfig,
const ContainerConfig &containerConfig, const DockerContainer container);
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container);
static void updateContainerConfigAfterInstallation(const DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
signals:
public:
QSharedPointer<ProtocolConfig> createProtocolConfig(const Proto protocol);
private:
QScopedPointer<ConfiguratorBase> createConfigurator(const Proto protocol);

View File

@@ -19,13 +19,6 @@ namespace amnezia
}
};
enum ServerConfigType
{
SelfHosted,
ApiV1,
ApiV2
};
struct InstalledAppInfo {
QString appName;
QString packageName;
@@ -127,6 +120,7 @@ namespace amnezia
ApiNotFoundError = 1109,
ApiMigrationError = 1110,
ApiUpdateRequestError = 1111,
ApiSubscriptionExpiredError = 1112,
// QFile errors
OpenError = 1200,

View File

@@ -77,6 +77,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
case (ErrorCode::ApiSubscriptionExpiredError): errorMessage = QObject::tr("Your Amnezia Premium subscription has expired.\n Please check your email for renewal instructions.\n If you haven't received an email, please contact our support."); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;

View File

@@ -1,76 +0,0 @@
#include "clientInfo.h"
#include <QJsonObject>
namespace
{
namespace configKey
{
constexpr char clientId[] = "clientId";
constexpr char clientName[] = "clientName";
constexpr char container[] = "container";
constexpr char userData[] = "userData";
constexpr char creationDate[] = "creationDate";
constexpr char latestHandshake[] = "latestHandshake";
constexpr char dataReceived[] = "dataReceived";
constexpr char dataSent[] = "dataSent";
constexpr char allowedIps[] = "allowedIps";
}
}
ClientInfo::ClientInfo()
: container(DockerContainer::None)
{
}
ClientInfo::ClientInfo(const QString &clientId, const QString &clientName)
: clientId(clientId), clientName(clientName), creationDate(QDateTime::currentDateTime()), container(DockerContainer::None)
{
}
ClientInfo::ClientInfo(const QJsonObject &jsonObject)
{
clientId = jsonObject.value(configKey::clientId).toString();
container = ContainerProps::containerFromString(jsonObject.value(configKey::container).toString());
QJsonObject userData = jsonObject.value(configKey::userData).toObject();
clientName = userData.value(configKey::clientName).toString();
creationDate = QDateTime::fromString(userData.value(configKey::creationDate).toString());
latestHandshake = jsonObject.value(configKey::latestHandshake).toString();
dataReceived = jsonObject.value(configKey::dataReceived).toString();
dataSent = jsonObject.value(configKey::dataSent).toString();
allowedIps = jsonObject.value(configKey::allowedIps).toString();
}
QJsonObject ClientInfo::toJson() const
{
QJsonObject jsonObject;
jsonObject[configKey::clientId] = clientId;
jsonObject[configKey::container] = ContainerProps::containerToString(container);
QJsonObject userData;
userData[configKey::clientName] = clientName;
userData[configKey::creationDate] = creationDate.toString();
jsonObject[configKey::userData] = userData;
if (!latestHandshake.isEmpty()) {
jsonObject[configKey::latestHandshake] = latestHandshake;
}
if (!dataReceived.isEmpty()) {
jsonObject[configKey::dataReceived] = dataReceived;
}
if (!dataSent.isEmpty()) {
jsonObject[configKey::dataSent] = dataSent;
}
if (!allowedIps.isEmpty()) {
jsonObject[configKey::allowedIps] = allowedIps;
}
return jsonObject;
}
ClientInfo ClientInfo::fromJson(const QJsonObject &jsonObject)
{
return ClientInfo(jsonObject);
}

View File

@@ -1,34 +0,0 @@
#ifndef CLIENTINFO_H
#define CLIENTINFO_H
#include <QString>
#include <QDateTime>
#include <QJsonObject>
#include "core/models/containers/containers_defs.h"
using namespace amnezia;
class ClientInfo
{
public:
ClientInfo();
ClientInfo(const QString &clientId, const QString &clientName);
ClientInfo(const QJsonObject &jsonObject);
QJsonObject toJson() const;
static ClientInfo fromJson(const QJsonObject &jsonObject);
QString clientId;
QString clientName;
QDateTime creationDate;
DockerContainer container;
QString latestHandshake;
QString dataReceived;
QString dataSent;
QString allowedIps;
};
#endif // CLIENTINFO_H

View File

@@ -1,5 +0,0 @@
#include "containerConfig.h"
ContainerConfig::ContainerConfig()
{
}

View File

@@ -1,21 +0,0 @@
#ifndef CONTAINERCONFIG_H
#define CONTAINERCONFIG_H
#include <QMap>
#include <QSharedPointer>
#include <QString>
#include "core/models/protocols/protocolConfig.h"
#include "containers_defs.h"
class ContainerConfig
{
public:
ContainerConfig();
QString containerName;
amnezia::DockerContainer containerType;
QMap<QString, QSharedPointer<ProtocolConfig>> protocolConfigs;
};
#endif // CONTAINERCONFIG_H

View File

@@ -1,256 +0,0 @@
#include "awgProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
AwgProtocolConfig::AwgProtocolConfig(const QString &protocolName) : ProtocolConfig(protocolName)
{
}
AwgProtocolConfig::AwgProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::awg::defaultPort);
serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString("udp");
serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
serverProtocolConfig.mtu = protocolConfigObject.value(config_key::mtu).toString(protocols::awg::defaultMtu);
serverProtocolConfig.awgData.junkPacketCount = protocolConfigObject.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
serverProtocolConfig.awgData.junkPacketMinSize = protocolConfigObject.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
serverProtocolConfig.awgData.junkPacketMaxSize = protocolConfigObject.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
serverProtocolConfig.awgData.initPacketJunkSize = protocolConfigObject.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
serverProtocolConfig.awgData.responsePacketJunkSize = protocolConfigObject.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
serverProtocolConfig.awgData.cookieReplyPacketJunkSize = protocolConfigObject.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
serverProtocolConfig.awgData.transportPacketJunkSize = protocolConfigObject.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
serverProtocolConfig.awgData.initPacketMagicHeader = protocolConfigObject.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
serverProtocolConfig.awgData.responsePacketMagicHeader = protocolConfigObject.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader);
serverProtocolConfig.awgData.underloadPacketMagicHeader = protocolConfigObject.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader);
serverProtocolConfig.awgData.transportPacketMagicHeader = protocolConfigObject.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader);
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
clientProtocolConfig.awgData.junkPacketCount = clientProtocolConfigObject.value(config_key::junkPacketCount).toString();
clientProtocolConfig.awgData.junkPacketMinSize = clientProtocolConfigObject.value(config_key::junkPacketMinSize).toString();
clientProtocolConfig.awgData.junkPacketMaxSize = clientProtocolConfigObject.value(config_key::junkPacketMaxSize).toString();
clientProtocolConfig.awgData.initPacketJunkSize = clientProtocolConfigObject.value(config_key::initPacketJunkSize).toString();
clientProtocolConfig.awgData.responsePacketJunkSize = clientProtocolConfigObject.value(config_key::responsePacketJunkSize).toString();
clientProtocolConfig.awgData.cookieReplyPacketJunkSize = clientProtocolConfigObject.value(config_key::cookieReplyPacketJunkSize).toString();
clientProtocolConfig.awgData.transportPacketJunkSize = clientProtocolConfigObject.value(config_key::transportPacketJunkSize).toString();
clientProtocolConfig.awgData.initPacketMagicHeader = clientProtocolConfigObject.value(config_key::initPacketMagicHeader).toString();
clientProtocolConfig.awgData.responsePacketMagicHeader =
clientProtocolConfigObject.value(config_key::responsePacketMagicHeader).toString();
clientProtocolConfig.awgData.underloadPacketMagicHeader =
clientProtocolConfigObject.value(config_key::underloadPacketMagicHeader).toString();
clientProtocolConfig.awgData.transportPacketMagicHeader =
clientProtocolConfigObject.value(config_key::transportPacketMagicHeader).toString();
clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString();
clientProtocolConfig.wireGuardData.clientIp = clientProtocolConfigObject.value(config_key::client_ip).toString();
clientProtocolConfig.wireGuardData.clientPrivateKey = clientProtocolConfigObject.value(config_key::client_priv_key).toString();
clientProtocolConfig.wireGuardData.clientPublicKey = clientProtocolConfigObject.value(config_key::client_pub_key).toString();
clientProtocolConfig.wireGuardData.persistentKeepAlive =
clientProtocolConfigObject.value(config_key::persistent_keep_alive).toString();
clientProtocolConfig.wireGuardData.pskKey = clientProtocolConfigObject.value(config_key::psk_key).toString();
clientProtocolConfig.wireGuardData.serverPubKey = clientProtocolConfigObject.value(config_key::server_pub_key).toString();
clientProtocolConfig.wireGuardData.mtu = clientProtocolConfigObject.value(config_key::mtu).toString();
clientProtocolConfig.hostname = clientProtocolConfigObject.value(config_key::hostName).toString();
clientProtocolConfig.port = clientProtocolConfigObject.value(config_key::port).toInt(0);
clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString();
if (clientProtocolConfigObject.contains(config_key::allowed_ips)
&& clientProtocolConfigObject.value(config_key::allowed_ips).isArray()) {
auto allowedIpsArray = clientProtocolConfigObject.value(config_key::allowed_ips).toArray();
for (const auto &ip : allowedIpsArray) {
clientProtocolConfig.wireGuardData.allowedIps.append(ip.toString());
}
}
}
}
AwgProtocolConfig::AwgProtocolConfig(const AwgProtocolConfig &other) : ProtocolConfig(other.protocolName)
{
serverProtocolConfig = other.serverProtocolConfig;
clientProtocolConfig = other.clientProtocolConfig;
}
QJsonObject AwgProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.transportProto.isEmpty()) {
json[config_key::transport_proto] = serverProtocolConfig.transportProto;
}
if (!serverProtocolConfig.subnetAddress.isEmpty()) {
json[config_key::subnet_address] = serverProtocolConfig.subnetAddress;
}
if (!serverProtocolConfig.mtu.isEmpty()) {
json[config_key::mtu] = serverProtocolConfig.mtu;
}
if (!serverProtocolConfig.awgData.junkPacketCount.isEmpty()) {
json[config_key::junkPacketCount] = serverProtocolConfig.awgData.junkPacketCount;
}
if (!serverProtocolConfig.awgData.junkPacketMinSize.isEmpty()) {
json[config_key::junkPacketMinSize] = serverProtocolConfig.awgData.junkPacketMinSize;
}
if (!serverProtocolConfig.awgData.junkPacketMaxSize.isEmpty()) {
json[config_key::junkPacketMaxSize] = serverProtocolConfig.awgData.junkPacketMaxSize;
}
if (!serverProtocolConfig.awgData.initPacketJunkSize.isEmpty()) {
json[config_key::initPacketJunkSize] = serverProtocolConfig.awgData.initPacketJunkSize;
}
if (!serverProtocolConfig.awgData.responsePacketJunkSize.isEmpty()) {
json[config_key::responsePacketJunkSize] = serverProtocolConfig.awgData.responsePacketJunkSize;
}
if (!serverProtocolConfig.awgData.cookieReplyPacketJunkSize.isEmpty()) {
json[config_key::cookieReplyPacketJunkSize] = serverProtocolConfig.awgData.cookieReplyPacketJunkSize;
}
if (!serverProtocolConfig.awgData.transportPacketJunkSize.isEmpty()) {
json[config_key::transportPacketJunkSize] = serverProtocolConfig.awgData.transportPacketJunkSize;
}
if (!serverProtocolConfig.awgData.initPacketMagicHeader.isEmpty()) {
json[config_key::initPacketMagicHeader] = serverProtocolConfig.awgData.initPacketMagicHeader;
}
if (!serverProtocolConfig.awgData.responsePacketMagicHeader.isEmpty()) {
json[config_key::responsePacketMagicHeader] = serverProtocolConfig.awgData.responsePacketMagicHeader;
}
if (!serverProtocolConfig.awgData.underloadPacketMagicHeader.isEmpty()) {
json[config_key::underloadPacketMagicHeader] = serverProtocolConfig.awgData.underloadPacketMagicHeader;
}
if (!serverProtocolConfig.awgData.transportPacketMagicHeader.isEmpty()) {
json[config_key::transportPacketMagicHeader] = serverProtocolConfig.awgData.transportPacketMagicHeader;
}
if (!clientProtocolConfig.isEmpty) {
QJsonObject clientConfigJson;
if (!clientProtocolConfig.clientId.isEmpty()) {
clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId;
}
if (!clientProtocolConfig.awgData.junkPacketCount.isEmpty()) {
clientConfigJson[config_key::junkPacketCount] = clientProtocolConfig.awgData.junkPacketCount;
}
if (!clientProtocolConfig.awgData.junkPacketMinSize.isEmpty()) {
clientConfigJson[config_key::junkPacketMinSize] = clientProtocolConfig.awgData.junkPacketMinSize;
}
if (!clientProtocolConfig.awgData.junkPacketMaxSize.isEmpty()) {
clientConfigJson[config_key::junkPacketMaxSize] = clientProtocolConfig.awgData.junkPacketMaxSize;
}
if (!clientProtocolConfig.awgData.initPacketJunkSize.isEmpty()) {
clientConfigJson[config_key::initPacketJunkSize] = clientProtocolConfig.awgData.initPacketJunkSize;
}
if (!clientProtocolConfig.awgData.responsePacketJunkSize.isEmpty()) {
clientConfigJson[config_key::responsePacketJunkSize] = clientProtocolConfig.awgData.responsePacketJunkSize;
}
if (!clientProtocolConfig.awgData.initPacketMagicHeader.isEmpty()) {
clientConfigJson[config_key::initPacketMagicHeader] = clientProtocolConfig.awgData.initPacketMagicHeader;
}
if (!clientProtocolConfig.awgData.responsePacketMagicHeader.isEmpty()) {
clientConfigJson[config_key::responsePacketMagicHeader] = clientProtocolConfig.awgData.responsePacketMagicHeader;
}
if (!clientProtocolConfig.awgData.underloadPacketMagicHeader.isEmpty()) {
clientConfigJson[config_key::underloadPacketMagicHeader] = clientProtocolConfig.awgData.underloadPacketMagicHeader;
}
if (!clientProtocolConfig.awgData.transportPacketMagicHeader.isEmpty()) {
clientConfigJson[config_key::transportPacketMagicHeader] = clientProtocolConfig.awgData.transportPacketMagicHeader;
}
if (!clientProtocolConfig.wireGuardData.clientIp.isEmpty()) {
clientConfigJson[config_key::client_ip] = clientProtocolConfig.wireGuardData.clientIp;
}
if (!clientProtocolConfig.wireGuardData.clientPrivateKey.isEmpty()) {
clientConfigJson[config_key::client_priv_key] = clientProtocolConfig.wireGuardData.clientPrivateKey;
}
if (!clientProtocolConfig.wireGuardData.clientPublicKey.isEmpty()) {
clientConfigJson[config_key::client_pub_key] = clientProtocolConfig.wireGuardData.clientPublicKey;
}
if (!clientProtocolConfig.wireGuardData.persistentKeepAlive.isEmpty()) {
clientConfigJson[config_key::persistent_keep_alive] = clientProtocolConfig.wireGuardData.persistentKeepAlive;
}
if (!clientProtocolConfig.wireGuardData.pskKey.isEmpty()) {
clientConfigJson[config_key::psk_key] = clientProtocolConfig.wireGuardData.pskKey;
}
if (!clientProtocolConfig.wireGuardData.serverPubKey.isEmpty()) {
clientConfigJson[config_key::server_pub_key] = clientProtocolConfig.wireGuardData.serverPubKey;
}
if (!clientProtocolConfig.wireGuardData.mtu.isEmpty()) {
clientConfigJson[config_key::mtu] = clientProtocolConfig.wireGuardData.mtu;
}
if (!clientProtocolConfig.wireGuardData.allowedIps.isEmpty()) {
QJsonArray allowedIpsArray;
for (const auto &ip : clientProtocolConfig.wireGuardData.allowedIps) {
if (!ip.isEmpty()) {
allowedIpsArray.append(ip);
}
}
if (!allowedIpsArray.isEmpty()) {
clientConfigJson[config_key::allowed_ips] = allowedIpsArray;
}
}
if (!clientProtocolConfig.hostname.isEmpty()) {
clientConfigJson[config_key::hostName] = clientProtocolConfig.hostname;
}
if (clientProtocolConfig.port) {
clientConfigJson[config_key::port] = clientProtocolConfig.port;
}
if (!clientProtocolConfig.nativeConfig.isEmpty()) {
clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig;
}
if (!clientConfigJson.isEmpty()) {
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson(QJsonDocument::Compact));
}
}
return json;
}
bool AwgProtocolConfig::hasEqualServerSettings(const AwgProtocolConfig &other) const
{
if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress
|| serverProtocolConfig.port != other.serverProtocolConfig.port
|| serverProtocolConfig.awgData.junkPacketCount != other.serverProtocolConfig.awgData.junkPacketCount
|| serverProtocolConfig.awgData.junkPacketMinSize != other.serverProtocolConfig.awgData.junkPacketMinSize
|| serverProtocolConfig.awgData.junkPacketMaxSize != other.serverProtocolConfig.awgData.junkPacketMaxSize
|| serverProtocolConfig.awgData.initPacketJunkSize != other.serverProtocolConfig.awgData.initPacketJunkSize
|| serverProtocolConfig.awgData.responsePacketJunkSize != other.serverProtocolConfig.awgData.responsePacketJunkSize
|| serverProtocolConfig.awgData.initPacketMagicHeader != other.serverProtocolConfig.awgData.initPacketMagicHeader
|| serverProtocolConfig.awgData.responsePacketMagicHeader != other.serverProtocolConfig.awgData.responsePacketMagicHeader
|| serverProtocolConfig.awgData.underloadPacketMagicHeader != other.serverProtocolConfig.awgData.underloadPacketMagicHeader
|| serverProtocolConfig.awgData.transportPacketMagicHeader != other.serverProtocolConfig.awgData.transportPacketMagicHeader) {
return false;
}
return true;
}
bool AwgProtocolConfig::hasEqualClientSettings(const AwgProtocolConfig &other) const
{
if (clientProtocolConfig.wireGuardData.mtu != other.clientProtocolConfig.wireGuardData.mtu
|| clientProtocolConfig.awgData.junkPacketCount != other.clientProtocolConfig.awgData.junkPacketCount
|| clientProtocolConfig.awgData.junkPacketMinSize != other.clientProtocolConfig.awgData.junkPacketMinSize
|| clientProtocolConfig.awgData.junkPacketMaxSize != other.clientProtocolConfig.awgData.junkPacketMaxSize) {
return false;
}
return true;
}
void AwgProtocolConfig::clearClientSettings()
{
clientProtocolConfig = awg::ClientProtocolConfig();
}

View File

@@ -1,76 +0,0 @@
#ifndef AWGPROTOCOLCONFIG_H
#define AWGPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QStringList>
#include "protocolConfig.h"
#include "wireguardProtocolConfig.h"
namespace awg
{
struct AwgData
{
QString junkPacketCount;
QString junkPacketMinSize;
QString junkPacketMaxSize;
QString initPacketJunkSize;
QString responsePacketJunkSize;
QString cookieReplyPacketJunkSize;
QString transportPacketJunkSize;
QString initPacketMagicHeader;
QString responsePacketMagicHeader;
QString underloadPacketMagicHeader;
QString transportPacketMagicHeader;
};
struct ServerProtocolConfig
{
QString port;
QString transportProto;
QString subnetAddress;
AwgData awgData;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString clientId;
wireguard::WireGuardData wireGuardData;
AwgData awgData;
QString hostname;
int port;
QString nativeConfig;
};
const int messageInitiationSize = 148;
const int messageResponseSize = 92;
}
class AwgProtocolConfig : public ProtocolConfig
{
public:
AwgProtocolConfig(const QString &protocolName);
AwgProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
AwgProtocolConfig(const AwgProtocolConfig &other);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const AwgProtocolConfig &other) const;
bool hasEqualClientSettings(const AwgProtocolConfig &other) const;
void clearClientSettings();
awg::ServerProtocolConfig serverProtocolConfig;
awg::ClientProtocolConfig clientProtocolConfig;
};
#endif // AWGPROTOCOLCONFIG_H

View File

@@ -1,58 +0,0 @@
#include "cloakProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
CloakProtocolConfig::CloakProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::cloak::defaultPort);
serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(protocols::cloak::defaultCipher);
serverProtocolConfig.site = protocolConfigObject.value(config_key::site).toString(protocols::cloak::defaultRedirSite);
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
}
}
QJsonObject CloakProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.cipher.isEmpty()) {
json[config_key::cipher] = serverProtocolConfig.cipher;
}
if (!serverProtocolConfig.site.isEmpty()) {
json[config_key::site] = serverProtocolConfig.site;
}
if (!clientProtocolConfig.isEmpty) {
QJsonObject clientConfigJson;
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
}
return json;
}
bool CloakProtocolConfig::hasEqualServerSettings(const CloakProtocolConfig &other) const
{
if (serverProtocolConfig.port != other.serverProtocolConfig.port ||
serverProtocolConfig.cipher != other.serverProtocolConfig.cipher ||
serverProtocolConfig.site != other.serverProtocolConfig.site) {
return false;
}
return true;
}
void CloakProtocolConfig::clearClientSettings()
{
clientProtocolConfig = cloak::ClientProtocolConfig();
}

View File

@@ -1,52 +0,0 @@
#ifndef CLOAKPROTOCOLCONFIG_H
#define CLOAKPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace cloak
{
struct ServerProtocolConfig
{
QString port;
QString cipher;
QString site;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString transport = "direct";
QString proxyMethod = "openvpn";
QString encryptionMethod = "aes-gcm";
QString uid;
QString publicKey;
QString serverName;
int numConn = 1;
QString browserSig = "chrome";
int streamTimeout = 300;
QString remoteHost;
QString remotePort;
QString nativeConfig;
};
}
class CloakProtocolConfig : public ProtocolConfig
{
public:
CloakProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const CloakProtocolConfig &other) const;
void clearClientSettings();
cloak::ServerProtocolConfig serverProtocolConfig;
cloak::ClientProtocolConfig clientProtocolConfig;
};
#endif // CLOAKPROTOCOLCONFIG_H

View File

@@ -1,32 +0,0 @@
#include "ikev2ProtocolConfig.h"
#include "config_key.h"
Ikev2ProtocolConfig::Ikev2ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName)
: ProtocolConfig(protocolName)
{
Q_UNUSED(protocolConfigObject)
}
Ikev2ProtocolConfig::Ikev2ProtocolConfig(const QString &protocolName)
: ProtocolConfig(protocolName)
{
}
QJsonObject Ikev2ProtocolConfig::toJson() const
{
QJsonObject protocolConfigObject;
return protocolConfigObject;
}
bool Ikev2ProtocolConfig::hasEqualServerSettings(const Ikev2ProtocolConfig &other) const
{
Q_UNUSED(other)
return true;
}
void Ikev2ProtocolConfig::clearClientSettings()
{
clientProtocolConfig = ikev2::ClientProtocolConfig();
}

View File

@@ -1,45 +0,0 @@
#ifndef IKEV2PROTOCOLCONFIG_H
#define IKEV2PROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include <QByteArray>
#include "protocolConfig.h"
namespace ikev2
{
struct ServerProtocolConfig
{
bool isEmpty = true;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString hostName;
QString userName;
QString cert;
QString password;
QString nativeConfig;
};
}
class Ikev2ProtocolConfig : public ProtocolConfig
{
public:
Ikev2ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
explicit Ikev2ProtocolConfig(const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const Ikev2ProtocolConfig &other) const;
void clearClientSettings();
ikev2::ServerProtocolConfig serverProtocolConfig;
ikev2::ClientProtocolConfig clientProtocolConfig;
};
#endif // IKEV2PROTOCOLCONFIG_H

View File

@@ -1,99 +0,0 @@
#include "openvpnProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include <QJsonDocument>
using namespace amnezia;
OpenVpnProtocolConfig::OpenVpnProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName)
: ProtocolConfig(protocolName)
{
serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress);
serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::openvpn::defaultPort);
serverProtocolConfig.ncpDisable = protocolConfigObject.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
serverProtocolConfig.hash = protocolConfigObject.value(config_key::hash).toString(protocols::openvpn::defaultHash);
serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
serverProtocolConfig.tlsAuth = protocolConfigObject.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
serverProtocolConfig.blockOutsideDns = protocolConfigObject.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns);
serverProtocolConfig.additionalClientConfig = protocolConfigObject.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
serverProtocolConfig.additionalServerConfig = protocolConfigObject.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig);
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString();
clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString();
}
}
QJsonObject OpenVpnProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.subnetAddress.isEmpty()) {
json[config_key::subnet_address] = serverProtocolConfig.subnetAddress;
}
if (!serverProtocolConfig.transportProto.isEmpty()) {
json[config_key::transport_proto] = serverProtocolConfig.transportProto;
}
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
json[config_key::ncp_disable] = serverProtocolConfig.ncpDisable;
if (!serverProtocolConfig.hash.isEmpty()) {
json[config_key::hash] = serverProtocolConfig.hash;
}
if (!serverProtocolConfig.cipher.isEmpty()) {
json[config_key::cipher] = serverProtocolConfig.cipher;
}
json[config_key::tls_auth] = serverProtocolConfig.tlsAuth;
json[config_key::block_outside_dns] = serverProtocolConfig.blockOutsideDns;
if (!serverProtocolConfig.additionalClientConfig.isEmpty()) {
json[config_key::additional_client_config] = serverProtocolConfig.additionalClientConfig;
}
if (!serverProtocolConfig.additionalServerConfig.isEmpty()) {
json[config_key::additional_server_config] = serverProtocolConfig.additionalServerConfig;
}
if (!clientProtocolConfig.isEmpty) {
QJsonObject clientConfigJson;
if (!clientProtocolConfig.clientId.isEmpty()) {
clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId;
}
if (!clientProtocolConfig.nativeConfig.isEmpty()) {
clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig;
}
if (!clientConfigJson.isEmpty()) {
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
}
}
return json;
}
bool OpenVpnProtocolConfig::hasEqualServerSettings(const OpenVpnProtocolConfig &other) const
{
if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress
|| serverProtocolConfig.transportProto != other.serverProtocolConfig.transportProto
|| serverProtocolConfig.port != other.serverProtocolConfig.port
|| serverProtocolConfig.ncpDisable != other.serverProtocolConfig.ncpDisable
|| serverProtocolConfig.hash != other.serverProtocolConfig.hash || serverProtocolConfig.cipher != other.serverProtocolConfig.cipher
|| serverProtocolConfig.tlsAuth != other.serverProtocolConfig.tlsAuth
|| serverProtocolConfig.blockOutsideDns != other.serverProtocolConfig.blockOutsideDns
|| serverProtocolConfig.additionalClientConfig != other.serverProtocolConfig.additionalClientConfig
|| serverProtocolConfig.additionalServerConfig != other.serverProtocolConfig.additionalServerConfig) {
return false;
}
return true;
}
void OpenVpnProtocolConfig::clearClientSettings()
{
clientProtocolConfig = openvpn::ClientProtocolConfig();
}

View File

@@ -1,49 +0,0 @@
#ifndef OPENVPNPROTOCOLCONFIG_H
#define OPENVPNPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace openvpn
{
struct ServerProtocolConfig
{
QString subnetAddress;
QString transportProto;
QString port;
bool ncpDisable;
QString hash;
QString cipher;
bool tlsAuth;
bool blockOutsideDns;
QString additionalClientConfig;
QString additionalServerConfig;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString clientId;
QString nativeConfig;
};
}
class OpenVpnProtocolConfig : public ProtocolConfig
{
public:
OpenVpnProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const OpenVpnProtocolConfig &other) const;
void clearClientSettings();
openvpn::ServerProtocolConfig serverProtocolConfig;
openvpn::ClientProtocolConfig clientProtocolConfig;
};
#endif // OPENVPNPROTOCOLCONFIG_H

View File

@@ -1,76 +0,0 @@
#include "protocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "protocols/protocols_defs.h"
using namespace amnezia;
ProtocolConfig::ProtocolConfig(const QString &protocolName) : protocolName(protocolName)
{
}
ProtocolConfigVariant ProtocolConfig::getProtocolConfigVariant(const QSharedPointer<ProtocolConfig> &protocolConfig)
{
ProtocolConfigVariant variant;
auto proto = ProtocolProps::protoFromString(protocolConfig->protocolName);
switch (proto) {
case Proto::OpenVpn: variant = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig); break;
case Proto::WireGuard: variant = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig); break;
case Proto::ShadowSocks: variant = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig); break;
case Proto::Cloak: variant = qSharedPointerCast<CloakProtocolConfig>(protocolConfig); break;
case Proto::Xray: variant = qSharedPointerCast<XrayProtocolConfig>(protocolConfig); break;
case Proto::Awg: variant = qSharedPointerCast<AwgProtocolConfig>(protocolConfig); break;
default: break;
}
return variant;
}
bool ProtocolConfig::isServerSettingsEqual(const QSharedPointer<ProtocolConfig> &other)
{
auto proto = ProtocolProps::protoFromString(protocolName);
switch (proto) {
case Proto::OpenVpn: {
auto thisConfig = qSharedPointerCast<OpenVpnProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<OpenVpnProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::WireGuard: {
auto thisConfig = qSharedPointerCast<WireGuardProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<WireGuardProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::ShadowSocks: {
auto thisConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::Cloak: {
auto thisConfig = qSharedPointerCast<CloakProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<CloakProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::Xray: {
auto thisConfig = qSharedPointerCast<XrayProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<XrayProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::Awg: {
auto thisConfig = qSharedPointerCast<AwgProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<AwgProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
default: return false;
}
}
QJsonObject ProtocolConfig::toJson() const
{
return QJsonObject();
}

View File

@@ -1,37 +0,0 @@
#ifndef PROTOCOLCONFIG_H
#define PROTOCOLCONFIG_H
#include <QJsonObject>
#include <QSharedPointer>
#include <variant>
class OpenVpnProtocolConfig;
class WireGuardProtocolConfig;
class ShadowsocksProtocolConfig;
class CloakProtocolConfig;
class XrayProtocolConfig;
class AwgProtocolConfig;
class SftpProtocolConfig;
class Socks5ProtocolConfig;
class Ikev2ProtocolConfig;
class TorWebsiteProtocolConfig;
using ProtocolConfigVariant =
std::variant<QSharedPointer<OpenVpnProtocolConfig>, QSharedPointer<WireGuardProtocolConfig>, QSharedPointer<ShadowsocksProtocolConfig>,
QSharedPointer<CloakProtocolConfig>, QSharedPointer<XrayProtocolConfig>, QSharedPointer<AwgProtocolConfig>,
QSharedPointer<SftpProtocolConfig>, QSharedPointer<Socks5ProtocolConfig>, QSharedPointer<Ikev2ProtocolConfig>,
QSharedPointer<TorWebsiteProtocolConfig> >;
class ProtocolConfig
{
public:
ProtocolConfig(const QString &protocolName);
virtual QJsonObject toJson() const;
static ProtocolConfigVariant getProtocolConfigVariant(const QSharedPointer<ProtocolConfig> &protocolConfig);
bool isServerSettingsEqual(const QSharedPointer<ProtocolConfig> &protocolConfig);
QString protocolName;
};
#endif // PROTOCOLCONFIG_H

View File

@@ -1,48 +0,0 @@
#include "sftpProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
SftpProtocolConfig::SftpProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp)));
serverProtocolConfig.userName = protocolConfigObject.value(config_key::userName).toString(protocols::sftp::defaultUserName);
serverProtocolConfig.password = protocolConfigObject.value(config_key::password).toString();
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
}
}
QJsonObject SftpProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.userName.isEmpty()) {
json[config_key::userName] = serverProtocolConfig.userName;
}
if (!serverProtocolConfig.password.isEmpty()) {
json[config_key::password] = serverProtocolConfig.password;
}
return json;
}
bool SftpProtocolConfig::hasEqualServerSettings(const SftpProtocolConfig &other) const
{
return serverProtocolConfig.port == other.serverProtocolConfig.port &&
serverProtocolConfig.userName == other.serverProtocolConfig.userName &&
serverProtocolConfig.password == other.serverProtocolConfig.password;
}
void SftpProtocolConfig::clearClientSettings()
{
clientProtocolConfig.isEmpty = true;
}

View File

@@ -1,38 +0,0 @@
#ifndef SFTPPROTOCOLCONFIG_H
#define SFTPPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace sftp
{
struct ServerProtocolConfig
{
QString port;
QString userName;
QString password;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
};
}
class SftpProtocolConfig : public ProtocolConfig
{
public:
SftpProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const SftpProtocolConfig &other) const;
void clearClientSettings();
sftp::ServerProtocolConfig serverProtocolConfig;
sftp::ClientProtocolConfig clientProtocolConfig;
};
#endif // SFTPPROTOCOLCONFIG_H

View File

@@ -1,53 +0,0 @@
#include "shadowsocksProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
ShadowsocksProtocolConfig::ShadowsocksProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::shadowsocks::defaultPort);
serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher);
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
}
}
QJsonObject ShadowsocksProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.cipher.isEmpty()) {
json[config_key::cipher] = serverProtocolConfig.cipher;
}
if (!clientProtocolConfig.isEmpty) {
QJsonObject clientConfigJson;
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
}
return json;
}
bool ShadowsocksProtocolConfig::hasEqualServerSettings(const ShadowsocksProtocolConfig &other) const
{
if (serverProtocolConfig.port != other.serverProtocolConfig.port ||
serverProtocolConfig.cipher != other.serverProtocolConfig.cipher) {
return false;
}
return true;
}
void ShadowsocksProtocolConfig::clearClientSettings()
{
clientProtocolConfig = shadowsocks::ClientProtocolConfig();
}

View File

@@ -1,46 +0,0 @@
#ifndef SHADOWSOCKSPROTOCOLCONFIG_H
#define SHADOWSOCKSPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace shadowsocks
{
struct ServerProtocolConfig
{
QString port;
QString cipher;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString server;
QString serverPort;
QString localPort;
QString password;
int timeout = 60;
QString method;
QString nativeConfig;
};
}
class ShadowsocksProtocolConfig : public ProtocolConfig
{
public:
ShadowsocksProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const ShadowsocksProtocolConfig &other) const;
void clearClientSettings();
shadowsocks::ServerProtocolConfig serverProtocolConfig;
shadowsocks::ClientProtocolConfig clientProtocolConfig;
};
#endif // SHADOWSOCKSPROTOCOLCONFIG_H

View File

@@ -1,48 +0,0 @@
#include "socks5ProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
Socks5ProtocolConfig::Socks5ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::socks5Proxy::defaultPort);
serverProtocolConfig.userName = protocolConfigObject.value(config_key::userName).toString(protocols::socks5Proxy::defaultUserName);
serverProtocolConfig.password = protocolConfigObject.value(config_key::password).toString();
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
}
}
QJsonObject Socks5ProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.userName.isEmpty()) {
json[config_key::userName] = serverProtocolConfig.userName;
}
if (!serverProtocolConfig.password.isEmpty()) {
json[config_key::password] = serverProtocolConfig.password;
}
return json;
}
bool Socks5ProtocolConfig::hasEqualServerSettings(const Socks5ProtocolConfig &other) const
{
return serverProtocolConfig.port == other.serverProtocolConfig.port &&
serverProtocolConfig.userName == other.serverProtocolConfig.userName &&
serverProtocolConfig.password == other.serverProtocolConfig.password;
}
void Socks5ProtocolConfig::clearClientSettings()
{
clientProtocolConfig.isEmpty = true;
}

View File

@@ -1,38 +0,0 @@
#ifndef SOCKS5PROTOCOLCONFIG_H
#define SOCKS5PROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace socks5
{
struct ServerProtocolConfig
{
QString port;
QString userName;
QString password;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
};
}
class Socks5ProtocolConfig : public ProtocolConfig
{
public:
Socks5ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const Socks5ProtocolConfig &other) const;
void clearClientSettings();
socks5::ServerProtocolConfig serverProtocolConfig;
socks5::ClientProtocolConfig clientProtocolConfig;
};
#endif // SOCKS5PROTOCOLCONFIG_H

View File

@@ -1,32 +0,0 @@
#include "torWebsiteProtocolConfig.h"
#include "protocols/protocols_defs.h"
using namespace amnezia;
TorWebsiteProtocolConfig::TorWebsiteProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName)
: ProtocolConfig(protocolName)
{
serverProtocolConfig.site = protocolConfigObject.value(config_key::site).toString();
clientProtocolConfig.isEmpty = false;
}
QJsonObject TorWebsiteProtocolConfig::toJson() const
{
QJsonObject json;
json[config_key::site] = serverProtocolConfig.site;
return json;
}
bool TorWebsiteProtocolConfig::hasEqualServerSettings(const TorWebsiteProtocolConfig &other) const
{
return serverProtocolConfig.site == other.serverProtocolConfig.site;
}
void TorWebsiteProtocolConfig::clearClientSettings()
{
clientProtocolConfig.isEmpty = true;
}

View File

@@ -1,36 +0,0 @@
#ifndef TORWEBSITEPROTOCOLCONFIG_H
#define TORWEBSITEPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace torWebsite
{
struct ServerProtocolConfig
{
QString site;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
};
}
class TorWebsiteProtocolConfig : public ProtocolConfig
{
public:
TorWebsiteProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const TorWebsiteProtocolConfig &other) const;
void clearClientSettings();
torWebsite::ServerProtocolConfig serverProtocolConfig;
torWebsite::ClientProtocolConfig clientProtocolConfig;
};
#endif // TORWEBSITEPROTOCOLCONFIG_H

View File

@@ -1,138 +0,0 @@
#include "wireguardProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
WireGuardProtocolConfig::WireGuardProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName)
: ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::wireguard::defaultPort);
serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString("udp");
serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
serverProtocolConfig.mtu = protocolConfigObject.value(config_key::mtu).toString(protocols::wireguard::defaultMtu);
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString();
clientProtocolConfig.wireGuardData.clientIp = clientProtocolConfigObject.value(config_key::client_ip).toString();
clientProtocolConfig.wireGuardData.clientPrivateKey = clientProtocolConfigObject.value(config_key::client_priv_key).toString();
clientProtocolConfig.wireGuardData.clientPublicKey = clientProtocolConfigObject.value(config_key::client_pub_key).toString();
clientProtocolConfig.wireGuardData.persistentKeepAlive =
clientProtocolConfigObject.value(config_key::persistent_keep_alive).toString();
clientProtocolConfig.wireGuardData.pskKey = clientProtocolConfigObject.value(config_key::psk_key).toString();
clientProtocolConfig.wireGuardData.serverPubKey = clientProtocolConfigObject.value(config_key::server_pub_key).toString();
clientProtocolConfig.wireGuardData.mtu = clientProtocolConfigObject.value(config_key::mtu).toString();
clientProtocolConfig.hostname = clientProtocolConfigObject.value(config_key::hostName).toString();
clientProtocolConfig.port = clientProtocolConfigObject.value(config_key::port).toInt(0);
clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString();
if (clientProtocolConfigObject.contains(config_key::allowed_ips)
&& clientProtocolConfigObject.value(config_key::allowed_ips).isArray()) {
auto allowedIpsArray = clientProtocolConfigObject.value(config_key::allowed_ips).toArray();
for (const auto &ip : allowedIpsArray) {
clientProtocolConfig.wireGuardData.allowedIps.append(ip.toString());
}
}
}
}
QJsonObject WireGuardProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.transportProto.isEmpty()) {
json[config_key::transport_proto] = serverProtocolConfig.transportProto;
}
if (!serverProtocolConfig.subnetAddress.isEmpty()) {
json[config_key::subnet_address] = serverProtocolConfig.subnetAddress;
}
if (!serverProtocolConfig.mtu.isEmpty()) {
json[config_key::mtu] = serverProtocolConfig.mtu;
}
if (!clientProtocolConfig.isEmpty) {
QJsonObject clientConfigJson;
if (!clientProtocolConfig.clientId.isEmpty()) {
clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId;
}
if (!clientProtocolConfig.wireGuardData.clientIp.isEmpty()) {
clientConfigJson[config_key::client_ip] = clientProtocolConfig.wireGuardData.clientIp;
}
if (!clientProtocolConfig.wireGuardData.clientPrivateKey.isEmpty()) {
clientConfigJson[config_key::client_priv_key] = clientProtocolConfig.wireGuardData.clientPrivateKey;
}
if (!clientProtocolConfig.wireGuardData.clientPublicKey.isEmpty()) {
clientConfigJson[config_key::client_pub_key] = clientProtocolConfig.wireGuardData.clientPublicKey;
}
if (!clientProtocolConfig.wireGuardData.persistentKeepAlive.isEmpty()) {
clientConfigJson[config_key::persistent_keep_alive] = clientProtocolConfig.wireGuardData.persistentKeepAlive;
}
if (!clientProtocolConfig.wireGuardData.pskKey.isEmpty()) {
clientConfigJson[config_key::psk_key] = clientProtocolConfig.wireGuardData.pskKey;
}
if (!clientProtocolConfig.wireGuardData.serverPubKey.isEmpty()) {
clientConfigJson[config_key::server_pub_key] = clientProtocolConfig.wireGuardData.serverPubKey;
}
if (!clientProtocolConfig.wireGuardData.mtu.isEmpty()) {
clientConfigJson[config_key::mtu] = clientProtocolConfig.wireGuardData.mtu;
}
if (!clientProtocolConfig.wireGuardData.allowedIps.isEmpty()) {
QJsonArray allowedIpsArray;
for (const auto &ip : clientProtocolConfig.wireGuardData.allowedIps) {
if (!ip.isEmpty()) {
allowedIpsArray.append(ip);
}
}
if (!allowedIpsArray.isEmpty()) {
clientConfigJson[config_key::allowed_ips] = allowedIpsArray;
}
}
if (!clientProtocolConfig.hostname.isEmpty()) {
clientConfigJson[config_key::hostName] = clientProtocolConfig.hostname;
}
if (clientProtocolConfig.port) {
clientConfigJson[config_key::port] = clientProtocolConfig.port;
}
if (!clientProtocolConfig.nativeConfig.isEmpty()) {
clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig;
}
if (!clientConfigJson.isEmpty()) {
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
}
}
return json;
}
bool WireGuardProtocolConfig::hasEqualServerSettings(const WireGuardProtocolConfig &other) const
{
if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress ||
serverProtocolConfig.port != other.serverProtocolConfig.port) {
return false;
}
return true;
}
void WireGuardProtocolConfig::clearClientSettings()
{
clientProtocolConfig = wireguard::ClientProtocolConfig();
}

View File

@@ -1,61 +0,0 @@
#ifndef WIREGUARDPROTOCOLCONFIG_H
#define WIREGUARDPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QStringList>
#include "protocolConfig.h"
namespace wireguard
{
struct WireGuardData
{
QStringList allowedIps;
QString clientIp;
QString clientPrivateKey;
QString clientPublicKey;
QString mtu;
QString persistentKeepAlive;
QString pskKey;
QString serverPubKey;
};
struct ServerProtocolConfig
{
QString port;
QString transportProto;
QString subnetAddress;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString clientId;
WireGuardData wireGuardData;
QString hostname;
int port;
QString nativeConfig;
};
}
class WireGuardProtocolConfig : public ProtocolConfig
{
public:
WireGuardProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const WireGuardProtocolConfig &other) const;
void clearClientSettings();
wireguard::ServerProtocolConfig serverProtocolConfig;
wireguard::ClientProtocolConfig clientProtocolConfig;
};
#endif // WIREGUARDPROTOCOLCONFIG_H

View File

@@ -1,58 +0,0 @@
#include "xrayProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
XrayProtocolConfig::XrayProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.site = protocolConfigObject.value(config_key::site).toString(protocols::xray::defaultSite);
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::xray::defaultPort);
serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString("tcp");
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
}
}
QJsonObject XrayProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.site.isEmpty()) {
json[config_key::site] = serverProtocolConfig.site;
}
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.transportProto.isEmpty()) {
json[config_key::transport_proto] = serverProtocolConfig.transportProto;
}
if (!clientProtocolConfig.isEmpty) {
QJsonObject clientConfigJson;
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
}
return json;
}
bool XrayProtocolConfig::hasEqualServerSettings(const XrayProtocolConfig &other) const
{
if (serverProtocolConfig.site != other.serverProtocolConfig.site ||
serverProtocolConfig.port != other.serverProtocolConfig.port ||
serverProtocolConfig.transportProto != other.serverProtocolConfig.transportProto) {
return false;
}
return true;
}
void XrayProtocolConfig::clearClientSettings()
{
clientProtocolConfig = xray::ClientProtocolConfig();
}

Some files were not shown because too many files have changed in this diff Show More