Compare commits

...

341 Commits

Author SHA1 Message Date
KsZnak
db1dc78e38 Update PageSettingsServerData.qml 2024-02-04 21:59:55 +02:00
agalehaga
fd98ef1250 Reboot server button (#553)
* add button Reboot Server
2024-02-04 16:52:03 +00:00
pokamest
cdf46c968a Merge pull request #480 from amnezia-vpn/feature/error-code-output
added error code output
2024-02-01 05:05:36 -08:00
pokamest
72b20ef563 Version bump - 4.3.0.0 2024-01-31 20:37:10 +00:00
pokamest
30a0ac0def Merge pull request #527 from amnezia-vpn/feature/api-awg
added support for awg configs for api
2024-01-31 12:04:54 -08:00
albexk
090208bd2c Bug fix for processing exclude routes for older android versions 2024-01-31 19:59:00 +03:00
vladimir.kuznetsov
1e5c9c9c4d if allowedIps from the backend is empty, split tunneling of the application works 2024-01-31 21:41:46 +07:00
vladimir.kuznetsov
5918f37ffa Merge branch 'feature/api-awg' of github.com:amnezia-vpn/amnezia-client into feature/api-awg 2024-01-31 21:30:47 +07:00
vladimir.kuznetsov
554e1b1b91 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/api-awg 2024-01-31 21:30:18 +07:00
vladimir.kuznetsov
8f510c1431 added allowedIPs processing for configs from the backend 2024-01-31 21:29:39 +07:00
vladimir.kuznetsov
4723019624 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2024-01-31 12:42:19 +07:00
vladimir.kuznetsov
1be9078b6c added break after each line in errorstrings 2024-01-31 12:42:05 +07:00
pokamest
520658a295 Merge pull request #550 from amnezia-vpn/KsZnak-patch-1
Ks znak patch 1
2024-01-30 15:58:50 -08:00
pokamest
b9ec722abb Merge pull request #543 from amnezia-vpn/KsZnak-patch-7
Update PageSettingsSplitTunneling.qml
2024-01-30 15:57:40 -08:00
pokamest
d917c798d7 Merge pull request #544 from amnezia-vpn/KsZnak-patch-8
Update PageSettingsAbout.qml
2024-01-30 15:57:10 -08:00
pokamest
0d168c039f Merge pull request #545 from amnezia-vpn/KsZnak-patch-6
Update PageSettingsSplitTunneling.qml
2024-01-30 15:56:50 -08:00
pokamest
af8f265555 Merge pull request #546 from amnezia-vpn/KsZnak-patch-5
Update PageSettingsConnection.qml
2024-01-30 15:56:29 -08:00
pokamest
c0e0d64284 Merge pull request #547 from amnezia-vpn/KsZnak-patch-4
Update PageServiceTorWebsiteSettings.qml
2024-01-30 15:56:02 -08:00
pokamest
9466a71141 Merge pull request #548 from amnezia-vpn/KsZnak-patch-3
Update PageProtocolOpenVpnSettings.qml
2024-01-30 15:55:29 -08:00
pokamest
d28a586a97 Merge pull request #549 from amnezia-vpn/KsZnak-patch-2
Update containers_defs.cpp
2024-01-30 15:55:04 -08:00
pokamest
6fde0b6663 Merge pull request #551 from amnezia-vpn/KsZnak-change-eng-text
Update PageSetupWizardCredentials.qml
2024-01-30 15:54:10 -08:00
KsZnak
d143b9213b Update PageSettingsAbout.qml 2024-01-30 20:50:30 +02:00
KsZnak
16433e9e46 Update PageSettingsSplitTunneling.qml 2024-01-30 20:40:11 +02:00
KsZnak
39479d1999 Update PageSettingsSplitTunneling.qml 2024-01-30 20:35:05 +02:00
KsZnak
a03b766e33 Update PageSettingsConnection.qml 2024-01-30 20:27:01 +02:00
KsZnak
7df2655ba0 Update PageServiceTorWebsiteSettings.qml 2024-01-30 20:22:26 +02:00
KsZnak
5c2ca9803d Update PageProtocolOpenVpnSettings.qml 2024-01-30 20:08:14 +02:00
KsZnak
4e6af947fa Update containers_defs.cpp 2024-01-30 20:05:18 +02:00
pokamest
bc9d5c8fd6 Merge branch 'dev' into feature/api-awg 2024-01-30 10:15:13 +00:00
pokamest
f412ac6260 Merge pull request #537 from amnezia-vpn/ios-log-3
Disable ioslogger (due memory leak)
2024-01-29 11:37:17 -08:00
pokamest
26d8dfbb7f Merge branch 'dev' into feature/api-awg 2024-01-29 19:35:35 +00:00
pokamest
b45517bafd Merge pull request #444 from amnezia-vpn/feature/killswitch
Kill Switch for desktop client
2024-01-29 11:23:23 -08:00
vladimir.kuznetsov
3edb6755b4 fixed filling in the wg private key 2024-01-30 00:47:22 +07:00
pokamest
bda64fa391 Merge pull request #536 from amnezia-vpn/bugfix/revoke-openvpn-client
fixed cache clearing when deleting admin configure
2024-01-29 07:31:09 -08:00
vladimir.kuznetsov
e7040f7cc8 specified error codes 2024-01-29 21:33:35 +07:00
pokamest
229970e799 Merge pull request #542 from amnezia-vpn/KsZnak-patch-2
Update amneziavpn_ru.ts
2024-01-28 13:10:49 -08:00
KsZnak
7b0a9a055f Update amneziavpn_ru.ts 2024-01-28 21:46:20 +02:00
pokamest
06d370f716 Merge pull request #541 from amnezia-vpn/version-bump
Version bump 4.2.1.1
2024-01-28 07:23:58 -08:00
albexk
ce0a4f1f96 Version bump 4.2.1.1 2024-01-28 17:29:54 +03:00
vladimir.kuznetsov
3240aa3cb3 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2024-01-28 21:24:23 +07:00
pokamest
5c5935c738 Merge pull request #539 from amnezia-vpn/bugfix/android
Fix Android bugs
2024-01-28 05:25:38 -08:00
pokamest
00b2b1abcd Merge pull request #529 from amnezia-vpn/bugfix/container-selection-after-cleanup-server
fixed first container selection on HomeContainersListView after server cleanup
2024-01-28 02:56:16 -08:00
Mykola Baibuz
e0891e1a15 Change license text 2024-01-28 05:39:12 -05:00
vladimir.kuznetsov
f7df621c56 fixed cache clearing when deleting admin configure
- added permissions for the crl.pem file
2024-01-27 18:09:14 +03:00
albexk
0b6dc5bcfc Add unbinding and destroying vpn service after disconnection 2024-01-27 17:34:57 +03:00
albexk
cbd6755aa5 Fix OpenVpn over Cloak 2024-01-27 17:30:56 +03:00
albexk
3afbc248b1 Refactor split-tunneling: separate site addresses from routes 2024-01-27 17:28:31 +03:00
Mykola Baibuz
30af81fe0a Move linuxfirewall header to "headers" part 2024-01-27 07:59:36 -05:00
Mykola Baibuz
427b43c99b Add code license 2024-01-27 07:50:50 -05:00
KsZnak
ed08ac6b46 Update containers_defs.cpp 2024-01-27 00:58:40 +02:00
KsZnak
1a2c1fa1b5 Update PageSetupWizardCredentials.qml 2024-01-27 00:18:17 +02:00
Igor Sorokin
709fbac231 Disable ioslogger (due memory leak) 2024-01-25 19:07:22 +03:00
pokamest
6b80a56f92 Merge pull request #530 from amnezia-vpn/bugfix/admin-user-clinet-management
fixed adding admin user to client management
2024-01-25 01:58:39 -08:00
Mykola Baibuz
5c9d45a8a8 Use MacOS logic for LinuxFirewall 2024-01-24 17:20:50 -05:00
pokamest
2edac24945 Merge pull request #532 from amnezia-vpn/bugfix/issue485
Fix autostart for Linux Desktop
2024-01-24 11:32:37 -08:00
albexk
b68c9cc145 Fix the double extension of the config file name: 'amnezia_config.vpn.vpn' 2024-01-24 17:00:48 +03:00
albexk
6f02d4eb62 Remove useless 'Open folder with logs' button for Adnroid too 2024-01-24 16:34:37 +03:00
pokamest
6e60688e70 Merge pull request #533 from amnezia-vpn/bugfix/easy-setup-random-port
random ports are now used for easy setup
2024-01-24 05:20:17 -08:00
vladimir.kuznetsov
140b5c4292 random ports are now used for easy setup 2024-01-24 12:27:11 +07:00
pokamest
8ee2ebbd20 Merge pull request #531 from amnezia-vpn/ios-log-2
Fix/iOS log (Part 2)
2024-01-23 14:18:17 -08:00
Igor Sorokin
b3eda4106d Fix: Share view is not showing on iPadOS 2024-01-23 23:41:08 +03:00
Mykola Baibuz
885e22be7c Fix autostart for Linux Desktop 2024-01-23 15:17:07 -05:00
vladimir.kuznetsov
83ec073734 fixed adding admin user to client management 2024-01-24 00:33:19 +07:00
vladimir.kuznetsov
0160b0f9dc editServer now does not update the whole model, but only the modified element 2024-01-23 23:57:14 +07:00
vladimir.kuznetsov
f55bd5e1a1 fixed first container selection on HomeContainersListView after server cleanup 2024-01-23 23:56:16 +07:00
Igor Sorokin
4d88eb8e79 Try to expand 'internal error' 2024-01-23 18:41:33 +03:00
Mykola Baibuz
874de74ac8 Add exclusion for VPN Server host (MacOS/OpenVPN) 2024-01-22 20:32:40 +02:00
pokamest
b3a4b34d48 Merge pull request #524 from amnezia-vpn/KsZnak-patch-1
Update amneziavpn_ru.ts
2024-01-22 07:27:45 -08:00
vladimir.kuznetsov
f7b9d2bae7 added support for awg configs for api 2024-01-22 16:51:11 +03:00
KsZnak
ec4574bfcf Update amneziavpn_ru.ts 2024-01-22 01:52:14 +02:00
pokamest
73cfab166f Merge pull request #523 from amnezia-vpn/fix/ios-log
Fix/iOS log (Part 1)
2024-01-21 13:02:28 -08:00
Igor Sorokin
0e8f85057d Remove distracting 'stale focus object' records from log 2024-01-21 21:15:34 +03:00
Igor Sorokin
2e4908c557 Records detailed disconnect error info to log 2024-01-21 20:41:06 +03:00
Igor Sorokin
401784414a Fix: 'OpenVPN' is recorded to the log instead of 'WireGuard' 2024-01-21 19:05:24 +03:00
Igor Sorokin
8495124bc8 Remove useless 'Open folder with logs' button (iOS) 2024-01-21 17:49:24 +03:00
pokamest
ba6ed540f5 Merge pull request #521 from amnezia-vpn/KsZnak-patch-2
Update README.md
2024-01-20 12:16:16 -08:00
KsZnak
19a60ea856 Update README.md
del signal link
2024-01-20 22:13:00 +02:00
pokamest
aecf1b463c Merge pull request #519 from amnezia-vpn/fix/android-deploy
Enable AAB build for all branches
2024-01-20 11:08:24 -08:00
pokamest
ff24ba1011 Merge pull request #518 from amnezia-vpn/fix/android-wgipv6
Fix wg address parameter parsing
2024-01-20 10:57:36 -08:00
albexk
802b708232 Enable AAB build for all branches 2024-01-20 21:49:37 +03:00
pokamest
adeff3efb9 Version bump 4.2.1.0 2024-01-20 18:46:33 +00:00
albexk
0103c1722e Fix wg address parameter parsing 2024-01-20 21:38:58 +03:00
pokamest
f9123e7b71 Merge pull request #516 from amnezia-vpn/bugfix/easy-setup-default-container
fixed easy setup default container selection
2024-01-20 09:18:57 -08:00
pokamest
0f9ed4e69c Merge pull request #515 from amnezia-vpn/fix/android-wg
set wg PresharedKey parameter as optional
2024-01-20 09:16:16 -08:00
vladimir.kuznetsov
9dfc95bac0 fixed easy setup default container selection 2024-01-20 23:28:20 +07:00
albexk
9b7914538f set wg PresharedKey parameter as optional 2024-01-20 18:43:26 +03:00
pokamest
85c9cd260d Merge pull request #514 from amnezia-vpn/fix/android-camera
Hide "QR-code" button from config import menu for Android devices wit…
2024-01-20 07:42:55 -08:00
pokamest
73751cc049 Merge pull request #513 from amnezia-vpn/bugfix/issue499
Fix import of some native WG configs
2024-01-20 07:27:54 -08:00
albexk
2b61c48303 Hide "QR-code" button from config import menu for Android devices without camera 2024-01-20 18:20:06 +03:00
Mykola Baibuz
47b03f2bf4 PSK paramater is not mandatory 2024-01-20 08:44:48 -05:00
albexk
3e02dfef63 Refactoring Android logging (#511)
Refactoring Android logging
2024-01-20 13:40:12 +00:00
Mykola Baibuz
4176d0130a Fix import of some native WG configs 2024-01-20 08:35:24 -05:00
pokamest
f6175c2c69 Merge pull request #512 from amnezia-vpn/fix/line-ending
Fix amnezia_application line-ending
2024-01-20 05:04:22 -08:00
albexk
b8b423ca19 Fix amnezia_application line-ending 2024-01-20 16:02:35 +03:00
pokamest
3702d69b9d Merge branch 'dev' into feature/killswitch 2024-01-19 14:58:28 +00:00
pokamest
3253de9384 Merge pull request #509 from amnezia-vpn/featute/amneziawg-apple
Change to amneziawg-apple
2024-01-19 02:17:02 -08:00
Igor Sorokin
51070635a5 Change to amneziawg-apple 2024-01-19 03:27:44 +03:00
pokamest
9b533f1ba6 Merge pull request #507 from amnezia-vpn/linux-installer
Improve Linux installer
2024-01-18 09:39:00 -08:00
tiaga
9371dd405e Improve Linux installer
Pack installer to a .tar archive in order to save executable bit for `AmneziaVPN_Linux_Installer.bin`.
2024-01-19 00:16:19 +07:00
pokamest
27d8108e55 Merge pull request #501 from amnezia-vpn/feature/api-spit-tunneling
for servers received via api, ignore the split tunneling settings
2024-01-18 08:51:12 -08:00
pokamest
820a3f0b77 Merge pull request #506 from amnezia-vpn/bugfix/client-management-ss-cloak
fixed append/rename/revoke client for cloak and ss on client management panel
2024-01-18 07:09:14 -08:00
pokamest
3d61b20271 Merge pull request #505 from amnezia-vpn/fix/readme
Update README.md
2024-01-18 07:03:08 -08:00
vladimir.kuznetsov
eec81f8124 fixed append/rename/revoke client for cloak and ss on client management panel 2024-01-18 21:29:56 +07:00
Igor Sorokin
d7fbddb97f Update README.md 2024-01-18 17:13:04 +03:00
pokamest
9170cb0318 Merge pull request #498 from amnezia-vpn/sort-installer-containers-first
show installed protocols first in protocols tab
2024-01-18 05:57:16 -08:00
pokamest
76dec6fa9c Merge pull request #504 from amnezia-vpn/feature/android-deploy
Add Android App Bundle build and upload
2024-01-18 05:56:50 -08:00
pokamest
4e3edcfc0a Merge pull request #503 from amnezia-vpn/fix/ipv6
Fix IPv6 problem
2024-01-18 05:56:07 -08:00
albexk
ad21d7ab64 Add Android App Bundle build and upload
Up upload-artifact action to version 4
2024-01-18 16:06:37 +03:00
Igor Sorokin
f576eb509d Fix IPv6 problem 2024-01-18 15:25:36 +03:00
vladimir.kuznetsov
33e229d0b2 added blocking of the page with dns settings for servers received by API 2024-01-17 23:38:20 +07:00
pokamest
78420d617b Merge pull request #502 from amnezia-vpn/fix/android-clipboard
Fix/android clipboard
2024-01-17 07:24:24 -08:00
albexk
2552e33d64 Add Android TextArea clipboard workaround 2024-01-17 17:46:54 +03:00
albexk
301141c755 Remove Android TextField workaround
It has been fixed in Qt 6.5.3, 6.6.0
2024-01-17 17:43:54 +03:00
vladimir.kuznetsov
fac57ac89a for servers received via api, ignore the split tunneling settings 2024-01-17 11:28:57 +07:00
pokamest
da5ad0a845 Merge pull request #500 from amnezia-vpn/fix/android-keyboard
Restore previous Android keyboard behavior
2024-01-16 05:50:28 -08:00
albexk
60aeefe1c2 Restore previous Android keyboard behaviour
There is a Qt bug related to this behaviour: https://bugreports.qt.io/browse/QTBUG-41170
2024-01-16 15:06:34 +03:00
Morteza Sherafati
d527f6a5ce show installed protocols first in protocols tab 2024-01-15 20:18:10 +00:00
pokamest
2802b42747 Merge pull request #491 from amnezia-vpn/bugfix/current-processed-server-selection
fixed current processed server selection after import/install new server
2024-01-15 05:43:34 -08:00
pokamest
66c5d2f0a8 Updated submodule qtkeychain 2024-01-15 10:06:20 +00:00
pokamest
6dbf4ac62c Release 4.2.0.1 2024-01-15 09:28:48 +00:00
pokamest
ae2872830b Updated submodule qtkeychain 2024-01-15 09:20:13 +00:00
pokamest
6f4a3587e4 Merge pull request #495 from amnezia-vpn/feature/update-awg-core
Update AWG core (v0.1.8)
2024-01-14 17:33:10 -05:00
Mykola Baibuz
145f51906e Update AWG core (v0.1.8) 2024-01-14 21:58:53 +02:00
pokamest
12e72bc74b Merge pull request #481 from amnezia-vpn/refactoring/android
Refactor Android open file method
2024-01-13 06:46:25 -05:00
pokamest
65a04799ef Merge branch 'dev' into feature/killswitch 2024-01-11 13:45:56 +00:00
vladimir.kuznetsov
88cd5825d3 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2024-01-11 20:26:06 +07:00
pokamest
ecdad2a315 Merge branch 'dev' into refactoring/android 2024-01-11 13:01:03 +00:00
pokamest
0398ddd6a2 Merge pull request #488 from amnezia-vpn/bugfix/split-tunnel-reset-on-app-reset
bugfix/split-tunnel-reset-on-app-reset
2024-01-11 07:58:04 -05:00
pokamest
50ea4d3b0f Merge pull request #453 from amnezia-vpn/bugfix/windows-crush-on-utf8-symbolos
added conversion using the system locale
2024-01-11 07:51:22 -05:00
pokamest
77be8169f2 Merge pull request #493 from amnezia-vpn/bugfix/secureqsettings-deadlock-2
now value and setValue of secureQSettings are always called in the main thread
2024-01-11 07:48:42 -05:00
lunardunno
7a435f76b6 ArchLinux_support (#463)
Arch linux support (#464)
2024-01-10 20:15:14 +00:00
pokamest
42949e0dea Merge pull request #492 from amnezia-vpn/bugfix/append-client
fixed insert rows count in appendClient function
2024-01-10 12:42:07 -05:00
pokamest
26db423232 Merge pull request #489 from amnezia-vpn/bugfix/remote-links-on-site
added the ability to change the site link via a translation file
2024-01-10 12:40:19 -05:00
vladimir.kuznetsov
645cf52803 now value and setValue of secureQSettings are always called in the main thread 2024-01-10 23:05:22 +07:00
vladimir.kuznetsov
673b8ad5b2 fixed insert rows count in appendClient function 2024-01-10 11:55:32 +07:00
vladimir.kuznetsov
2a03834bb2 fixed current processed server selection after import/install new server 2024-01-09 00:25:18 +07:00
lunardunno
c1b6149e49 Improve servercontroller.cpp for Ubuntu (#482)
Improve servercontroller.cpp for Ubuntu, CentOS, snd Fedora
2024-01-06 16:41:06 +00:00
vladimir.kuznetsov
0690d86e52 added the ability to change the site link via a translation file 2024-01-06 20:42:43 +07:00
vladimir.kuznetsov
bb8a11d110 added reset routeMode when resetting application settings 2024-01-06 20:38:41 +07:00
pokamest
4f7ba4c9a8 Merge pull request #483 from amnezia-vpn/persian_translation
full persian translation
2023-12-30 09:11:08 -05:00
Morteza Sherafati
aa41e4d915 full persian translation 2023-12-30 11:12:54 +00:00
pokamest
bb43b5451f Merge pull request #478 from amnezia-vpn/bugfix/macos-dns-resolve
Fix resolv-update script for MacOS (OpenVPN DNS)
2023-12-28 14:52:28 -05:00
vladimir.kuznetsov
69dd415ab5 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into bugfix/windows-crush-on-utf8-symbolos 2023-12-28 14:47:19 +07:00
albexk
e605f549bd Merge branch 'dev' into refactoring/android 2023-12-26 18:23:53 +03:00
albexk
a961932b2e Refactor AndroidUtils 2023-12-26 17:41:49 +03:00
albexk
e8cc80f046 Refactor Android open file method
Fix some bugs with mimetype filters when the Qt mimetype database does not match the Android database
2023-12-26 16:30:33 +03:00
vladimir.kuznetsov
67f29ac483 added conversion using the system locale 2023-12-26 13:36:21 +07:00
pokamest
c9bde5cdc0 Merge pull request #479 from amnezia-vpn/feature/client-management-migrations
added migration from version 3 of client management to version 4
2023-12-25 22:00:13 -05:00
pokamest
e878911819 Merge pull request #472 from amnezia-vpn/ksznak
Update amneziavpn_ru.ts
2023-12-25 13:36:03 -05:00
pokamest
f1a0b7f0ef Merge pull request #473 from amnezia-vpn/bugfix/new-client-on-connection
fixed hang after creating configuration on connection
2023-12-25 12:21:44 -05:00
KsZnak
3e0a5104e7 Update amneziavpn_ru.ts 2023-12-25 19:18:23 +02:00
vladimir.kuznetsov
8f53d563a4 moved the client table for cloak and ss to the openvpn folder 2023-12-25 22:49:24 +07:00
pokamest
414740ffb7 Merge pull request #467 from amnezia-vpn/feature/full-access-last-config
full access config no longer contains the last_config field
2023-12-25 10:29:16 -05:00
albexk
7437d47d92 Remove unnecessary permission RECEIVE_BOOT_COMPLETED 2023-12-25 15:16:22 +03:00
vladimir.kuznetsov
a68f19d72f added migration from version 3 of client management to version 4 2023-12-24 21:13:09 +07:00
vladimir.kuznetsov
11641c5e22 added error code output 2023-12-24 21:11:47 +07:00
Mykola Baibuz
8d3e21d46a Fix resolv-update script for MacOS (OpenVPN DNS)
script from https://github.com/andrewgdotcom/openvpn-mac-dns
2023-12-23 23:14:02 +02:00
pokamest
5ad54bfdc1 Merge pull request #452 from amnezia-vpn/refactoring/android
Android refactoring
2023-12-23 13:48:26 -05:00
Mykola Baibuz
a8f5e95fb1 MacOS OpenVPN/OpenVPN over Cloak killswitch 2023-12-23 16:04:17 +02:00
Mykola Baibuz
a4f3d08c02 Fix PF config path 2023-12-23 14:21:13 +02:00
Mykola Baibuz
3d2174d84e MacOS WG/AWG killswitch 2023-12-23 12:51:55 +02:00
pokamest
12fbc7d258 Merge pull request #474 from amnezia-vpn/bugfix/ss-in-cloak-container
returned shadowsocks to cloak container
2023-12-22 15:04:21 +00:00
albexk
164b7e2551 Merge branch 'dev' into refactoring/android 2023-12-22 15:38:29 +03:00
albexk
eafac491d8 Add a stub for errors coming from Android
These errors are related to VPN connection errors
2023-12-22 15:35:24 +03:00
pokamest
3cfb6e968d Merge pull request #475 from amnezia-vpn/bugfix/default-server-full-access-sharing
added selection of a default server for full access sharing
2023-12-22 12:21:59 +00:00
albexk
375825125f Fix cloak config 2023-12-22 15:11:48 +03:00
vladimir.kuznetsov
a8520e7545 added selection of a default server for full access sharing 2023-12-22 13:43:46 +07:00
vladimir.kuznetsov
ac154cdd83 returned shadowsocks to cloak container 2023-12-22 13:08:53 +07:00
vladimir.kuznetsov
9290775ab5 added removal of last_config when revoke admin config on client management panel 2023-12-21 23:34:27 +07:00
pokamest
d14e8cdee4 Merge pull request #460 from amnezia-vpn/bugfix/minor-ui-fixes
added an empty string check for the server name and user name change fields
2023-12-21 15:55:33 +00:00
vladimir.kuznetsov
7aac9f9d0e fixed hang after creating configuration on connection
- config created on connection is displayed as admin (platform name) on the client management page
- added config creation time on the client management page
2023-12-21 17:47:34 +07:00
KsZnak
41aaac7d32 Update amneziavpn_ru.ts 2023-12-20 20:16:35 +02:00
albexk
c8d2399db9 Merge branch 'dev' into refactoring/android 2023-12-20 20:47:19 +03:00
KsZnak
f37c8e5fd4 Update amneziavpn_ru.ts 2023-12-20 19:14:15 +02:00
pokamest
9f5025c10b Merge pull request #471 from amnezia-vpn/bugfix/macos_deploy_fix
build_macos.sh fix
2023-12-20 17:13:37 +00:00
pokamest
c27d999c41 build_macos.sh fix 2023-12-20 08:39:43 -08:00
pokamest
9681bea237 Update ts files 2023-12-20 12:14:06 +00:00
pokamest
8905d6352c Merge pull request #465 from amnezia-vpn/persian_translation
added persian translation file and menu item
2023-12-20 12:07:51 +00:00
vladimir.kuznetsov
8599b20678 full access config no longer contains the last_config field 2023-12-18 13:17:39 +07:00
Morteza Sherafati
491f09a51b added persian translation file and menu item 2023-12-17 18:37:07 +00:00
pokamest
0f1519a21f Merge pull request #462 from amnezia-vpn/bugfix/service-containers-search
fixed server search for sftp, dns and tor containers
2023-12-17 16:53:52 +00:00
Mykola Baibuz
1a17f2956a Fix build action 2023-12-16 10:05:54 -05:00
Mykola Baibuz
d94e27bfa9 Linux killswitch 2023-12-16 09:19:04 -05:00
vladimir.kuznetsov
b892156092 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2023-12-16 09:23:44 +07:00
vladimir.kuznetsov
7b1b8dc749 fixed server search for sftp, dns and tor containers 2023-12-15 13:58:30 +07:00
albexk
6d0167dcf3 Get back the ability to open configuration files
Doesn't work in all applications because some applications pass an abstract URI instead of the file path in the 'content' scheme
2023-12-14 22:10:22 +03:00
albexk
0c2d661e1c Disable ShadowSocks support on Android 2023-12-14 21:08:45 +03:00
albexk
95d1440d6f Some minor refactoring 2023-12-14 18:01:55 +03:00
albexk
0cabf60dc4 Fixes and refactoring in file saving and import functions 2023-12-14 18:00:58 +03:00
albexk
836ca1cc6b Switch to Qt fileprovider introduced in 6.6 2023-12-14 17:59:03 +03:00
pokamest
be799daafe Merge pull request #431 from amnezia-vpn/re_second_adaptation_to_diff_os
Improve install_docker.sh
2023-12-14 05:27:29 -08:00
pokamest
ad9d674a03 Merge pull request #368 from amnezia-vpn/feature/import-config-from-cloud
API support
2023-12-14 04:15:38 -08:00
vladimir.kuznetsov
f52c3c430f added notification after config revokation 2023-12-14 11:03:27 +07:00
vladimir.kuznetsov
a91ab0e910 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2023-12-13 14:21:51 +07:00
vladimir.kuznetsov
39d1f1677f fixed description update, after changing the default protocol 2023-12-13 14:14:37 +07:00
albexk
b0dcae3586 Disable global split tunneling if a non-default route exists in the Wireguard configuration 2023-12-12 22:48:18 +03:00
vladimir.kuznetsov
ed6351f8f1 added an empty string check for the server name and user name change fields 2023-12-12 17:15:10 +03:00
pokamest
e486a2fb26 Merge pull request #443 from amnezia-vpn/feature/cancel-installation-button
added a button to cancel installation if the package manager on the server is busy
2023-12-12 05:17:40 -08:00
albexk
195bdb947e Refactor import config
Remove the path filter, as the content path may not contain a filename.
Disable import when viewing files.
Config can be imported from:
- shared file
- shared text
- vpn:// link
2023-12-11 22:56:01 +03:00
albexk
1576aed1ea Add network state listening and reconnection
Vpn reconnects when the default network is changed
2023-12-11 15:16:50 +03:00
vladimir.kuznetsov
e66fbc3289 added default container installation, after downloading the config from the api 2023-12-11 13:42:42 +07:00
vladimir.kuznetsov
b4c89ad58f Reworked the interaction between models. Now only serversModel directly interacts with server config 2023-12-08 13:50:03 +07:00
albexk
8cc5846808 Fix application hangs in disconnection state 2023-12-07 22:43:33 +03:00
albexk
2eaaf01ca1 Up AGP to version 8.2.0 2023-12-07 19:28:41 +03:00
albexk
67694c0f96 Fix abort error: 'Pure virtual function called!' 2023-12-06 18:25:11 +03:00
albexk
163e5b2c52 Fix for Qt Creator 2023-12-06 18:25:11 +03:00
albexk
508f1d3a42 Add service bind timeout 2023-12-06 18:24:52 +03:00
albexk
d2207a5255 Up Gradle to version 8.5 2023-12-06 13:38:36 +03:00
albexk
bf03f5c9ae Fix for Qt Creator 2023-12-05 23:53:49 +03:00
albexk
656223f57d Fix rebinding to the service 2023-12-05 16:57:53 +03:00
albexk
dc6e3ec53b Add passing the VPN connection status when rebinding to the service 2023-12-05 16:10:29 +03:00
albexk
5835a756ce Add onError callback to handle errors in protocol threads 2023-12-05 13:56:01 +03:00
vladimir.kuznetsov
f3f98a50ed fixed include path of servercontroller 2023-12-05 17:49:11 +07:00
vladimir.kuznetsov
0f4bb78712 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2023-12-05 17:38:53 +07:00
vladimir.kuznetsov
c4014518cb renamed CloudController to ApiController 2023-12-05 17:38:38 +07:00
vladimir.kuznetsov
3605f62feb added support for the updated api 2023-12-05 17:28:14 +07:00
albexk
5c3e253067 Fix service description in AndroidManifest.xml 2023-12-05 13:09:28 +03:00
albexk
8d43cee52e Remove shadowsocks code 2023-12-04 18:45:53 +03:00
albexk
1e64413904 Fix disconnect bug 2023-12-04 18:23:08 +03:00
pokamest
6fd1ea26ee Merge pull request #438 from amnezia-vpn/bugfix/app-is-running-win11
fixed appProcessIsRunning() for win11
2023-12-01 03:24:49 -08:00
vladimir.kuznetsov
e619fd4af9 replaced loader with PageSetupWizardInstalling when updating container settings 2023-12-01 14:16:27 +07:00
albexk
e7658f9859 Add split tunneling 2023-12-01 00:12:50 +03:00
vladimir.kuznetsov
3defb09da9 added a button to cancel installation if the package manager on the server is busy 2023-11-30 19:21:57 +07:00
vladimir.kuznetsov
a672434909 changed tasklist | findstr на tasklist 2023-11-30 07:13:43 +03:00
Mykola Baibuz
c3fdd977b1 Windows OpenVPN/OpenVPN+Cloak killswitch feature 2023-11-29 22:50:36 +02:00
pokamest
dd233f77fc Merge pull request #432 from amnezia-vpn/feature/client-management
added client management
2023-11-29 07:04:56 -08:00
vladimir.kuznetsov
02efd9c217 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/client-management 2023-11-29 17:26:26 +03:00
albexk
20f3c0388a Add sending statistics 2023-11-29 17:08:53 +03:00
albexk
ef530780bd Fixes in AmneziaVpnService 2023-11-29 17:01:21 +03:00
albexk
d7ec611ff4 Fix bugs in awg and wireguard protocols 2023-11-29 16:55:17 +03:00
pokamest
b897e7102e Merge pull request #437 from amnezia-vpn/bugfix/minor-ui-fixes
Bugfix/minor UI fixes
2023-11-29 05:34:30 -08:00
pokamest
1cc5c5384e Merge pull request #407 from amnezia-vpn/feature/ss-and-cloak-native-configs
added native config generation for ss and cloak
2023-11-29 05:28:33 -08:00
vladimir.kuznetsov
db602ac65b fixed ss string generation 2023-11-29 10:57:47 +07:00
albexk
eaa209bc3a Add OpenVpn over Cloak module 2023-11-28 22:27:00 +03:00
albexk
51d4aea9e2 Add OpenVpn module 2023-11-28 20:07:39 +03:00
albexk
9738ada946 ProtocolApi refactoring, move network classes to NetworkUtils.kt 2023-11-28 19:47:22 +03:00
albexk
8ec105bee0 Move Log class to org.amnezia.vpn.util package 2023-11-28 19:07:32 +03:00
tiaga
4fd0852bb3 Improve install_docker.sh
- add delays before and after Docker activation
- cancel installation if `sudo` wasn't found
2023-11-27 21:47:54 +07:00
vladimir.kuznetsov
a53e904f7b fixed appProcessIsRunning() for win11 2023-11-27 13:56:52 +07:00
vladimir.kuznetsov
2d22b52b5d limited client name length 2023-11-27 13:42:08 +07:00
vladimir.kuznetsov
426ac49f6f fixed "auto-connect" option 2023-11-27 12:46:59 +07:00
vladimir.kuznetsov
c164814abd fixed default server setting after importing 2023-11-27 10:59:48 +07:00
vladimir.kuznetsov
3084892ed8 added selection of default server and container on the sharing page 2023-11-26 21:15:58 +07:00
albexk
9297f877c4 Add AWG module 2023-11-26 13:07:31 +03:00
vladimir.kuznetsov
1bf808c9ee fixed qr code generation for native configs 2023-11-25 13:02:02 +07:00
albexk
91f44fb394 Up Qt to version 6.6.1 2023-11-24 22:22:15 +03:00
albexk
385a52f676 Vpn service refactoring 2023-11-24 21:51:09 +03:00
albexk
8ef16781eb Remove old wireguard code 2023-11-24 21:49:54 +03:00
albexk
ad5ea1ca44 Add IpcMessenger class 2023-11-24 17:10:08 +03:00
albexk
0ba5d754d5 Android Activity refactoring 2023-11-23 21:20:31 +03:00
albexk
ccdcfdce8a Remove VPNPermissionHelper class 2023-11-23 16:49:32 +03:00
albexk
712fb4d0d3 Add Wireguard module 2023-11-23 16:45:15 +03:00
albexk
de65a03998 Add ProtocolApi module 2023-11-23 16:03:52 +03:00
albexk
6d6710db4a Prefs refactoring 2023-11-23 14:19:51 +03:00
albexk
c34c3e51ea Remove 'https://jitpack.io' repository 2023-11-23 14:13:38 +03:00
vladimir.kuznetsov
5dc3b64e0b added search bar for client management page 2023-11-23 14:54:49 +07:00
vladimir.kuznetsov
e8ceeb6e20 added full access sharing 2023-11-23 00:04:06 +07:00
pokamest
d38c7ce6a5 Error codes cleanup 2023-11-22 13:57:05 +00:00
albexk
e625543b94 CameraActivity refactoring 2023-11-21 23:35:26 +03:00
albexk
679bd4e4c9 Get rid of AppCompat theme 2023-11-21 21:31:49 +03:00
vladimir.kuznetsov
c6a312845a added client management 2023-11-21 20:31:53 +07:00
pokamest
ef0530ec6b Merge pull request #420 from amnezia-vpn/adaptation_to_different_os
Improve logic of install_docker.sh
2023-11-17 11:51:58 -08:00
albexk
2c98a90d60 Move icons to mipmap folders 2023-11-17 15:30:13 +03:00
albexk
b90fad6664 Android activity and AndroidController class refactoring 2023-11-17 15:10:11 +03:00
albexk
5a5ea4a018 Android project restructuring 2023-11-16 20:16:28 +03:00
albexk
bc68c487ee Update to qt 6.6.0 2023-11-16 19:49:36 +03:00
albexk
617cdf14ad Log class refactoring 2023-11-16 15:30:44 +03:00
albexk
f1c970461f Create utils module, move Log class there
BuildConfig class is now only created in the utils module
2023-11-16 15:15:02 +03:00
albexk
2fde47a86f Move qt binding java code to a separate module 2023-11-15 22:10:44 +03:00
albexk
4e5f2f44b6 Detach shadowsocks module 2023-11-15 22:08:00 +03:00
pokamest
e8a2e54d05 Typo fix 2023-11-15 12:51:39 +00:00
albexk
d77f3ecee8 Fix build error 2023-11-15 15:48:40 +03:00
albexk
06776ebe8f Fix errors 2023-11-14 23:19:46 +03:00
albexk
e1eec55f62 Refactoring build
Move to gradle kotlin DSL
Use gradle version catalog
All android build parameters are set via cmake files
Use gradle abi split to build APKs
Improve local development in the android project folder
2023-11-14 23:18:59 +03:00
albexk
fcabf08e74 Update gradle, fix gradlew.bat crlf 2023-11-14 23:13:14 +03:00
pokamest
b4694313a0 Merge pull request #426 from amnezia-vpn/ios-build
Change Qt mirror for builds
2023-11-14 09:54:54 -08:00
tiaga
abb2cae1f8 Change Qt mirror for builds
Use UC Berkeley mirror for installing Qt during a build. In addition, don't trigger builds on a tag push.
2023-11-14 23:39:15 +07:00
pokamest
b0004fd9dc Version bump 2023-11-14 12:50:52 +00:00
tiaga
362a82f944 Improve logic of install_docker.sh
- check packages update only when it's required
- avoid `dnf/yum update` for RHEL-based systems
2023-11-14 16:57:16 +07:00
pokamest
19fe61ed29 Merge pull request #423 from amnezia-vpn/r2
Upload new versions to R2
2023-11-13 09:34:30 -08:00
tiaga
72de38b4fb Upload new versions to R2
A new GitHub Actions workflow for a tagged commit which uploads installers for a desktop version to Cloudflare R2.
2023-11-10 23:19:00 +07:00
pokamest
02c0f96e5e Merge pull request #418 from amnezia-vpn/feature/split-tunnel-dns-forwad
Use DNS over VPN for ForwardSites mode split tunnel
2023-11-07 06:35:43 -08:00
vladimir.kuznetsov
aee82282ac Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/import-config-from-cloud 2023-11-06 14:10:43 +03:00
vladimir.kuznetsov
8497aeeb91 removed the debugging code 2023-11-06 14:10:31 +03:00
Mykola Baibuz
5e9f688000 Use DNS over VPN for ForwardSites mode split tunnel
This feature was in previous version of Split Tunnel
2023-11-04 15:28:59 -04:00
pokamest
6a7e346695 Merge pull request #416 from amnezia-vpn/bugfix/openvpn-exclude-route
Fix Split tunnel exclude sites mode for OpenVPN and Cloak. Windows.
2023-11-04 06:09:01 -07:00
Mykola Baibuz
071738116e Update Windows OpenVPN binary
This binary builded with ENABLE_DEBUG flag. This flag needed for ROUTE_GATEWAY varible output in log.
2023-11-03 17:29:40 -04:00
pokamest
147726ecb0 Merge branch 'dev' into feature/import-config-from-cloud 2023-11-01 21:42:07 +00:00
pokamest
ae4ee6431d Merge pull request #409 from useribs/patch-2
Update servercontroller.cpp, replace 2 calls (shred ; rm)  with one
2023-11-01 12:23:14 -07:00
vladimir.kuznetsov
9cfcb714ae added native config generation for ss and cloak 2023-11-01 21:29:58 +05:00
pokamest
d1ccde2a4b Merge pull request #396 from amnezia-vpn/bugfix/server-config-sync
bugfix/server config sync
2023-11-01 07:15:10 -07:00
useribs
4848091203 Update servercontroller.cpp, replace 2 calls (shred ; rm) with one (shred -u) 2023-10-30 20:09:13 +03:00
pokamest
282f159311 Merge pull request #402 from amnezia-vpn/bugfix/description_dns
Update DNS description
2023-10-29 09:07:42 -07:00
lunardunno
4ef8c77a2d Update DNS description 2023-10-29 17:56:10 +04:00
pokamest
08c1cf2439 Merge pull request #382 from amnezia-vpn/feature/split-tunnel-mobile
Split tunnel for missed Protocol/OS
2023-10-29 06:26:07 -07:00
Mykola Baibuz
2fc33875bb Bump version 2023-10-27 15:38:24 -04:00
Mykola Baibuz
9e92e4b5ff Merge branch 'dev' into feature/split-tunnel-mobile 2023-10-27 15:37:28 -04:00
pokamest
7f2cf70bf5 Merge pull request #393 from amnezia-vpn/bugfix/return-after-installation
fixed page return after installation
2023-10-26 12:40:39 -07:00
pokamest
40725d4155 Merge pull request #397 from amnezia-vpn/dev
Release 4.0.8
2023-10-26 11:45:11 -07:00
vladimir.kuznetsov
8164026891 fixed server config update, after container config change 2023-10-26 23:37:51 +05:00
Mykola Baibuz
0e23b3a1ac Allow traffic to Amezia DNS for all OS 2023-10-25 22:19:07 +03:00
Mykola Baibuz
1739d4861e Allow acces to Amnezia DNS when used only for selected sites 2023-10-25 21:50:35 +03:00
Mykola Baibuz
a6b6e7850d Allow traffic for excluded route on Windows kill switch 2023-10-24 23:07:07 +03:00
Mykola Baibuz
3e9dea6f07 Remove some not implemented notification 2023-10-24 13:37:40 +03:00
Mykola Baibuz
1b37ca805f Cleanup debug stuff 2023-10-24 11:10:16 +03:00
Mykola Baibuz
c772f56da7 Fixes after merge 2023-10-24 11:00:40 +03:00
Mykola Baibuz
bc183e39bb Merge branch 'feature/split-tunnel-mobile' of github.com:amnezia-vpn/amnezia-client into feature/split-tunnel-mobile 2023-10-24 00:35:58 +03:00
Mykola Baibuz
306d4f70a8 Update NE Sources 2023-10-24 00:33:35 +03:00
Mykola Baibuz
a386d39495 iOS Cloak/OVPN SplitTunnel 2023-10-24 00:28:41 +03:00
Mykola Baibuz
22b14dff5f iOS AWG/WG split tunnel 2023-10-23 22:42:02 +03:00
pokamest
e749cc7578 Update amneziavpn_ru.ts
Typo fix
2023-10-23 20:32:28 +01:00
vladimir.kuznetsov
3e03002ead added getting cloud config for cloak 2023-10-23 23:55:01 +05:00
vladimir.kuznetsov
4ae9cddcce fixed a typo in the serverController include 2023-10-23 23:53:46 +05:00
vladimir.kuznetsov
16724645ce Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/import-config-from-cloud 2023-10-23 21:40:18 +05:00
vladimir.kuznetsov
6a12cad1c9 fixed page return after installation 2023-10-23 21:33:07 +05:00
Mykola Baibuz
c15665803d Merge branch 'dev' into feature/split-tunnel-mobile 2023-10-22 15:26:20 -04:00
pokamest
97090888d5 Bump version 2023-10-22 08:11:37 -07:00
pokamest
4642308fbb Merge pull request #374 from amnezia-vpn/bugfix/split-tunneling
Bugfix/split tunneling
2023-10-22 08:02:43 -07:00
vladimir.kuznetsov
59bccb1188 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into bugfix/split-tunneling 2023-10-22 20:00:39 +05:00
pokamest
cd8fc007ac Merge pull request #392 from amnezia-vpn/bugfix/existing-awg-container
added getting awg parameters when adding an already installed awg container
2023-10-22 07:49:34 -07:00
Mykola Baibuz
f0b872e86b Merge remote-tracking branch 'origin/bugfix/pull-awg-config' into feature/split-tunnel-mobile 2023-10-21 23:24:54 +03:00
Mykola Baibuz
0c33432436 Fix pulling exiting AWG config from server 2023-10-21 14:55:15 -04:00
Mykola Baibuz
78c83b2e21 Some logic fix 2023-10-19 17:03:24 -04:00
Mykola Baibuz
414a47e2f2 WG/AWG Desktop AllowedIP from plain WG config 2023-10-19 14:50:51 -04:00
Mykola Baibuz
32c304dc1b WG/AWG SplitTunnel for desktop 2023-10-18 17:44:28 -04:00
Mykola Baibuz
4ea1a19572 Cleanup WG implementation 2023-10-18 13:41:58 -04:00
vladimir.kuznetsov
fcf6bb43b7 Merge branch 'bugfix/split-tunneling' of github.com:amnezia-vpn/amnezia-client into bugfix/split-tunneling 2023-10-18 12:18:46 +05:00
vladimir.kuznetsov
f5f72f87a6 fixed switcher status display for page split site tunneling 2023-10-18 12:17:24 +05:00
vladimir.kuznetsov
3340451245 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into bugfix/split-tunneling 2023-10-18 11:55:24 +05:00
Mykola Baibuz
c14f1b5000 Android OpenVPN/Cloak Split tunnel 2023-10-17 16:39:56 -04:00
Mykola Baibuz
546d4c1d3d WG/AWG Android splitTunnel 2023-10-17 14:54:46 -04:00
Mykola Baibuz
2a0166bb26 Merge branch 'bugfix/split-tunneling' of https://github.com/amnezia-vpn/amnezia-client into bugfix/split-tunneling 2023-10-16 12:05:50 -04:00
Mykola Baibuz
2df612ec1f Android SplitTunnel 2023-10-16 12:05:35 -04:00
vladimir.kuznetsov
9cf5590371 disabled split site tunneling for awg 2023-10-16 15:17:09 +05:00
vladimir.kuznetsov
2a4a01a4be removed split site tunneling page blocking when switcher is turned off 2023-10-16 13:28:37 +05:00
Mykola Baibuz
501670bdd2 Merge branch 'feature/split-tunneling-config' into bugfix/split-tunneling 2023-10-15 15:10:05 -04:00
vladimir.kuznetsov
24637a1693 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2023-10-15 21:08:45 +05:00
vladimir.kuznetsov
7bd1340190 fixed display of sites on page split tunneling 2023-10-15 20:41:49 +05:00
vladimir.kuznetsov
b78bf39767 added split tunneling to the config 2023-10-13 15:45:06 +05:00
vladimir.kuznetsov
25f8283edd added getting the config from the cloud service 2023-10-12 20:48:03 +05:00
pokamest
9090ec54e7 Merge pull request #339 from amnezia-vpn/dev
Release 3.1.0.1
2023-09-21 07:28:08 -07:00
378 changed files with 14869 additions and 86313 deletions

1
.gitattributes vendored
View File

@@ -3,3 +3,4 @@ deploy/data/windows/x64/tap/windows_10/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_7/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_10/OemVista.inf eol=crlf
client/3rd/* linguist-vendored
client/android/gradlew.bat eol=crlf

View File

@@ -1,7 +1,12 @@
name: 'Deploy workflow'
on:
push:
branches:
- '**'
on: [push]
env:
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
jobs:
Build-Linux-Ubuntu:
@@ -25,7 +30,7 @@ jobs:
setup-python: 'true'
tools: 'tools_ifw'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Get sources'
uses: actions/checkout@v3
@@ -43,14 +48,18 @@ jobs:
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
bash deploy/build_linux.sh
- name: 'Pack installer'
run: cd deploy && tar -cf AmneziaVPN_Linux_Installer.tar AmneziaVPN_Linux_Installer.bin
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Linux_installer
path: deploy/AmneziaVPN_Linux_Installer
name: AmneziaVPN_Linux_installer.tar
path: deploy/AmneziaVPN_Linux_Installer.tar
retention-days: 7
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Linux_unpacked
path: deploy/AppDir
@@ -89,7 +98,7 @@ jobs:
setup-python: 'true'
tools: 'tools_ifw'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Setup mvsc'
uses: ilammy/msvc-dev-cmd@v1
@@ -105,13 +114,14 @@ jobs:
call deploy\\build_windows.bat
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Windows_installer
path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe
retention-days: 7
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Windows_unpacked
path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release
@@ -119,15 +129,14 @@ jobs:
# ------------------------------------------------------
Build-IOS:
name: 'Build-IOS'
Build-iOS:
name: 'Build-iOS'
runs-on: macos-12
env:
QT_VERSION: 6.5.2
steps:
# Just select XCode
- name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1
with:
@@ -143,6 +152,7 @@ jobs:
arch: 'clang_64'
dir: ${{ runner.temp }}
set-env: 'true'
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install iOS Qt'
uses: jurplel/install-qt-action@v3
@@ -154,7 +164,7 @@ jobs:
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install go'
uses: actions/setup-go@v3
@@ -174,7 +184,7 @@ jobs:
- name: 'Setup ccache'
uses: hendrikmuhs/ccache-action@v1.2
- name: Install dependencies
- name: 'Install dependencies'
run: pip install jsonschema jinja2
- name: 'Build project'
@@ -195,7 +205,7 @@ jobs:
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
# - name: 'Upload appstore .ipa and dSYMs to artifacts'
# uses: actions/upload-artifact@v3
# uses: actions/upload-artifact@v4
# with:
# name: app-store ipa & dsyms
# path: |
@@ -232,7 +242,7 @@ jobs:
setup-python: 'true'
tools: 'tools_ifw'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Get sources'
uses: actions/checkout@v3
@@ -250,13 +260,14 @@ jobs:
bash deploy/build_macos.sh
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_MacOS_installer
path: AmneziaVPN.dmg
retention-days: 7
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_MacOS_unpacked
path: deploy/build/client/AmneziaVPN.app
@@ -268,21 +279,10 @@ jobs:
name: 'Build-Android'
runs-on: ubuntu-latest
strategy:
matrix:
include:
- abi: 'x86_64'
qt_arch: 'android_x86_64'
- abi: 'x86'
qt_arch: 'android_x86'
- abi: 'armeabi-v7a'
qt_arch: 'android_armv7'
- abi: 'arm64-v8a'
qt_arch: 'android_arm64_v8a'
env:
QT_VERSION: 6.5.2
ANDROID_BUILD_PLATFORM: android-33
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.6.1
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
steps:
- name: 'Install desktop Qt'
@@ -292,29 +292,58 @@ jobs:
host: 'linux'
target: 'desktop'
arch: 'gcc_64'
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android Qt'
- name: 'Install android_x86_64 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: ${{ matrix.qt_arch }}
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
arch: 'android_x86_64'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_x86'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_armv7'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_arm64_v8a'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Grant execute permission for qt-cmake'
shell: bash
run: |
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/${{ matrix.qt_arch }}/bin/qt-cmake
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin/qt-cmake
- name: 'Get sources'
uses: actions/checkout@v3
@@ -328,15 +357,14 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
java-version: '17'
cache: 'gradle'
- name: 'Setup Android NDK'
id: setup-ndk
uses: nttld/setup-ndk@v1
with:
ndk-version: 'r25c'
local-cache: 'true'
ndk-version: 'r26b'
- name: 'Decode keystore secret to file'
env:
@@ -349,16 +377,48 @@ jobs:
env:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64
QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
QT_ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
shell: bash
run: ./deploy/build_android.sh --apk ${{ matrix.abi }} --platform ${{ env.ANDROID_BUILD_PLATFORM }}
run: ./deploy/build_android.sh --aab --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
- name: 'Upload apk'
uses: actions/upload-artifact@v3
- name: 'Upload x86_64 apk'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-${{ matrix.abi }}
path: deploy/build/AmneziaVPN-${{ matrix.abi }}-release-signed.apk
name: AmneziaVPN-android-x86_64
path: deploy/build/AmneziaVPN-x86_64-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload x86 apk'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-x86
path: deploy/build/AmneziaVPN-x86-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload arm64-v8a apk'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-arm64-v8a
path: deploy/build/AmneziaVPN-arm64-v8a-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload armeabi-v7a apk'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-armeabi-v7a
path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload aab'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android
path: deploy/build/AmneziaVPN-release.aab
compression-level: 0
retention-days: 7

64
.github/workflows/tag-upload.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: 'Upload a new version'
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
jobs:
upload:
runs-on: ubuntu-latest
name: upload
steps:
- name: Checkout CMakeLists.txt
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
sparse-checkout: |
CMakeLists.txt
sparse-checkout-cone-mode: false
- name: Verify git tag
run: |
GIT_TAG=${{ github.ref_name }}
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
else
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
exit 1
fi
- name: Download artifacts from the "${{ github.ref_name }}" tag
uses: robinraju/release-downloader@v1.8
with:
tag: ${{ github.ref_name }}
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
out-file-path: ${{ github.ref_name }}
- name: Upload beta version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'dev')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: beta/${{ github.ref_name }}
- name: Upload stable version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'master')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: stable/${{ github.ref_name }}

6
.gitmodules vendored
View File

@@ -22,6 +22,6 @@
[submodule "client/3rd-prebuilt"]
path = client/3rd-prebuilt
url = https://github.com/amnezia-vpn/3rd-prebuilt
[submodule "client/3rd/awg-apple"]
path = client/3rd/awg-apple
url = https://github.com/amnezia-vpn/awg-apple
[submodule "client/3rd/amneziawg-apple"]
path = client/3rd/amneziawg-apple
url = https://github.com/amnezia-vpn/amneziawg-apple

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.0.8.5
project(${PROJECT} VERSION 4.3.0.0
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -11,6 +11,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 44)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")

View File

@@ -18,7 +18,6 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
[https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
[https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
[https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Telegram support channel (Russian)
[https://signal.group/...](https://signal.group/#CjQKIB2gUf8QH_IXnOJMGQWMDjYz9cNfmRQipGWLFiIgc4MwEhAKBONrSiWHvoUFbbD0xwdh) - Signal channel
## Tech
@@ -36,7 +35,7 @@ AmneziaVPN uses a number of open source projects to work:
Make sure to pull all submodules after checking out the repo.
```bash
git submodule update --init
git submodule update --init --recursive
```
## Development
@@ -50,7 +49,15 @@ Look deploy folder for build scripts.
1. First, make sure you have [XCode](https://developer.apple.com/xcode/) installed, at least version 14 or higher.
2. We use QT to generate the XCode project. we need QT version 6.4. Install QT for macos in [here](https://doc.qt.io/qt-6/macos.html)
2. We use QT to generate the XCode project. we need QT version 6.6.1. Install QT for macos in [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules:
- macOS
- iOS
- Qt 5 Compatibility Module
- Qt Shader Tools
- Additional Libraries:
- Qt Image Formats
- Qt Multimedia
- Qt Remote Objects
3. Install cmake is require. We recommend cmake version 3.25. You can install cmake in [here](https://cmake.org/download/)
@@ -66,10 +73,11 @@ gomobile init
5. Build project
```bash
export QT_BIN_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/ios/bin"
export QT_MACOS_ROOT_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/macos"
export QT_IOS_BIN=$QT_BIN_DIR
export PATH=$PATH:~/go/bin
mkdir build-ios
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_BIN_DIR
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
```
Replace PATH-TO-QT-FOLDER and QT-VERSION to your environment

1
client/3rd/amneziawg-apple vendored Submodule

Submodule client/3rd/awg-apple deleted from fab07138db

View File

@@ -56,6 +56,7 @@ set(CMAKE_AUTORCC ON)
set(AMNEZIAVPN_TS_FILES
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts
)
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
@@ -107,7 +108,7 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h
${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.h
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
@@ -146,7 +147,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp
${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.cpp
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp

View File

@@ -1,371 +1,389 @@
#include "amnezia_application.h"
#include <QClipboard>
#include <QFontDatabase>
#include <QMimeData>
#include <QQuickStyle>
#include <QResource>
#include <QStandardPaths>
#include <QTextDocument>
#include <QTimer>
#include <QTranslator>
#include <QQuickItem>
#include "logger.h"
#include "version.h"
#include "platforms/ios/QRCodeReaderBase.h"
#if defined(Q_OS_ANDROID)
#include "platforms/android/android_controller.h"
#endif
#include "protocols/qml_register_protocols.h"
#if defined(Q_OS_IOS)
#include "platforms/ios/ios_controller.h"
#endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
#else
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
int timeout, const QString &userData)
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
#endif
{
setQuitOnLastWindowClosed(false);
// Fix config file permissions
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
{
QSettings s(ORGANIZATION_NAME, APPLICATION_NAME);
s.setValue("permFixed", true);
}
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
#endif
m_settings = std::shared_ptr<Settings>(new Settings);
}
AmneziaApplication::~AmneziaApplication()
{
m_vpnConnectionThread.quit();
m_vpnConnectionThread.wait(3000);
if (m_engine) {
QObject::disconnect(m_engine, 0, 0, 0);
delete m_engine;
}
}
void AmneziaApplication::init()
{
m_engine = new QQmlApplicationEngine;
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->rootContext()->setContextProperty("Debug", &Logger::Instance());
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
initModels();
loadTranslator();
initControllers();
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::initialized, this,
[this](bool status, bool connected, const QDateTime &connectionDate) {
if (connected) {
m_connectionController->onConnectionStateChanged(Vpn::ConnectionState::Connected);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
}
});
if (!AndroidController::instance()->initialize()) {
qCritical() << QString("Init failed");
if (m_vpnConnection)
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Error);
return;
}
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
#endif
#ifdef Q_OS_IOS
IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
m_pageController->replaceStartPage();
m_pageController->goToPageSettingsBackup();
m_settingsController->importBackupFromOutside(filePath);
});
#endif
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
&NotificationHandler::setConnectionState);
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(),
&PageController::raiseMainWindow);
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
&ConnectionController::openConnection);
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
if (m_settings->isSaveLogs()) {
if (!Logger::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
}
#ifdef Q_OS_WIN
if (m_parser.isSet("a"))
m_pageController->showOnStartup();
else
emit m_pageController->raiseMainWindow();
#else
m_pageController->showOnStartup();
#endif
// TODO - fix
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (isPrimary()) {
QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
qDebug() << "Secondary instance started, showing this window instead";
emit m_pageController->raiseMainWindow();
});
}
#endif
// Android TextField clipboard workaround
// https://bugreports.qt.io/browse/QTBUG-113461
#ifdef Q_OS_ANDROID
QObject::connect(qApp, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state) {
if (state == Qt::ApplicationActive) {
if (qApp->clipboard()->mimeData()->formats().contains("text/html")) {
QTextDocument doc;
doc.setHtml(qApp->clipboard()->mimeData()->html());
qApp->clipboard()->setText(doc.toPlainText());
}
}
});
#endif
}
void AmneziaApplication::registerTypes()
{
qRegisterMetaType<ServerCredentials>("ServerCredentials");
qRegisterMetaType<DockerContainer>("DockerContainer");
qRegisterMetaType<TransportProto>("TransportProto");
qRegisterMetaType<Proto>("Proto");
qRegisterMetaType<ServiceType>("ServiceType");
declareQmlProtocolEnum();
declareQmlContainerEnum();
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
m_containerProps.reset(new ContainerProps());
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get());
m_protocolProps.reset(new ProtocolProps());
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get());
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
"ContainersModelFilters");
//
Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum();
}
void AmneziaApplication::loadFonts()
{
QQuickStyle::setStyle("Basic");
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
}
void AmneziaApplication::loadTranslator()
{
auto locale = m_settings->getAppLanguage();
m_translator.reset(new QTranslator());
updateTranslator(locale);
}
void AmneziaApplication::updateTranslator(const QLocale &locale)
{
if (!m_translator->isEmpty()) {
QCoreApplication::removeTranslator(m_translator.get());
}
QString strFileName = QString(":/translations/amneziavpn")+QLatin1String("_")+locale.name()+".qm";
if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale);
}
} else {
m_settings->setAppLanguage(QLocale::English);
}
m_engine->retranslate();
emit translationsUpdated();
}
bool AmneziaApplication::parseCommands()
{
m_parser.setApplicationDescription(APPLICATION_NAME);
m_parser.addHelpOption();
m_parser.addVersionOption();
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
m_parser.addOption(c_autostart);
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
m_parser.addOption(c_cleanup);
m_parser.process(*this);
if (m_parser.isSet(c_cleanup)) {
Logger::cleanUp();
QTimer::singleShot(100, this, [this] { quit(); });
exec();
return false;
}
return true;
}
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{
return m_engine;
}
void AmneziaApplication::initModels()
{
m_containersModel.reset(new ContainersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
connect(m_vpnConnection.get(), &VpnConnection::newVpnConfigurationCreated, m_containersModel.get(),
&ContainersModel::updateContainersConfig);
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex);
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
connect(m_containersModel.get(), &ContainersModel::defaultContainerChanged, this, [this]() {
if (m_containersModel->getDefaultContainer() == DockerContainer::WireGuard
&& m_sitesModel->getRouteMode() != Settings::RouteMode::VpnAllSites) {
m_sitesModel->setRouteMode(Settings::RouteMode::VpnAllSites);
emit m_pageController->showNotificationMessage(
tr("Split tunneling for WireGuard is not implemented, the option was disabled"));
}
});
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.reset(new ShadowSocksConfigModel(this));
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
m_cloakConfigModel.reset(new CloakConfigModel(this));
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
#endif
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
}
void AmneziaApplication::initControllers()
{
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
&PageController::showPassphraseRequestDrawer);
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
&InstallController::setEncryptedPassphrase);
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_settings, m_configurator));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoStartEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
}
#include "amnezia_application.h"
#include <QClipboard>
#include <QFontDatabase>
#include <QMimeData>
#include <QQuickStyle>
#include <QResource>
#include <QStandardPaths>
#include <QTextDocument>
#include <QTimer>
#include <QTranslator>
#include <QQuickItem>
#include "logger.h"
#include "version.h"
#include "platforms/ios/QRCodeReaderBase.h"
#if defined(Q_OS_ANDROID)
#include "platforms/android/android_controller.h"
#endif
#include "protocols/qml_register_protocols.h"
#if defined(Q_OS_IOS)
#include "platforms/ios/ios_controller.h"
#endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
#else
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
int timeout, const QString &userData)
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
#endif
{
setQuitOnLastWindowClosed(false);
// Fix config file permissions
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
{
QSettings s(ORGANIZATION_NAME, APPLICATION_NAME);
s.setValue("permFixed", true);
}
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
#endif
m_settings = std::shared_ptr<Settings>(new Settings);
}
AmneziaApplication::~AmneziaApplication()
{
m_vpnConnectionThread.quit();
m_vpnConnectionThread.wait(3000);
if (m_engine) {
QObject::disconnect(m_engine, 0, 0, 0);
delete m_engine;
}
}
void AmneziaApplication::init()
{
m_engine = new QQmlApplicationEngine;
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->rootContext()->setContextProperty("Debug", &Logger::Instance());
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
initModels();
loadTranslator();
initControllers();
#ifdef Q_OS_ANDROID
if(!AndroidController::initLogging()) {
qFatal("Android logging initialization failed");
}
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
connect(m_settings.get(), &Settings::saveLogsChanged,
AndroidController::instance(), &AndroidController::setSaveLogs);
connect(AndroidController::instance(), &AndroidController::initConnectionState, this,
[this](Vpn::ConnectionState state) {
m_connectionController->onConnectionStateChanged(state);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) {
qFatal("Android controller initialization failed");
}
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
#endif
#ifdef Q_OS_IOS
IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
m_pageController->replaceStartPage();
m_pageController->goToPageSettingsBackup();
m_settingsController->importBackupFromOutside(filePath);
});
#endif
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
&NotificationHandler::setConnectionState);
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(),
&PageController::raiseMainWindow);
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
&ConnectionController::openConnection);
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
&NotificationHandler::onTranslationsUpdated);
m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
#ifndef Q_OS_ANDROID
if (m_settings->isSaveLogs()) {
if (!Logger::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
}
#endif
#ifdef Q_OS_WIN
if (m_parser.isSet("a"))
m_pageController->showOnStartup();
else
emit m_pageController->raiseMainWindow();
#else
m_pageController->showOnStartup();
#endif
// TODO - fix
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (isPrimary()) {
QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
qDebug() << "Secondary instance started, showing this window instead";
emit m_pageController->raiseMainWindow();
});
}
#endif
// Android TextArea clipboard workaround
// Text from TextArea always has "text/html" mime-type:
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865
// Next, html is created for this mime-type:
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1885
// And this html goes to the Androids clipboard, i.e. text from TextArea is always copied as richText:
// /qt/6.6.1/Src/qtbase/src/plugins/platforms/android/androidjniclipboard.cpp:46
// So we catch all the copies to the clipboard and clear them from "text/html"
#ifdef Q_OS_ANDROID
connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, []() {
auto clipboard = QGuiApplication::clipboard();
if (clipboard->mimeData()->hasHtml()) {
clipboard->setText(clipboard->text());
}
});
#endif
}
void AmneziaApplication::registerTypes()
{
qRegisterMetaType<ServerCredentials>("ServerCredentials");
qRegisterMetaType<DockerContainer>("DockerContainer");
qRegisterMetaType<TransportProto>("TransportProto");
qRegisterMetaType<Proto>("Proto");
qRegisterMetaType<ServiceType>("ServiceType");
declareQmlProtocolEnum();
declareQmlContainerEnum();
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
m_containerProps.reset(new ContainerProps());
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get());
m_protocolProps.reset(new ProtocolProps());
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get());
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
"ContainersModelFilters");
//
Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum();
}
void AmneziaApplication::loadFonts()
{
QQuickStyle::setStyle("Basic");
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
}
void AmneziaApplication::loadTranslator()
{
auto locale = m_settings->getAppLanguage();
m_translator.reset(new QTranslator());
updateTranslator(locale);
}
void AmneziaApplication::updateTranslator(const QLocale &locale)
{
if (!m_translator->isEmpty()) {
QCoreApplication::removeTranslator(m_translator.get());
}
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale);
}
} else {
m_settings->setAppLanguage(QLocale::English);
}
m_engine->retranslate();
emit translationsUpdated();
}
bool AmneziaApplication::parseCommands()
{
m_parser.setApplicationDescription(APPLICATION_NAME);
m_parser.addHelpOption();
m_parser.addVersionOption();
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
m_parser.addOption(c_autostart);
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
m_parser.addOption(c_cleanup);
m_parser.process(*this);
if (m_parser.isSet(c_cleanup)) {
Logger::cleanUp();
QTimer::singleShot(100, this, [this] { quit(); });
exec();
return false;
}
return true;
}
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{
return m_engine;
}
void AmneziaApplication::initModels()
{
m_containersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(),
&ContainersModel::setDefaultContainer);
m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better?
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.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.reset(new ShadowSocksConfigModel(this));
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
m_cloakConfigModel.reset(new CloakConfigModel(this));
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
#endif
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked,
m_serversModel.get(), &ServersModel::clearCachedProfile);
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this,
[this](const QString &clientId, const QString &clientName, const DockerContainer container,
ServerCredentials credentials) {
m_serversModel->reloadContainerConfig();
m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
emit m_configurator->clientModelUpdated();
});
}
void AmneziaApplication::initControllers()
{
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(),
&ConnectionController::onTranslationsUpdated);
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
&PageController::showPassphraseRequestDrawer);
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
&InstallController::setEncryptedPassphrase);
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel,
m_settings, m_configurator));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled , m_serversModel.get(),
&ServersModel::toggleAmneziaDns);
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_cloudController.reset(new ApiController(m_serversModel, m_containersModel));
m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get());
}

View File

@@ -1,123 +1,127 @@
#ifndef AMNEZIA_APPLICATION_H
#define AMNEZIA_APPLICATION_H
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QThread>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "settings.h"
#include "vpnconnection.h"
#include "configurators/vpn_configurator.h"
#include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h"
#include "ui/controllers/importController.h"
#include "ui/controllers/installController.h"
#include "ui/controllers/pageController.h"
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
#include "ui/notificationhandler.h"
#ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h"
#endif
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/shadowsocksConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/sites_model.h"
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#define AMNEZIA_BASE_CLASS QGuiApplication
#else
#define AMNEZIA_BASE_CLASS SingleApplication
#define QAPPLICATION_CLASS QApplication
#include "singleapplication.h"
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
{
Q_OBJECT
public:
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication(int &argc, char *argv[]);
#else
AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false,
SingleApplication::Options options = SingleApplication::User, int timeout = 1000,
const QString &userData = {});
#endif
virtual ~AmneziaApplication();
void init();
void registerTypes();
void loadFonts();
void loadTranslator();
void updateTranslator(const QLocale &locale);
bool parseCommands();
QQmlApplicationEngine *qmlEngine() const;
signals:
void translationsUpdated();
private:
void initModels();
void initControllers();
QQmlApplicationEngine *m_engine {};
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
QSharedPointer<ContainerProps> m_containerProps;
QSharedPointer<ProtocolProps> m_protocolProps;
QSharedPointer<QTranslator> m_translator;
QCommandLineParser m_parser;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QScopedPointer<AwgConfigModel> m_awgConfigModel;
#ifdef Q_OS_WINDOWS
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
#endif
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;
QScopedPointer<NotificationHandler> m_notificationHandler;
QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<PageController> m_pageController;
QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController;
QScopedPointer<ExportController> m_exportController;
QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
};
#endif // AMNEZIA_APPLICATION_H
#ifndef AMNEZIA_APPLICATION_H
#define AMNEZIA_APPLICATION_H
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QThread>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "settings.h"
#include "vpnconnection.h"
#include "configurators/vpn_configurator.h"
#include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h"
#include "ui/controllers/importController.h"
#include "ui/controllers/installController.h"
#include "ui/controllers/pageController.h"
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/controllers/apiController.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
#include "ui/notificationhandler.h"
#ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h"
#endif
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/shadowsocksConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/clientManagementModel.h"
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#define AMNEZIA_BASE_CLASS QGuiApplication
#else
#define AMNEZIA_BASE_CLASS SingleApplication
#define QAPPLICATION_CLASS QApplication
#include "singleapplication.h"
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
{
Q_OBJECT
public:
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication(int &argc, char *argv[]);
#else
AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false,
SingleApplication::Options options = SingleApplication::User, int timeout = 1000,
const QString &userData = {});
#endif
virtual ~AmneziaApplication();
void init();
void registerTypes();
void loadFonts();
void loadTranslator();
void updateTranslator(const QLocale &locale);
bool parseCommands();
QQmlApplicationEngine *qmlEngine() const;
signals:
void translationsUpdated();
private:
void initModels();
void initControllers();
QQmlApplicationEngine *m_engine {};
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
QSharedPointer<ContainerProps> m_containerProps;
QSharedPointer<ProtocolProps> m_protocolProps;
QSharedPointer<QTranslator> m_translator;
QCommandLineParser m_parser;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QScopedPointer<AwgConfigModel> m_awgConfigModel;
#ifdef Q_OS_WINDOWS
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
#endif
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;
QScopedPointer<NotificationHandler> m_notificationHandler;
QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<PageController> m_pageController;
QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController;
QScopedPointer<ExportController> m_exportController;
QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<ApiController> m_cloudController;
};
#endif // AMNEZIA_APPLICATION_H

View File

@@ -1,166 +1,158 @@
<?xml version="1.0"?>
<!-- Leave package attribute for androiddeployqt -->
<manifest
package="org.amnezia.vpn"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.amnezia.vpn"
android:versionName="-- %%INSERT_VERSION_NAME%% --"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies
of the application. Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:anyDensity="true"
android:smallScreens="true"/>
<uses-permission android:name="android.permission.INTERNET" />
<!-- To request network state -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<application
android:name=".qt.AmneziaApp"
android:hardwareAccelerated="true"
android:name=".AmneziaApplication"
android:label="-- %%INSERT_APP_NAME%% --"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true"
android:allowNativeHeapPointerTagging="false"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:icon="@drawable/icon"
android:roundIcon="@drawable/icon_round">
android:icon="@mipmap/icon"
android:roundIcon="@mipmap/icon_round"
android:theme="@style/NoActionBar"
android:fullBackupContent="@xml/backup_content"
android:dataExtractionRules="@xml/data_extraction_rules"
tools:targetApi="s">
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name=".qt.VPNActivity"
android:label="-- %%INSERT_APP_NAME%% --"
android:screenOrientation="unspecified"
android:name=".AmneziaActivity"
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<!-- android:theme="@style/splashScreenTheme"-->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" />
<action android:name="org.amnezia.vpn.IMPORT_CONFIG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
<meta-data
android:name="android.app.extract_android_style"
android:value="minimal" />
<meta-data
android:name="android.app.background_running"
android:value="false"/>
<meta-data
android:name="android.app.arguments"
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
</activity>
<activity
android:name=".qt.CameraActivity"
android:name=".CameraActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false" />
<activity
android:name=".qt.ImportConfigActivity"
android:exported="true" >
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.vpn"/>
<data android:pathPattern=".*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
android:name=".VpnRequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false"
android:theme="@style/Translucent" />
<activity
android:name=".ImportConfigActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="true"
android:theme="@style/Translucent">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/octet-stream" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.cfg"/>
<data android:pathPattern=".*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="vpn" android:host="*" />
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.conf"/>
<data android:pathPattern=".*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.vpn" />
<data android:pathPattern=".*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\.cfg" />
<data android:pathPattern=".*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\.conf" />
<data android:pathPattern=".*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf" />
</intent-filter>
</activity>
<service
android:name=".VPNService"
android:process=":QtOnlyProcess"
android:name=".AmneziaVpnService"
android:process=":amneziaVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="connectedDevice"
android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
android:foregroundServiceType="specialUse"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
</service>
<service
android:name=".qt.VPNPermissionHelper"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="connectedDevice"
android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
</service>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.amnezia.vpn.fileprovider"
android:authorities="org.amnezia.vpn.qtprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider"/>
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
</provider>
</application>
</manifest>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +0,0 @@
[proxy_all]
[bypass_list]
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/29
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/3

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.awg"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":wireguard"))
}

View File

@@ -0,0 +1,80 @@
package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.json.JSONObject
/**
* Config example:
* {
* "protocol": "awg",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "awg_config_data": {
* "H1": "969537490",
* "H2": "481688153",
* "H3": "2049399200",
* "H4": "52029755",
* "Jc": "3",
* "Jmax": "1000",
* "Jmin": "50",
* "S1": "49",
* "S2": "60",
* "client_ip": "10.8.1.1",
* "hostName": "100.100.100.0",
* "port": 12345,
* "client_pub_key": "clientPublicKeyBase64",
* "client_priv_key": "privateKeyBase64",
* "psk_key": "presharedKeyBase64",
* "server_pub_key": "publicKeyBase64",
* "config": "[Interface]
* Address = 10.8.1.1/32
* DNS = 1.1.1.1, 1.0.0.1
* PrivateKey = privateKeyBase64
* Jc = 3
* Jmin = 50
* Jmax = 1000
* S1 = 49
* S2 = 60
* H1 = 969537490
* H2 = 481688153
* H3 = 2049399200
* H4 = 52029755
*
* [Peer]
* PublicKey = publicKeyBase64
* PresharedKey = presharedKeyBase64
* AllowedIPs = 0.0.0.0/0, ::/0
* Endpoint = 100.100.100.0:12345
* PersistentKeepalive = 25
* "
* }
* }
*/
class Awg : Wireguard() {
override val ifName: String = "awg0"
override fun parseConfig(config: JSONObject): AwgConfig {
val configDataJson = config.getJSONObject("awg_config_data")
val configData = parseConfigData(configDataJson.getString("config"))
return AwgConfig.build {
configWireguard(configData)
configSplitTunneling(config)
configData["Jc"]?.let { setJc(it.toInt()) }
configData["Jmin"]?.let { setJmin(it.toInt()) }
configData["Jmax"]?.let { setJmax(it.toInt()) }
configData["S1"]?.let { setS1(it.toInt()) }
configData["S2"]?.let { setS2(it.toInt()) }
configData["H1"]?.let { setH1(it.toLong()) }
configData["H2"]?.let { setH2(it.toLong()) }
configData["H3"]?.let { setH3(it.toLong()) }
configData["H4"]?.let { setH4(it.toLong()) }
}
}
}

View File

@@ -0,0 +1,108 @@
package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.wireguard.WireguardConfig
class AwgConfig private constructor(
wireguardConfigBuilder: WireguardConfig.Builder,
val jc: Int,
val jmin: Int,
val jmax: Int,
val s1: Int,
val s2: Int,
val h1: Long,
val h2: Long,
val h3: Long,
val h4: Long
) : WireguardConfig(wireguardConfigBuilder) {
private constructor(builder: Builder) : this(
builder,
builder.jc,
builder.jmin,
builder.jmax,
builder.s1,
builder.s2,
builder.h1,
builder.h2,
builder.h3,
builder.h4
)
override fun appendDeviceLine(sb: StringBuilder) = with(sb) {
super.appendDeviceLine(this)
appendLine("jc=$jc")
appendLine("jmin=$jmin")
appendLine("jmax=$jmax")
appendLine("s1=$s1")
appendLine("s2=$s2")
appendLine("h1=$h1")
appendLine("h2=$h2")
appendLine("h3=$h3")
appendLine("h4=$h4")
}
class Builder : WireguardConfig.Builder() {
private var _jc: Int? = null
internal var jc: Int
get() = _jc ?: throw BadConfigException("AWG: parameter jc is undefined")
private set(value) { _jc = value }
private var _jmin: Int? = null
internal var jmin: Int
get() = _jmin ?: throw BadConfigException("AWG: parameter jmin is undefined")
private set(value) { _jmin = value }
private var _jmax: Int? = null
internal var jmax: Int
get() = _jmax ?: throw BadConfigException("AWG: parameter jmax is undefined")
private set(value) { _jmax = value }
private var _s1: Int? = null
internal var s1: Int
get() = _s1 ?: throw BadConfigException("AWG: parameter s1 is undefined")
private set(value) { _s1 = value }
private var _s2: Int? = null
internal var s2: Int
get() = _s2 ?: throw BadConfigException("AWG: parameter s2 is undefined")
private set(value) { _s2 = value }
private var _h1: Long? = null
internal var h1: Long
get() = _h1 ?: throw BadConfigException("AWG: parameter h1 is undefined")
private set(value) { _h1 = value }
private var _h2: Long? = null
internal var h2: Long
get() = _h2 ?: throw BadConfigException("AWG: parameter h2 is undefined")
private set(value) { _h2 = value }
private var _h3: Long? = null
internal var h3: Long
get() = _h3 ?: throw BadConfigException("AWG: parameter h3 is undefined")
private set(value) { _h3 = value }
private var _h4: Long? = null
internal var h4: Long
get() = _h4 ?: throw BadConfigException("AWG: parameter h4 is undefined")
private set(value) { _h4 = value }
fun setJc(jc: Int) = apply { this.jc = jc }
fun setJmin(jmin: Int) = apply { this.jmin = jmin }
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
fun setS1(s1: Int) = apply { this.s1 = s1 }
fun setS2(s2: Int) = apply { this.s2 = s2 }
fun setH1(h1: Long) = apply { this.h1 = h1 }
fun setH2(h2: Long) = apply { this.h2 = h2 }
fun setH3(h3: Long) = apply { this.h3 = h3 }
fun setH4(h4: Long) = apply { this.h4 = h4 }
override fun build(): AwgConfig = configBuild().run { AwgConfig(this@Builder) }
}
companion object {
inline fun build(block: Builder.() -> Unit): AwgConfig = Builder().apply(block).build()
}
}

View File

@@ -1,153 +1,5 @@
apply plugin: 'com.github.ben-manes.versions'
buildscript {
ext{
kotlin_version = "1.7.22"
// for libwg
appcompatVersion = '1.1.0'
annotationsVersion = '1.0.1'
databindingVersion = '3.3.1'
jsr305Version = '3.0.2'
streamsupportVersion = '1.7.0'
threetenabpVersion = '1.1.1'
groupName = 'org.amnezia.vpn'
minSdkVer = '24'
cmakeMinVersion = "3.25.0+"
}
repositories {
google()
jcenter()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlinx-serialization'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation group: 'org.json', name: 'json', version: '20220924'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
implementation project(path: ':shadowsocks')
// CameraX core library using the camera2 implementation
def camerax_version = "1.2.1"
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
implementation("androidx.camera:camera-view:${camerax_version}")
implementation("androidx.camera:camera-extensions:${camerax_version}")
def camerax_ml_version = "1.2.0-beta02"
def ml_kit_version = "17.0.3"
implementation("androidx.camera:camera-mlkit-vision:${camerax_ml_version}")
implementation("com.google.mlkit:barcode-scanning:${ml_kit_version}")
}
androidExtensions {
experimental = true
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
ndkVersion androidNdkVersion
// Extract native libraries from the APK
packagingOptions.jniLibs.useLegacyPackaging true
dexOptions {
javaMaxHeapSize "3g"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qtAndroidDir + '/res', 'res']
resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
androidTest.assets.srcDirs += files("${qtAndroidDir}/schemas".toString())
}
}
tasks.withType(JavaCompile) {
options.incremental = true
}
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
lintOptions {
abortOnError false
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
defaultConfig {
resConfig "en"
minSdkVersion = 24
targetSdkVersion = 34
versionCode 37 // Change to a higher number
versionName "4.0.8" // Change to a higher number
javaCompileOptions.annotationProcessorOptions.arguments = [
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
]
}
}
// dummy file for androiddeployqt
// android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging

View File

@@ -0,0 +1,114 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("property-delegate")
}
kotlin {
jvmToolchain(17)
}
// get values from gradle or local properties
val qtTargetSdkVersion: String by gradleProperties
val qtTargetAbiList: String by gradleProperties
val outputBaseName: String by gradleProperties
android {
namespace = "org.amnezia.vpn"
buildFeatures {
viewBinding = true
}
androidResources {
// don't compress Qt binary resources file
noCompress += "rcc"
}
packaging {
// compress .so binary libraries
jniLibs.useLegacyPackaging = true
}
defaultConfig {
applicationId = "org.amnezia.vpn"
targetSdk = qtTargetSdkVersion.toInt()
// keeps language resources for only the locales specified below
resourceConfigurations += listOf("en", "ru", "b+zh+Hans")
}
sourceSets {
getByName("main") {
manifest.srcFile("AndroidManifest.xml")
java.setSrcDirs(listOf("src"))
res.setSrcDirs(listOf("res"))
// androyddeployqt creates the folders below
assets.setSrcDirs(listOf("assets"))
jniLibs.setSrcDirs(listOf("libs"))
}
}
signingConfigs {
register("release") {
storeFile = providers.environmentVariable("ANDROID_KEYSTORE_PATH").orNull?.let { file(it) }
storePassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
keyAlias = providers.environmentVariable("ANDROID_KEYSTORE_KEY_ALIAS").orNull
keyPassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
}
}
buildTypes {
release {
// exclude coroutine debug resource from release build
packaging {
resources.excludes += "DebugProbesKt.bin"
}
signingConfig = signingConfigs["release"]
}
}
splits {
abi {
isEnable = true
reset()
include(*qtTargetAbiList.split(',').toTypedArray())
isUniversalApk = false
}
}
// fix for Qt Creator to allow deploying the application to a device
// to enable this fix, add the line outputBaseName=android-build to local.properties
if (outputBaseName.isNotEmpty()) {
applicationVariants.all {
outputs.map { it as BaseVariantOutputImpl }
.forEach { output ->
if (output.outputFileName.endsWith(".apk")) {
output.outputFileName = "$outputBaseName-${buildType.name}.apk"
}
}
}
}
lint {
disable += "InvalidFragmentVersionForActivityResult"
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(project(":qt"))
implementation(project(":utils"))
implementation(project(":protocolApi"))
implementation(project(":wireguard"))
implementation(project(":awg"))
implementation(project(":openvpn"))
implementation(project(":cloak"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)
implementation(libs.kotlinx.coroutines)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
}

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.cloak"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":openvpn"))
}

View File

@@ -0,0 +1,79 @@
package org.amnezia.vpn.protocol.cloak
import android.util.Base64
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.amnezia.vpn.protocol.openvpn.OpenVpnConfig
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "cloak",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* "cloak_config_data": {
* "BrowserSig": "chrome",
* "EncryptionMethod": "aes-gcm",
* "NumConn": 1,
* "ProxyMethod": "openvpn",
* "PublicKey": "PublicKey=",
* "RemoteHost": "100.100.100.0",
* "RemotePort": "443",
* "ServerName": "servername",
* "StreamTimeout": 300,
* "Transport": "direct",
* "UID": "UID="
* }
* }
*/
class Cloak : OpenVpn() {
override fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
val openVpnConfigStr = config.getJSONObject("openvpn_config_data").getString("config")
val cloakConfigJson = checkCloakJson(config.getJSONObject("cloak_config_data"))
val cloakConfigStr = Base64.encodeToString(cloakConfigJson.toString().toByteArray(), Base64.DEFAULT)
val configStr = "$openVpnConfigStr\n<cloak>\n$cloakConfigStr\n</cloak>\n"
openVpnConfig.usePluggableTransports = true
openVpnConfig.content = configStr
return openVpnConfig
}
override fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {
// exclude remote server ip from vpn routes
val remoteServer = config.getString("hostName")
val remoteServerAddress = InetNetwork(parseInetAddress(remoteServer))
configBuilder.excludeRoute(remoteServerAddress)
}
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
cloakConfigJson.put("NumConn", 1)
cloakConfigJson.put("ProxyMethod", "openvpn")
if (cloakConfigJson.has("port")) {
val port = cloakConfigJson["port"]
cloakConfigJson.remove("port")
cloakConfigJson.put("RemotePort", port)
}
if (cloakConfigJson.has("remote")) {
val remote = cloakConfigJson["remote"]
cloakConfigJson.remove("remote")
cloakConfigJson.put("RemoteHost", remote)
}
return cloakConfigJson
}
}

View File

@@ -1,27 +1,46 @@
# Project-wide Gradle settings.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# Gradle caching allows reusing the build artifacts from a previous
# build with the same inputs. However, over time, the cache size will
# grow. Uncomment the following line to enable it.
#org.gradle.caching=true
# Specifies the JVM arguments used for the daemon process
org.gradle.jvmargs=-Xms512m -Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError \
-Dfile.encoding=UTF-8
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configureondemand=true
# Use AndroidX library instead of a Support Library
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Disable adding android:testOnly attribute to the manifest
android.injected.testOnly=false
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.bundle.enableUncompressedNativeLibs=false
androidBuildToolsVersion=30.0.2
androidCompileSdkVersion=30
org.gradle.caching=true
org.gradle.parallel=true
android.enableJetifier=true
android.injected.testOnly=false
kapt.use.worker.api=false
kapt.incremental.apt=false
# Disable providing custom values to resources from buildscript by default
android.defaults.buildfeatures.resvalues=false
# Disable compileShaders tasks by default
android.defaults.buildfeatures.shaders=false
# Disable Android resource processing for libraries by default
android.library.defaults.buildfeatures.androidresources=false
# Qt variables
# At build time androiddeployqt replaces these values with:
# androidCompileSdkVersion - androiddeployqt --android-platform parameter
# androidBuildToolsVersion - QT_ANDROID_SDK_BUILD_TOOLS_REVISION cmake target parameter
# qtMinSdkVersion - QT_ANDROID_MIN_SDK_VERSION cmake target parameter
# qtTargetSdkVersion - QT_ANDROID_TARGET_SDK_VERSION cmake target parameter
# androidNdkVersion - version from ANDROID_NDK_ROOT environment variable
# qtTargetAbiList - qt-cmake QT_ANDROID_ABIS parameter
# qtAndroidDir - path to qt binding java source code
# buildDir - hardcoded "build" value in androiddeployqt
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64
#qtAndroidDir=/QT_BASE/android_ABI/src/android/java
#buildDir=build
# Note about qtAndroidDir:
# Some IDEs (for example, IntelliJ IDEA) may index all data from a common root of the project and qtAndroidDir.
# Therefore, it's recommended to copy qt android files to a directory inside the project
# and specify the path to that directory in qtAndroidDir.

View File

@@ -0,0 +1,35 @@
[versions]
agp = "8.2.0"
kotlin = "1.9.20"
androidx-core = "1.12.0"
androidx-activity = "1.8.1"
androidx-annotation = "1.7.0"
androidx-camera = "1.3.0"
androidx-security-crypto = "1.1.0-alpha06"
kotlinx-coroutines = "1.7.3"
google-mlkit = "17.2.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
[bundles]
androidx-camera = [
"androidx-camera-core",
"androidx-camera-lifecycle",
"androidx-camera-view",
"androidx-camera-camera2"
]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View File

@@ -0,0 +1,25 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
kotlin {
jvmToolchain(17)
}
gradlePlugin {
plugins {
register("settingsGradlePropertyDelegate") {
id = "settings-property-delegate"
implementationClass = "SettingsPropertyDelegate"
}
register("projectGradlePropertyDelegate") {
id = "property-delegate"
implementationClass = "ProjectPropertyDelegate"
}
}
}

View File

@@ -0,0 +1,49 @@
import java.io.File
import java.io.FileInputStream
import java.io.InputStreamReader
import java.util.Properties
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.provider.ProviderFactory
private fun localProperties(rootDir: File) = Properties().apply {
val localProperties = File(rootDir, "local.properties")
if (localProperties.isFile) {
InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use {
load(it)
}
}
}
private class PropertyDelegate(
rootDir: File,
private val providers: ProviderFactory,
private val localProperties: Properties = localProperties(rootDir)
) : ReadOnlyProperty<Any?, String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String =
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name).orEmpty()
}
private lateinit var settingsPropertyDelegate: ReadOnlyProperty<Any?, String>
private lateinit var projectPropertyDelegate: ReadOnlyProperty<Any?, String>
class SettingsPropertyDelegate : Plugin<Settings> {
override fun apply(settings: Settings) {
settingsPropertyDelegate = PropertyDelegate(settings.rootDir, settings.providers)
}
}
class ProjectPropertyDelegate : Plugin<Project> {
override fun apply(project: Project) {
projectPropertyDelegate = PropertyDelegate(project.rootDir, project.providers)
}
}
val Settings.gradleProperties: ReadOnlyProperty<Any?, String>
get() = settingsPropertyDelegate
val Project.gradleProperties: ReadOnlyProperty<Any?, String>
get() = projectPropertyDelegate

Binary file not shown.

View File

@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

309
client/android/gradlew vendored
View File

@@ -1,78 +1,127 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -81,92 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -1,4 +1,20 @@
@if "%DEBUG%" == "" @echo off
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -9,19 +25,23 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +55,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,38 +65,26 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.openvpn"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(libs.kotlinx.coroutines)
}

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_Config {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Config obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ConnectionInfo {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ConnectionInfo obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_DynamicChallenge {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_DynamicChallenge obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_EvalConfig {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_EvalConfig obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_Event {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Event obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKIBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKICertRequest extends ClientAPI_ExternalPKIReque
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKICertRequest obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIRequestBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKIRequestBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKISignRequest extends ClientAPI_ExternalPKIReque
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKISignRequest obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_InterfaceStats {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_InterfaceStats obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_KeyValue {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_KeyValue obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_LLVector extends java.util.AbstractList<Long> implements
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_LLVector obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_LogInfo {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_LogInfo obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_MergeConfig {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_MergeConfig obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -20,6 +20,18 @@ public class ClientAPI_OpenVPNClient extends ClientAPI_TunBuilderBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_OpenVPNClient obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_OpenVPNClientHelper {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_OpenVPNClientHelper obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ProvideCreds {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ProvideCreds obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_RemoteOverride {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_RemoteOverride obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ServerEntry {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ServerEntry obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ServerEntryVector extends java.util.AbstractList<ClientAP
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ServerEntryVector obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_SessionToken {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_SessionToken obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_Status {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Status obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_StringVec extends java.util.AbstractList<String> implemen
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_StringVec obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_TransportStats {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_TransportStats obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_TunBuilderBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_TunBuilderBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__string {
protected static long getCPtr(SWIGTYPE_p_std__string obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(SWIGTYPE_p_std__string obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
}

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t {
protected static long getCPtr(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
}

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */

View File

@@ -0,0 +1,128 @@
package org.amnezia.vpn.protocol.openvpn
import android.content.Context
import android.net.VpnService.Builder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.util.net.getLocalNetworks
import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "openvpn",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* }
*/
open class OpenVpn : Protocol() {
private lateinit var context: Context
private var openVpnClient: OpenVpnClient? = null
private lateinit var scope: CoroutineScope
override val statistics: Statistics
get() {
openVpnClient?.let { client ->
val stats = client.transport_stats()
return Statistics.build {
setRxBytes(stats.bytesIn)
setTxBytes(stats.bytesOut)
}
}
return Statistics.EMPTY_STATISTICS
}
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
super.initialize(context, state, onError)
loadSharedLibrary(context, "ovpn3")
this.context = context
scope = CoroutineScope(Dispatchers.IO)
}
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient(
configBuilder = configBuilder,
state = state,
getLocalNetworks = { ipv6 -> getLocalNetworks(context, ipv6) },
establish = makeEstablish(vpnBuilder),
protect = protect,
onError = onError
)
try {
openVpnClient?.let { client ->
val openVpnConfig = parseConfig(config)
val evalConfig = client.eval_config(openVpnConfig)
if (evalConfig.error) {
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
}
configPluggableTransport(configBuilder, config)
configBuilder.configSplitTunneling(config)
scope.launch {
val status = client.connect()
if (status.error) {
state.value = DISCONNECTED
onError("OpenVpn connect() error: ${status.status}: ${status.message}")
}
}
}
} catch (e: Exception) {
openVpnClient = null
throw e
}
}
override fun stopVpn() {
openVpnClient?.stop()
openVpnClient = null
}
override fun reconnectVpn(vpnBuilder: Builder) {
openVpnClient?.let {
it.establish = makeEstablish(vpnBuilder)
it.reconnect(0)
}
}
protected open fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
openVpnConfig.content = config.getJSONObject("openvpn_config_data").getString("config")
return openVpnConfig
}
protected open fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {}
private fun makeEstablish(vpnBuilder: Builder): (OpenVpnConfig.Builder) -> Int = { configBuilder ->
val openVpnConfig = configBuilder.build()
buildVpnInterface(openVpnConfig, vpnBuilder)
vpnBuilder.establish().use { tunFd ->
if (tunFd == null) {
throw VpnStartException("Create VPN interface: permission not granted or revoked")
}
return@use tunFd.detachFd()
}
}
}

View File

@@ -0,0 +1,426 @@
package org.amnezia.vpn.protocol.openvpn
import android.net.ProxyInfo
import android.os.Build
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.getAndUpdate
import net.openvpn.ovpn3.ClientAPI_Config
import net.openvpn.ovpn3.ClientAPI_EvalConfig
import net.openvpn.ovpn3.ClientAPI_Event
import net.openvpn.ovpn3.ClientAPI_LogInfo
import net.openvpn.ovpn3.ClientAPI_OpenVPNClient
import net.openvpn.ovpn3.ClientAPI_Status
import net.openvpn.ovpn3.ClientAPI_StringVec
import net.openvpn.ovpn3.ClientAPI_TransportStats
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress
private const val TAG = "OpenVpnClient"
private const val EMULATED_EXCLUDE_ROUTES = (1 shl 16)
class OpenVpnClient(
private val configBuilder: OpenVpnConfig.Builder,
private val state: MutableStateFlow<ProtocolState>,
private val getLocalNetworks: (Boolean) -> List<InetNetwork>,
internal var establish: (OpenVpnConfig.Builder) -> Int,
private val protect: (Int) -> Boolean,
private val onError: (String) -> Unit
) : ClientAPI_OpenVPNClient() {
/**************************************************************************
* Tun builder callbacks
**************************************************************************/
// Tun builder methods, loosely based on the Android VpnService.Builder
// abstraction. These methods comprise an abstraction layer that
// allows the OpenVPN C++ core to call out to external methods for
// establishing the tunnel, adding routes, etc.
// All methods returning bool use the return
// value to indicate success (true) or fail (false).
// tun_builder_new() should be called first, then arbitrary setter methods,
// and finally tun_builder_establish to return the socket descriptor
// for the session. IP addresses are pre-validated before being passed to
// these methods.
// This interface is based on Android's VpnService.Builder.
// Callback to construct a new tun builder
// Should be called first.
override fun tun_builder_new(): Boolean {
Log.d(TAG, "tun_builder_new")
configBuilder.clearAddresses()
return true
}
// Callback to set MTU of the VPN interface
// Never called more than once per tun_builder session.
override fun tun_builder_set_mtu(mtu: Int): Boolean {
Log.d(TAG, "tun_builder_set_mtu: $mtu")
configBuilder.setMtu(mtu)
return true
}
// Callback to add network address to VPN interface
// May be called more than once per tun_builder session
override fun tun_builder_add_address(
address: String, prefix_length: Int,
gateway: String, ipv6: Boolean, net30: Boolean
): Boolean {
Log.d(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
configBuilder.addAddress(InetNetwork(address, prefix_length))
return true
}
// Callback to add route to VPN interface
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.d(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
if (address == "remote_host") return false
configBuilder.addRoute(InetNetwork(address, prefix_length))
return true
}
// Callback to exclude route from VPN interface
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.d(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
return true
}
// Callback to add DNS server to VPN interface
// May be called more than once per tun_builder session
// If reroute_dns is true, all DNS traffic should be routed over the
// tunnel, while if false, only DNS traffic that matches an added search
// domain should be routed.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_dns_server(address: String, ipv6: Boolean): Boolean {
Log.d(TAG, "tun_builder_add_dns_server: $address, $ipv6")
configBuilder.addDnsServer(parseInetAddress(address))
return true
}
// Optional callback that indicates whether traffic of a certain
// address family (AF_INET or AF_INET6) should be
// blocked or allowed, to prevent unencrypted packet leakage when
// the tunnel is IPv4-only/IPv6-only, but the local machine
// has connectivity with the other protocol to the internet.
// Controlled by "block-ipv6" and block-ipv6 config var.
// If addresses are added for a family this setting should be
// ignored for that family
// See also Android's VPNService.Builder.allowFamily method
/* override fun tun_builder_set_allow_family(af: Int, allow: Boolean): Boolean {
Log.d(TAG, "tun_builder_set_allow_family: $af, $allow")
return true
} */
// Callback to set address of remote server
// Never called more than once per tun_builder session.
override fun tun_builder_set_remote_address(address: String, ipv6: Boolean): Boolean {
Log.d(TAG, "tun_builder_set_remote_address: $address, $ipv6")
return true
}
// Optional callback that indicates OSI layer, should be 2 or 3.
// Defaults to 3.
override fun tun_builder_set_layer(layer: Int): Boolean {
Log.d(TAG, "tun_builder_set_layer: $layer")
return layer == 3
}
// Callback to set the session name
// Never called more than once per tun_builder session.
override fun tun_builder_set_session_name(name: String): Boolean {
Log.d(TAG, "tun_builder_set_session_name: $name")
return true
}
// Callback to establish the VPN tunnel, returning a file descriptor
// to the tunnel, which the caller will henceforth own. Returns -1
// if the tunnel could not be established.
// Always called last after tun_builder session has been configured.
override fun tun_builder_establish(): Int {
Log.d(TAG, "tun_builder_establish")
return establish(configBuilder)
}
// Callback to reroute default gateway to VPN interface.
// ipv4 is true if the default route to be added should be IPv4.
// ipv6 is true if the default route to be added should be IPv6.
// flags are defined in RGWFlags (rgwflags.hpp).
// Never called more than once per tun_builder session.
override fun tun_builder_reroute_gw(ipv4: Boolean, ipv6: Boolean, flags: Long): Boolean {
Log.d(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
if ((flags and EMULATED_EXCLUDE_ROUTES.toLong()) != 0L) return true
if (ipv4) {
configBuilder.addRoute(InetNetwork("0.0.0.0", 0))
}
if (ipv6) {
configBuilder.addRoute(InetNetwork("::", 0))
}
return true
}
// Callback to add search domain to DNS resolver
// May be called more than once per tun_builder session
// See tun_builder_add_dns_server above for description of
// reroute_dns parameter.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_search_domain(domain: String): Boolean {
Log.d(TAG, "tun_builder_add_search_domain: $domain")
configBuilder.setSearchDomain(domain)
return true
}
// Callback to set the HTTP proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
} catch (e: Exception) {
Log.e(TAG, "Could not set proxy: ${e.message}")
return false
}
}
return true
}
// Callback to set the HTTPS proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_https(host: String, port: Int): Boolean {
Log.d(TAG, "tun_builder_set_proxy_https: $host, $port")
return false
}
// When the exclude local network option is enabled this
// function is called to get a list of local networks so routes
// to exclude them from the VPN network are generated
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
override fun tun_builder_get_local_networks(ipv6: Boolean): ClientAPI_StringVec {
Log.d(TAG, "tun_builder_get_local_networks: $ipv6")
val networks = ClientAPI_StringVec()
for (address in getLocalNetworks(ipv6)) {
networks.add(address.toString())
}
return networks
}
// Optional callback to set default value for route metric.
// Guaranteed to be called before other methods that deal
// with routes such as tun_builder_add_route and
// tun_builder_reroute_gw. Route metric is ignored
// if < 0.
/* override fun tun_builder_set_route_metric_default(metric: Int): Boolean {
Log.d(TAG, "tun_builder_set_route_metric_default: $metric")
return super.tun_builder_set_route_metric_default(metric)
} */
// Callback to add a host which should bypass the proxy
// May be called more than once per tun_builder session
/* override fun tun_builder_add_proxy_bypass(bypass_host: String): Boolean {
Log.d(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
return super.tun_builder_add_proxy_bypass(bypass_host)
} */
// Callback to set the proxy "Auto Config URL"
// Never called more than once per tun_builder session.
/* override fun tun_builder_set_proxy_auto_config_url(url: String): Boolean {
Log.d(TAG, "tun_builder_set_proxy_auto_config_url: $url")
return super.tun_builder_set_proxy_auto_config_url(url)
} */
// Callback to add Windows WINS server to VPN interface.
// WINS server addresses are always IPv4.
// May be called more than once per tun_builder session.
// Guaranteed to be called after tun_builder_reroute_gw.
/* override fun tun_builder_add_wins_server(address: String): Boolean {
Log.d(TAG, "tun_builder_add_wins_server: $address")
return super.tun_builder_add_wins_server(address)
} */
// Optional callback to set a DNS suffix on tun/tap adapter.
// Currently only implemented on Windows, where it will
// set the "Connection-specific DNS Suffix" property on
// the TAP driver.
/* override fun tun_builder_set_adapter_domain_suffix(name: String): Boolean {
Log.d(TAG, "tun_builder_set_adapter_domain_suffix: $name")
return super.tun_builder_set_adapter_domain_suffix(name)
} */
// Return true if tun interface may be persisted, i.e. rolled
// into a new session with properties untouched. This method
// is only called after all other tests of persistence
// allowability succeed, therefore it can veto persistence.
// If persistence is ultimately enabled,
// tun_builder_establish_lite() will be called. Otherwise,
// tun_builder_establish() will be called.
/* override fun tun_builder_persist(): Boolean {
Log.d(TAG, "tun_builder_persist")
return super.tun_builder_persist()
} */
// Indicates a reconnection with persisted tun state.
/* override fun tun_builder_establish_lite() {
Log.d(TAG, "tun_builder_establish_lite")
super.tun_builder_establish_lite()
} */
// Indicates that tunnel is being torn down.
// If disconnect == true, then the teardown is occurring
// prior to final disconnect.
/* override fun tun_builder_teardown(disconnect: Boolean) {
Log.d(TAG, "tun_builder_teardown: $disconnect")
super.tun_builder_teardown(disconnect)
} */
/**************************************************************************
* Connection control methods
**************************************************************************/
// Parse OpenVPN configuration file.
override fun eval_config(arg0: ClientAPI_Config): ClientAPI_EvalConfig {
Log.d(TAG, "eval_config")
return super.eval_config(arg0)
}
// Primary VPN client connect method, doesn't return until disconnect.
// Should be called by a worker thread. This method will make callbacks
// to event() and log() functions. Make sure to call eval_config()
// and possibly provide_creds() as well before this function.
override fun connect(): ClientAPI_Status {
Log.d(TAG, "connect")
return super.connect()
}
// Callback to "protect" a socket from being routed through the tunnel.
// Will be called from the thread executing connect().
// The remote and ipv6 are the remote host this socket will connect to
override fun socket_protect(socket: Int, remote: String, ipv6: Boolean): Boolean {
Log.d(TAG, "socket_protect: $socket, $remote, $ipv6")
return protect(socket)
}
// Stop the client. Only meaningful when connect() is running.
// May be called asynchronously from a different thread
// when connect() is running.
override fun stop() {
Log.d(TAG, "stop")
super.stop()
}
// Pause the client -- useful to avoid continuous reconnection attempts
// when network is down. May be called from a different thread
// when connect() is running.
override fun pause(reason: String) {
Log.d(TAG, "pause: $reason")
super.pause(reason)
}
// Resume the client after it has been paused. May be called from a
// different thread when connect() is running.
override fun resume() {
Log.d(TAG, "resume")
super.resume()
}
// Do a disconnect/reconnect cycle n seconds from now. May be called
// from a different thread when connect() is running.
override fun reconnect(seconds: Int) {
Log.d(TAG, "reconnect: $seconds")
super.reconnect(seconds)
}
// When a connection is close to timeout, the core will call this
// method. If it returns false, the core will disconnect with a
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
// state.
override fun pause_on_connection_timeout(): Boolean {
Log.d(TAG, "pause_on_connection_timeout")
return false
}
// Return information about the most recent connection. Should be called
// after an event of type "CONNECTED".
/* override fun connection_info(): ClientAPI_ConnectionInfo {
Log.d(TAG, "connection_info")
return super.connection_info()
} */
/**************************************************************************
* Status callbacks
**************************************************************************/
// Callback for delivering events during connect() call.
// Will be called from the thread executing connect().
override fun event(event: ClientAPI_Event) {
val name = event.name
val info = event.info
Log.d(TAG, "OpenVpn event: $name: $info")
when (name) {
"COMPRESSION_ENABLED", "WARN" -> Log.w(TAG, "$name: $info")
"CONNECTED" -> state.value = CONNECTED
"DISCONNECTED" -> state.value = DISCONNECTED
"RECONNECTING" -> {
state.getAndUpdate { state ->
if (state == DISCONNECTED || state == CONNECTED) RECONNECTING
else state
}
}
}
if (event.error || event.fatal) {
state.value = DISCONNECTED
onError("OpenVpn ${if (event.error) "ERROR" else "FATAL"}: $name: $info")
}
}
// Callback for logging.
// Will be called from the thread executing connect().
override fun log(arg0: ClientAPI_LogInfo) {
arg0.text.dropLastWhile { it == '\n' }.let {
Log.d(TAG, "OpenVpnLog: $it")
}
}
/**************************************************************************
* Stats methods
**************************************************************************/
// return transport stats only
override fun transport_stats(): ClientAPI_TransportStats {
Log.d(TAG, "transport_stats")
return super.transport_stats()
}
// return a stats value, index should be >= 0 and < stats_n()
/* override fun stats_value(index: Int): Long {
Log.d(TAG, "stats_value: $index")
return super.stats_value(index)
} */
// return all stats in a bundle
/* override fun stats_bundle(): ClientAPI_LLVector {
Log.d(TAG, "stats_bundle")
return super.stats_bundle()
} */
// return tun stats only
/* override fun tun_stats(): ClientAPI_InterfaceStats {
Log.d(TAG, "tun_stats")
return super.tun_stats()
} */
// post control channel message
/* override fun post_cc_msg(msg: String) {
Log.d(TAG, "post_cc_msg: $msg")
super.post_cc_msg(msg)
} */
}

View File

@@ -0,0 +1,20 @@
package org.amnezia.vpn.protocol.openvpn
import org.amnezia.vpn.protocol.ProtocolConfig
private const val OPENVPN_DEFAULT_MTU = 1500
class OpenVpnConfig private constructor(
protocolConfigBuilder: ProtocolConfig.Builder
) : ProtocolConfig(protocolConfigBuilder) {
class Builder : ProtocolConfig.Builder(false) {
override var mtu: Int = OPENVPN_DEFAULT_MTU
override fun build(): OpenVpnConfig = configBuild().run { OpenVpnConfig(this@Builder) }
}
companion object {
inline fun build(block: Builder.() -> Unit): OpenVpnConfig = Builder().apply(block).build()
}
}

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol"
}
dependencies {
compileOnly(project(":utils"))
implementation(libs.androidx.annotation)
implementation(libs.kotlinx.coroutines)
}

View File

@@ -0,0 +1,9 @@
package org.amnezia.vpn.protocol
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)

View File

@@ -0,0 +1,191 @@
package org.amnezia.vpn.protocol
import android.annotation.SuppressLint
import android.content.Context
import android.net.IpPrefix
import android.net.VpnService
import android.net.VpnService.Builder
import android.os.Build
import android.system.OsConstants
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipFile
import kotlinx.coroutines.flow.MutableStateFlow
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.json.JSONObject
private const val TAG = "Protocol"
const val VPN_SESSION_NAME = "AmneziaVPN"
private const val SPLIT_TUNNEL_DISABLE = 0
private const val SPLIT_TUNNEL_INCLUDE = 1
private const val SPLIT_TUNNEL_EXCLUDE = 2
abstract class Protocol {
abstract val statistics: Statistics
protected lateinit var state: MutableStateFlow<ProtocolState>
protected lateinit var onError: (String) -> Unit
open fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
this.state = state
this.onError = onError
}
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun stopVpn()
abstract fun reconnectVpn(vpnBuilder: Builder)
protected fun ProtocolConfig.Builder.configSplitTunneling(config: JSONObject) {
if (!allowSplitTunneling) {
Log.i(TAG, "Global address split tunneling is prohibited, " +
"only tunneling from the protocol config is used")
return
}
val splitTunnelType = config.optInt("splitTunnelType")
if (splitTunnelType == SPLIT_TUNNEL_DISABLE) return
val splitTunnelSites = config.getJSONArray("splitTunnelSites")
val addressHandlerFunc = when (splitTunnelType) {
SPLIT_TUNNEL_INCLUDE -> ::includeAddress
SPLIT_TUNNEL_EXCLUDE -> ::excludeAddress
else -> throw BadConfigException("Unexpected value of the 'splitTunnelType' parameter: $splitTunnelType")
}
for (i in 0 until splitTunnelSites.length()) {
val address = InetNetwork.parse(splitTunnelSites.getString(i))
addressHandlerFunc(address)
}
}
protected open fun buildVpnInterface(config: ProtocolConfig, vpnBuilder: Builder) {
vpnBuilder.setSession(VPN_SESSION_NAME)
for (addr in config.addresses) {
Log.d(TAG, "addAddress: $addr")
vpnBuilder.addAddress(addr)
}
for (addr in config.dnsServers) {
Log.d(TAG, "addDnsServer: $addr")
vpnBuilder.addDnsServer(addr)
}
// fix for Samsung android ignoring DNS servers outside the VPN route range
if (Build.BRAND == "samsung") {
for (addr in config.dnsServers) {
Log.d(TAG, "addRoute: $addr")
vpnBuilder.addRoute(InetNetwork(addr))
}
}
config.searchDomain?.let {
Log.d(TAG, "addSearchDomain: $it")
vpnBuilder.addSearchDomain(it)
}
for (addr in config.routes) {
Log.d(TAG, "addRoute: $addr")
vpnBuilder.addRoute(addr)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
for (addr in config.excludedRoutes) {
Log.d(TAG, "excludeRoute: $addr")
vpnBuilder.excludeRoute(addr)
}
}
for (app in config.excludedApplications) {
Log.d(TAG, "addDisallowedApplication: $app")
vpnBuilder.addDisallowedApplication(app)
}
Log.d(TAG, "setMtu: ${config.mtu}")
vpnBuilder.setMtu(config.mtu)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
config.httpProxy?.let {
Log.d(TAG, "setHttpProxy: $it")
vpnBuilder.setHttpProxy(it)
}
}
if (config.allowAllAF) {
Log.d(TAG, "allowFamily")
vpnBuilder.allowFamily(OsConstants.AF_INET)
vpnBuilder.allowFamily(OsConstants.AF_INET6)
}
Log.d(TAG, "setBlocking: ${config.blockingMode}")
vpnBuilder.setBlocking(config.blockingMode)
vpnBuilder.setUnderlyingNetworks(null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
vpnBuilder.setMetered(false)
}
companion object {
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
Log.d(TAG, "Extracting library: $libraryName")
val apks = hashSetOf<String>()
context.applicationInfo.run {
sourceDir?.let { apks += it }
splitSourceDirs?.let { apks += it }
}
for (abi in Build.SUPPORTED_ABIS) {
for (apk in apks) {
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
val mappedName = System.mapLibraryName(libraryName)
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
val zipEntry = zipFile.getEntry(libraryZipPath)
zipEntry?.let {
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
FileOutputStream(destination).use { outStream ->
zipFile.getInputStream(zipEntry).use { inStream ->
inStream.copyTo(outStream, 32 * 1024)
outStream.fd.sync()
}
}
}
return true
}
}
}
return false
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
fun loadSharedLibrary(context: Context, libraryName: String) {
Log.d(TAG, "Loading library: $libraryName")
try {
System.loadLibrary(libraryName)
return
} catch (_: UnsatisfiedLinkError) {
Log.d(TAG, "Failed to load library, try to extract it from apk")
}
var tempFile: File? = null
try {
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
if (extractLibrary(context, libraryName, tempFile)) {
System.load(tempFile.absolutePath)
return
}
} catch (e: Exception) {
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
} finally {
tempFile?.delete()
}
}
}
}
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
private fun VpnService.Builder.addRoute(addr: InetNetwork) = addRoute(addr.address, addr.mask)
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun VpnService.Builder.excludeRoute(addr: InetNetwork) = excludeRoute(IpPrefix(addr.address, addr.mask))

View File

@@ -0,0 +1,173 @@
package org.amnezia.vpn.protocol
import android.net.ProxyInfo
import android.os.Build
import androidx.annotation.RequiresApi
import java.net.InetAddress
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.IpRange
import org.amnezia.vpn.util.net.IpRangeSet
open class ProtocolConfig protected constructor(
val addresses: Set<InetNetwork>,
val dnsServers: Set<InetAddress>,
val searchDomain: String?,
val routes: Set<InetNetwork>,
val excludedRoutes: Set<InetNetwork>,
val includedAddresses: Set<InetNetwork>,
val excludedAddresses: Set<InetNetwork>,
val excludedApplications: Set<String>,
val httpProxy: ProxyInfo?,
val allowAllAF: Boolean,
val blockingMode: Boolean,
val mtu: Int
) {
protected constructor(builder: Builder) : this(
builder.addresses,
builder.dnsServers,
builder.searchDomain,
builder.routes,
builder.excludedRoutes,
builder.includedAddresses,
builder.excludedAddresses,
builder.excludedApplications,
builder.httpProxy,
builder.allowAllAF,
builder.blockingMode,
builder.mtu
)
open class Builder(blockingMode: Boolean) {
internal val addresses: MutableSet<InetNetwork> = hashSetOf()
internal val dnsServers: MutableSet<InetAddress> = hashSetOf()
internal val routes: MutableSet<InetNetwork> = hashSetOf()
internal val excludedRoutes: MutableSet<InetNetwork> = hashSetOf()
internal val includedAddresses: MutableSet<InetNetwork> = hashSetOf()
internal val excludedAddresses: MutableSet<InetNetwork> = hashSetOf()
internal val excludedApplications: MutableSet<String> = hashSetOf()
internal var searchDomain: String? = null
private set
internal var httpProxy: ProxyInfo? = null
private set
internal var allowAllAF: Boolean = false
private set
internal var blockingMode: Boolean = blockingMode
private set
internal var allowSplitTunneling: Boolean = true
private set
open var mtu: Int = 0
protected set
fun addAddress(addr: InetNetwork) = apply { this.addresses += addr }
fun addAddresses(addresses: Collection<InetNetwork>) = apply { this.addresses += addresses }
fun clearAddresses() = apply { this.addresses.clear() }
fun addDnsServer(dnsServer: InetAddress) = apply { this.dnsServers += dnsServer }
fun addDnsServers(dnsServers: Collection<InetAddress>) = apply { this.dnsServers += dnsServers }
fun setSearchDomain(domain: String) = apply { this.searchDomain = domain }
fun addRoute(route: InetNetwork) = apply { this.routes += route }
fun addRoutes(routes: Collection<InetNetwork>) = apply { this.routes += routes }
fun removeRoute(route: InetNetwork) = apply { this.routes.remove(route) }
fun clearRoutes() = apply { this.routes.clear() }
fun excludeRoute(route: InetNetwork) = apply { this.excludedRoutes += route }
fun excludeRoutes(routes: Collection<InetNetwork>) = apply { this.excludedRoutes += routes }
fun includeAddress(addr: InetNetwork) = apply { this.includedAddresses += addr }
fun includeAddresses(addresses: Collection<InetNetwork>) = apply { this.includedAddresses += addresses }
fun excludeAddress(addr: InetNetwork) = apply { this.excludedAddresses += addr }
fun excludeAddresses(addresses: Collection<InetNetwork>) = apply { this.excludedAddresses += addresses }
fun excludeApplication(application: String) = apply { this.excludedApplications += application }
fun excludeApplications(applications: Collection<String>) = apply { this.excludedApplications += applications }
@RequiresApi(Build.VERSION_CODES.Q)
fun setHttpProxy(httpProxy: ProxyInfo) = apply { this.httpProxy = httpProxy }
fun setAllowAllAF(allowAllAF: Boolean) = apply { this.allowAllAF = allowAllAF }
fun setBlockingMode(blockingMode: Boolean) = apply { this.blockingMode = blockingMode }
fun disableSplitTunneling() = apply { this.allowSplitTunneling = false }
fun setMtu(mtu: Int) = apply { this.mtu = mtu }
private fun processSplitTunneling() {
if (includedAddresses.isNotEmpty() && excludedAddresses.isNotEmpty()) {
throw BadConfigException("Config contains addresses for inclusive and exclusive split tunneling at the same time")
}
if (includedAddresses.isNotEmpty()) {
// remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0))
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
// for older versions of Android, add the default route to the excluded routes
// to correctly build the excluded subnets list later
excludeRoute(InetNetwork("0.0.0.0", 0))
}
addRoutes(includedAddresses)
} else if (excludedAddresses.isNotEmpty()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// default routes are required for split tunneling in newer versions of Android
addRoute(InetNetwork("0.0.0.0", 0))
addRoute(InetNetwork("::", 0))
}
excludeRoutes(excludedAddresses)
}
}
private fun processExcludedRoutes() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && excludedRoutes.isNotEmpty()) {
// todo: rewrite, taking into account the current routes
// for older versions of Android, build a list of subnets without excluded routes
// and add them to routes
val ipRangeSet = IpRangeSet()
ipRangeSet.remove(IpRange("127.0.0.0", 8))
excludedRoutes.forEach {
ipRangeSet.remove(IpRange(it))
}
// remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0))
ipRangeSet.subnets().forEach(::addRoute)
addRoute(InetNetwork("2000::", 3))
}
}
private fun validate() {
val errorMessage = StringBuilder()
with(errorMessage) {
if (addresses.isEmpty()) appendLine("VPN interface network address not specified.")
if (routes.isEmpty()) appendLine("VPN interface route not specified.")
if (mtu == 0) appendLine("MTU not set.")
}
if (errorMessage.isNotEmpty()) throw BadConfigException(errorMessage.toString())
}
protected fun configBuild() {
processSplitTunneling()
processExcludedRoutes()
validate()
}
open fun build(): ProtocolConfig = configBuild().run { ProtocolConfig(this@Builder) }
}
companion object {
inline fun build(blockingMode: Boolean, block: Builder.() -> Unit): ProtocolConfig =
Builder(blockingMode).apply(block).build()
}
}

View File

@@ -0,0 +1,11 @@
package org.amnezia.vpn.protocol
// keep synchronized with client/platforms/android/android_controller.h ConnectionState
enum class ProtocolState {
CONNECTED,
CONNECTING,
DISCONNECTED,
DISCONNECTING,
RECONNECTING,
UNKNOWN
}

View File

@@ -0,0 +1,50 @@
package org.amnezia.vpn.protocol
import android.os.Bundle
private const val RX_BYTES_KEY = "rxBytes"
private const val TX_BYTES_KEY = "txBytes"
@Suppress("DataClassPrivateConstructor")
data class Statistics private constructor(
val rxBytes: Long = 0L,
val txBytes: Long = 0L
) {
private constructor(builder: Builder) : this(builder.rxBytes, builder.txBytes)
@Suppress("SuspiciousEqualsCombination")
fun isEmpty(): Boolean = this === EMPTY_STATISTICS || this == EMPTY_STATISTICS
class Builder {
var rxBytes: Long = 0L
private set
var txBytes: Long = 0L
private set
fun setRxBytes(rxBytes: Long) = apply { this.rxBytes = rxBytes }
fun setTxBytes(txBytes: Long) = apply { this.txBytes = txBytes }
fun build(): Statistics =
if (rxBytes + txBytes == 0L) EMPTY_STATISTICS
else Statistics(this)
}
companion object {
val EMPTY_STATISTICS: Statistics = Statistics()
inline fun build(block: Builder.() -> Unit): Statistics = Builder().apply(block).build()
}
}
fun Bundle.putStatistics(statistics: Statistics) {
putLong(RX_BYTES_KEY, statistics.rxBytes)
putLong(TX_BYTES_KEY, statistics.txBytes)
}
fun Bundle.getStatistics(): Statistics =
Statistics.build {
setRxBytes(getLong(RX_BYTES_KEY))
setTxBytes(getLong(TX_BYTES_KEY))
}

View File

@@ -0,0 +1,34 @@
package org.amnezia.vpn.protocol
import android.os.Bundle
private const val STATE_KEY = "state"
@Suppress("DataClassPrivateConstructor")
data class Status private constructor(
val state: ProtocolState
) {
private constructor(builder: Builder) : this(builder.state)
class Builder {
lateinit var state: ProtocolState
private set
fun setState(state: ProtocolState) = apply { this.state = state }
fun build(): Status = Status(this)
}
companion object {
inline fun build(block: Builder.() -> Unit): Status = Builder().apply(block).build()
}
}
fun Bundle.putStatus(status: Status) {
putInt(STATE_KEY, status.state.ordinal)
}
fun Bundle.getStatus(): Status =
Status.build {
setState(ProtocolState.entries[getInt(STATE_KEY)])
}

View File

@@ -0,0 +1,25 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id("property-delegate")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
val qtAndroidDir: String by gradleProperties
android {
namespace = "org.qtproject.qt.android.binding"
sourceSets {
getByName("main") {
java.setSrcDirs(listOf("$qtAndroidDir/src"))
res.setSrcDirs(listOf("$qtAndroidDir/res"))
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar", "*.aar"))))
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.Dialog">
<style name="NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="Translucent" parent="NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="ThemeOverlay.AppCompat.DayNight" parent="ThemeOverlay.AppCompat.Light"/>
</resources>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<exclude domain="sharedpref" path="." />
</full-backup-content>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
<exclude domain="sharedpref" path="." />
</cloud-backup>
<device-transfer>
<exclude domain="sharedpref" path="." />
</device-transfer>
</data-extraction-rules>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="/" />
</paths>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="files_path" path="/"/>
</paths>

View File

@@ -1,132 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 1000,
"identityHash": "14b379f7776710b79b9d617090efe40e",
"entities": [
{
"tableName": "Profile",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remotePort",
"columnName": "remotePort",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "method",
"columnName": "method",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remoteDns",
"columnName": "remoteDns",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "udpdns",
"columnName": "udpdns",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ipv6",
"columnName": "ipv6",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '14b379f7776710b79b9d617090efe40e')"
]
}
}

View File

@@ -1,46 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
]
}
}

View File

@@ -1,21 +0,0 @@
pluginManagement {
repositories {
google()
mavenCentral()
jcenter()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter()
maven { url 'https://jitpack.io' }
}
}
include ':shadowsocks'

View File

@@ -0,0 +1,60 @@
import com.android.build.api.dsl.SettingsExtension
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
includeBuild("./gradle/plugins")
}
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
includeBuild("./gradle/plugins")
plugins {
id("com.android.settings") version "8.2.0"
id("settings-property-delegate")
}
rootProject.name = "AmneziaVPN"
rootProject.buildFileName = "build.gradle.kts"
include(":qt")
include(":utils")
include(":protocolApi")
include(":wireguard")
include(":awg")
include(":openvpn")
include(":cloak")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties
val androidCompileSdkVersion: String by gradleProperties
val androidNdkVersion: String by gradleProperties
val qtMinSdkVersion: String by gradleProperties
// set default values for all modules
configure<SettingsExtension> {
buildToolsVersion = androidBuildToolsVersion
compileSdk = androidCompileSdkVersion.substringAfter('-').toInt()
minSdk = qtMinSdkVersion.toInt()
ndkVersion = androidNdkVersion
}
// stop Gradle running by androiddeployqt
gradle.taskGraph.whenReady {
if (providers.environmentVariable("ANDROIDDEPLOYQT_RUN").isPresent
&& !providers.systemProperty("explicitRun").isPresent) {
allTasks.forEach { it.enabled = false }
}
}

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