Compare commits

...

142 Commits

Author SHA1 Message Date
lunardunno
a5d7a9514a Rollback some changes to dev 2023-12-14 21:09:34 +04:00
lunardunno
1bae323a1a Back to some changes install_docker.sh 2023-12-14 21:00:12 +04:00
lunardunno
a36eafa56c variable replacement
replacing the $check_pkgs variable
2023-12-01 03:05:53 +04:00
lunardunno
4fd3760a35 Merge pull request #447 from amnezia-vpn/test_support_archlinux
Test support archlinux
2023-12-01 02:13:00 +04:00
lunardunno
51c46bae12 Merge branch 'ArchLinux-support' into test_support_archlinux 2023-12-01 01:57:04 +04:00
lunardunno
112df79f94 rollback to old dev 2023-12-01 01:28:41 +04:00
lunardunno
2904bb96b9 small edit 2023-12-01 00:34:33 +04:00
lunardunno
669f5c21dd small edit 2023-12-01 00:27:13 +04: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
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
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
lunardunno
4fbacab643 updating script variables
adding variables to update the list of packages in repositories and to install packages
2023-11-25 21:57:06 +04:00
vladimir.kuznetsov
1bf808c9ee fixed qr code generation for native configs 2023-11-25 13:02:02 +07: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
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
pokamest
e8a2e54d05 Typo fix 2023-11-15 12:51:39 +00: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
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
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
lunardunno
abb4678b0a feature/test_support_pacman2
adding pacman check to see if the server is busy
2023-10-30 17:14:46 +04:00
lunardunno
e609b22cf8 feature/test_support_pacman1
pacman was added as a test, assuming that all other components were pre-installed on the server.
2023-10-30 16:41:36 +04: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
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
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
vladimir.kuznetsov
7cfb38307e removed re-processing of server config for awg 2023-10-22 18:04:34 +05:00
vladimir.kuznetsov
994aa32745 added getting awg parameters when adding an already installed awg container 2023-10-22 17:31:13 +05: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
pokamest
0bb4dd9442 Text and translations fixes 2023-10-21 18:32:30 +01:00
pokamest
7a54dc15da Update amneziavpn_ru.ts 2023-10-21 16:33:21 +01:00
pokamest
e16a1100d8 Update amneziavpn_ru.ts 2023-10-21 16:20:57 +01:00
pokamest
99214e22e3 Fix docs url 2023-10-21 16:05:09 +01:00
pokamest
c77d35a2ed Merge pull request #390 from amnezia-vpn/revert-370-feature/custom_drawer_component
Revert "added new drawer2type for replacing drawertype"
2023-10-21 06:21:07 -07:00
pokamest
d98fdbdc5c Revert "added new drawer2type for replacing drawertype" 2023-10-21 14:17:45 +01:00
ronoaer
4551cf0a21 Merge pull request #370 from amnezia-vpn/feature/custom_drawer_component
added new drawer2type for replacing drawertype
2023-10-21 09:34:21 +08:00
ronoaer
023c3474d2 Merge branch 'dev' into feature/custom_drawer_component 2023-10-21 09:28:41 +08:00
pokamest
2a4cefb4bf Merge pull request #387 from amnezia-vpn/bugfix/awg-mtu-len-fix
Fix MTU len for Win WG/AWG
2023-10-20 15:02:29 -07:00
Mykola Baibuz
09305724fa Fix MTU len for Win WG/AWG 2023-10-20 16:44:30 -04:00
pokamest
360fda1ba7 Merge pull request #386 from amnezia-vpn/bugfix/minor-ui-fixes-4-version
Bugfix/minor UI fixes 4 version
2023-10-20 12:23:03 -07:00
vladimir.kuznetsov
dadf0cf96e Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into dev 2023-10-20 21:51:40 +05:00
vladimir.kuznetsov
3d60ac751e removed the default protocol/server change if connected to VPN 2023-10-20 20:52:14 +05:00
pokamest
32793eef8c Merge pull request #385 from amnezia-vpn/bugfix/translated_new_source_strings_to_chinese
translated new source strings to chinese
2023-10-20 06:36:17 -07:00
ronoaer
da1cdfd6fa translated new source strings to chinese 2023-10-20 18:01:57 +08:00
vladimir.kuznetsov
58ad7dc161 removed the "remove protocol" buttons from where they shouldn't be 2023-10-20 14:10:04 +05:00
ronoaer
0a15f44193 removed states 'opened', 'closed' 2023-10-20 10:38:12 +08:00
pokamest
e1dec3c1ba Merge pull request #384 from amnezia-vpn/bugfix/startCentos7docker
Restoring autostart and enable docker for CentOS 7
2023-10-19 18:30:45 -07:00
pokamest
7834860245 Merge pull request #383 from amnezia-vpn/feature/awg-random-values
Feature/awg random values
2023-10-19 18:28:13 -07:00
pokamest
2da1025f26 Random port on install 2023-10-20 02:25:40 +01: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
ronoaer
6c78b4ec8f enabled drag-pagehome-drawer in tabBar 2023-10-19 23:01:03 +08:00
ronoaer
a6949bd3ae resized questiondrawer of page serverdata 2023-10-19 19:45:22 +08:00
ronoaer
f7bed04ab2 removed invalid function code 2023-10-19 19:32:15 +08:00
ronoaer
6ec773079c added hovering effect of button 2023-10-19 11:22:52 +08:00
ronoaer
366e27a321 re-adatped pagehome 2023-10-19 09:27:39 +08:00
Mykola Baibuz
32c304dc1b WG/AWG SplitTunnel for desktop 2023-10-18 17:44:28 -04:00
vladimir.kuznetsov
338499247d changed the display order of containers 2023-10-19 01:16:36 +05:00
vladimir.kuznetsov
79e1761c1f added generation of random values for awg parameters 2023-10-19 01:14:09 +05:00
Mykola Baibuz
4ea1a19572 Cleanup WG implementation 2023-10-18 13:41:58 -04:00
pokamest
e2ae341ba9 AndroidManifest fix 2023-10-18 14:01:06 +01:00
pokamest
de03435bac Merge pull request #381 from amnezia-vpn/bugfix/minor-ui-fixes-4-version
Bugfix/minor UI fixes 4 version
2023-10-18 04:05:18 -07:00
pokamest
e16c425f87 PageHome.qml fix 2023-10-18 12:04:39 +01:00
ronoaer
c461e00c5c keeping parent's cusorshape and Drawer2Type's close-animation 2023-10-18 16:17:57 +08: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
vladimir.kuznetsov
a46e55d5c2 added a dash for drawerType 2023-10-18 01:11:41 +05:00
vladimir.kuznetsov
4b64bfaec0 fixed questionDrawer height 2023-10-18 00:37:15 +05:00
vladimir.kuznetsov
2f0c1eeecc fixed selection of default container after installing a new server 2023-10-18 00:36:40 +05:00
Mykola Baibuz
546d4c1d3d WG/AWG Android splitTunnel 2023-10-17 14:54:46 -04:00
lunardunno
160d88f002 Restoring autostart and enable docker for CentOS 7 2023-10-17 21:26:50 +04:00
ronoaer
a83cd29f72 fixed the cursorShape, and some minor issues 2023-10-17 22:00:19 +08:00
pokamest
94304b5777 Version bump 2023-10-17 14:47:31 +01:00
pokamest
61ddfe01a1 macos build script updated [no ci] 2023-10-17 06:39:49 -07:00
pokamest
00d334f704 Merge pull request #377 from amnezia-vpn/bugfix/minor-ui-fixes-4-version
disabled the ability to change the protocol/server when a vpn connection is active
2023-10-17 05:39:51 -07:00
pokamest
f4a4979997 Merge pull request #378 from amnezia-vpn/bugfix/updated_chinese_translations
updated Chinese translations for updating source strings
2023-10-17 05:11:19 -07:00
ronoaer
03171e4743 update background color and drag-effect, moved dulicated code 2023-10-17 19:34:34 +08:00
ronoaer
5369e68267 updated Chinese translations for updating source strings 2023-10-17 14:30:59 +08:00
vladimir.kuznetsov
9eb23e38bd disabled the ability to change the protocol/server when a vpn connection is active 2023-10-16 22:57:12 +05: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
pokamest
36ba3758db Translation updates 2023-10-16 15:27:26 +01:00
ronoaer
7cc0f39d3c adapted pagehome by new custom drawer type 2023-10-16 22:21:01 +08:00
vladimir.kuznetsov
9cf5590371 disabled split site tunneling for awg 2023-10-16 15:17:09 +05:00
pokamest
81f835458f Merge pull request #375 from amnezia-vpn/bugfix/minor-ui-fixes-4-version
Bugfix/minor UI fixes 4 version
2023-10-16 03:10:53 -07:00
vladimir.kuznetsov
e01b1db706 text corrections 2023-10-16 14:34:03 +05:00
vladimir.kuznetsov
cdb18de305 brought back the ability to share wireguard native format configs 2023-10-16 13:43:27 +05:00
vladimir.kuznetsov
8e0eef3316 fixed selection of default container after installing a new container 2023-10-16 13:40:43 +05:00
vladimir.kuznetsov
221d45f564 fixed pageSettingsDns width 2023-10-16 13:32:56 +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
ronoaer
cb5c09d967 adapted questionDrawer 2023-10-15 21:29:01 +08:00
ronoaer
29b4966119 shown ConnectionTypeSelectionDrawer on top level alway 2023-10-15 17:34:35 +08:00
ronoaer
d0f8358431 removed invalid code, and fixed top button hidden-shown 2023-10-15 17:29:22 +08:00
ronoaer
a75bd07cd8 fixed the clicked event 2023-10-15 15:54:05 +08:00
ronoaer
8c1835950b added transparent-background, for blocking clicked event 2023-10-15 15:17:04 +08:00
ronoaer
384ce9853b added new drawer2type for replacing drawertype 2023-10-14 23:00:31 +08:00
vladimir.kuznetsov
b78bf39767 added split tunneling to the config 2023-10-13 15:45:06 +05:00
85 changed files with 3300 additions and 1001 deletions

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
@@ -89,7 +94,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
@@ -119,15 +124,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 +147,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 +159,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 +179,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'
@@ -232,7 +237,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
@@ -296,7 +301,7 @@ jobs:
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android Qt'
uses: jurplel/install-qt-action@v3
@@ -309,7 +314,7 @@ jobs:
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Grant execute permission for qt-cmake'
shell: bash

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 }}

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.0.8.2
project(${PROJECT} VERSION 4.1.0.1
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)

View File

@@ -139,7 +139,8 @@ void AmneziaApplication::init()
&ConnectionController::openConnection);
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
&NotificationHandler::onTranslationsUpdated);
m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
@@ -226,14 +227,13 @@ void AmneziaApplication::loadTranslator()
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";
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale);
@@ -279,13 +279,17 @@ 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(),
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, 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);
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
&ServersModel::updateContainersConfig);
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
@@ -294,15 +298,7 @@ void AmneziaApplication::initModels()
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());
@@ -328,6 +324,11 @@ void AmneziaApplication::initModels()
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_configurator.get(), &VpnConfigurator::newVpnConfigCreated, m_clientManagementModel.get(),
&ClientManagementModel::appendClient);
}
void AmneziaApplication::initControllers()
@@ -335,7 +336,8 @@ 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);
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());
@@ -352,12 +354,12 @@ void AmneziaApplication::initControllers()
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_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_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoStartEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}

View File

@@ -39,6 +39,7 @@
#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()))
@@ -94,6 +95,7 @@ private:
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;

View File

@@ -45,6 +45,7 @@
android:label="-- %%INSERT_APP_NAME%% --"
android:screenOrientation="unspecified"
android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<!-- android:theme="@style/splashScreenTheme"-->

View File

@@ -138,8 +138,8 @@ android {
resConfig "en"
minSdkVersion = 24
targetSdkVersion = 34
versionCode 36 // Change to a higher number
versionName "4.0.8" // Change to a higher number
versionCode 39 // Change to a higher number
versionName "4.1.0" // Change to a higher number
javaCompileOptions.annotationProcessorOptions.arguments = [
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()

View File

@@ -0,0 +1,509 @@
/*
* Copyright (C) 2012-2017 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import androidx.annotation.NonNull;
/**
* Class that represents a range of IP addresses. This range could be a proper subnet, but that's
* not necessarily the case (see {@code getPrefix} and {@code toSubnets}).
*/
public class IPRange implements Comparable<IPRange>
{
private final byte[] mBitmask = { (byte)0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
private byte[] mFrom;
private byte[] mTo;
private Integer mPrefix;
/**
* Determine if the range is a proper subnet and, if so, what the network prefix is.
*/
private void determinePrefix()
{
boolean matching = true;
mPrefix = mFrom.length * 8;
for (int i = 0; i < mFrom.length; i++)
{
for (int bit = 0; bit < 8; bit++)
{
if (matching)
{
if ((mFrom[i] & mBitmask[bit]) != (mTo[i] & mBitmask[bit]))
{
mPrefix = (i * 8) + bit;
matching = false;
}
}
else
{
if ((mFrom[i] & mBitmask[bit]) != 0 || (mTo[i] & mBitmask[bit]) == 0)
{
mPrefix = null;
return;
}
}
}
}
}
private IPRange(byte[] from, byte[] to)
{
mFrom = from;
mTo = to;
determinePrefix();
}
public IPRange(String from, String to) throws UnknownHostException
{
this(Utils.parseInetAddress(from), Utils.parseInetAddress(to));
}
public IPRange(InetAddress from, InetAddress to)
{
initializeFromRange(from, to);
}
private void initializeFromRange(InetAddress from, InetAddress to)
{
byte[] fa = from.getAddress(), ta = to.getAddress();
if (fa.length != ta.length)
{
throw new IllegalArgumentException("Invalid range");
}
if (compareAddr(fa, ta) < 0)
{
mFrom = fa;
mTo = ta;
}
else
{
mTo = fa;
mFrom = ta;
}
determinePrefix();
}
public IPRange(String base, int prefix) throws UnknownHostException
{
this(Utils.parseInetAddress(base), prefix);
}
public IPRange(InetAddress base, int prefix)
{
this(base.getAddress(), prefix);
}
private IPRange(byte[] from, int prefix)
{
initializeFromCIDR(from, prefix);
}
private void initializeFromCIDR(byte[] from, int prefix)
{
if (from.length != 4 && from.length != 16)
{
throw new IllegalArgumentException("Invalid address");
}
if (prefix < 0 || prefix > from.length * 8)
{
throw new IllegalArgumentException("Invalid prefix");
}
byte[] to = from.clone();
byte mask = (byte)(0xff << (8 - prefix % 8));
int i = prefix / 8;
if (i < from.length)
{
from[i] = (byte)(from[i] & mask);
to[i] = (byte)(to[i] | ~mask);
Arrays.fill(from, i+1, from.length, (byte)0);
Arrays.fill(to, i+1, to.length, (byte)0xff);
}
mFrom = from;
mTo = to;
mPrefix = prefix;
}
public IPRange(String cidr) throws UnknownHostException
{
/* only verify the basic structure */
if (!cidr.matches("(?i)^(([0-9.]+)|([0-9a-f:]+))(-(([0-9.]+)|([0-9a-f:]+))|(/\\d+))?$"))
{
throw new IllegalArgumentException("Invalid CIDR or range notation");
}
if (cidr.contains("-"))
{
String[] parts = cidr.split("-");
InetAddress from = InetAddress.getByName(parts[0]);
InetAddress to = InetAddress.getByName(parts[1]);
initializeFromRange(from, to);
}
else
{
String[] parts = cidr.split("/");
InetAddress addr = InetAddress.getByName(parts[0]);
byte[] base = addr.getAddress();
int prefix = base.length * 8;
if (parts.length > 1)
{
prefix = Integer.parseInt(parts[1]);
}
initializeFromCIDR(base, prefix);
}
}
/**
* Returns the first address of the range. The network ID in case this is a proper subnet.
*/
public InetAddress getFrom()
{
try
{
return InetAddress.getByAddress(mFrom);
}
catch (UnknownHostException ignored)
{
return null;
}
}
/**
* Returns the last address of the range.
*/
public InetAddress getTo()
{
try
{
return InetAddress.getByAddress(mTo);
}
catch (UnknownHostException ignored)
{
return null;
}
}
/**
* If this range is a proper subnet returns its prefix, otherwise returns null.
*/
public Integer getPrefix()
{
return mPrefix;
}
@Override
public int compareTo(@NonNull IPRange other)
{
int cmp = compareAddr(mFrom, other.mFrom);
if (cmp == 0)
{ /* smaller ranges first */
cmp = compareAddr(mTo, other.mTo);
}
return cmp;
}
@Override
public boolean equals(Object o)
{
if (o == null || !(o instanceof IPRange))
{
return false;
}
return this == o || compareTo((IPRange)o) == 0;
}
@Override
public String toString()
{
try
{
if (mPrefix != null)
{
return InetAddress.getByAddress(mFrom).getHostAddress() + "/" + mPrefix;
}
return InetAddress.getByAddress(mFrom).getHostAddress() + "-" +
InetAddress.getByAddress(mTo).getHostAddress();
}
catch (UnknownHostException ignored)
{
return super.toString();
}
}
private int compareAddr(byte a[], byte b[])
{
if (a.length != b.length)
{
return (a.length < b.length) ? -1 : 1;
}
for (int i = 0; i < a.length; i++)
{
if (a[i] != b[i])
{
if (((int)a[i] & 0xff) < ((int)b[i] & 0xff))
{
return -1;
}
else
{
return 1;
}
}
}
return 0;
}
/**
* Check if this range fully contains the given range.
*/
public boolean contains(IPRange range)
{
return compareAddr(mFrom, range.mFrom) <= 0 && compareAddr(range.mTo, mTo) <= 0;
}
/**
* Check if this and the given range overlap.
*/
public boolean overlaps(IPRange range)
{
return !(compareAddr(mTo, range.mFrom) < 0 || compareAddr(range.mTo, mFrom) < 0);
}
private byte[] dec(byte[] addr)
{
for (int i = addr.length - 1; i >= 0; i--)
{
if (--addr[i] != (byte)0xff)
{
break;
}
}
return addr;
}
private byte[] inc(byte[] addr)
{
for (int i = addr.length - 1; i >= 0; i--)
{
if (++addr[i] != 0)
{
break;
}
}
return addr;
}
/**
* Remove the given range from the current range. Returns a list of resulting ranges (these are
* not proper subnets). At most two ranges are returned, in case the given range is contained in
* this but does not equal it, which would result in an empty list (which is also the case if
* this range is fully contained in the given range).
*/
public List<IPRange> remove(IPRange range)
{
ArrayList<IPRange> list = new ArrayList<>();
if (!overlaps(range))
{ /* | this | or | this |
* | range | | range | */
list.add(this);
}
else if (!range.contains(this))
{ /* we are not completely removed, so none of these cases applies:
* | this | or | this | or | this |
* | range | | range | | range | */
if (compareAddr(mFrom, range.mFrom) < 0 && compareAddr(range.mTo, mTo) < 0)
{ /* the removed range is completely within our boundaries:
* | this |
* | range | */
list.add(new IPRange(mFrom, dec(range.mFrom.clone())));
list.add(new IPRange(inc(range.mTo.clone()), mTo));
}
else
{ /* one end is within our boundaries the other at or outside it:
* | this | or | this | or | this | or | this |
* | range | | range | | range | | range | */
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : inc(range.mTo.clone());
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : dec(range.mFrom.clone());
list.add(new IPRange(from, to));
}
}
return list;
}
private boolean adjacent(IPRange range)
{
if (compareAddr(mTo, range.mFrom) < 0)
{
byte[] to = inc(mTo.clone());
return compareAddr(to, range.mFrom) == 0;
}
byte[] from = dec(mFrom.clone());
return compareAddr(from, range.mTo) == 0;
}
/**
* Merge two adjacent or overlapping ranges, returns null if it's not possible to merge them.
*/
public IPRange merge(IPRange range)
{
if (overlaps(range))
{
if (contains(range))
{
return this;
}
else if (range.contains(this))
{
return range;
}
}
else if (!adjacent(range))
{
return null;
}
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : range.mFrom;
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : range.mTo;
return new IPRange(from, to);
}
/**
* Split the given range into a sorted list of proper subnets.
*/
public List<IPRange> toSubnets()
{
ArrayList<IPRange> list = new ArrayList<>();
if (mPrefix != null)
{
list.add(this);
}
else
{
int i = 0, bit = 0, prefix, netmask, common_byte, common_bit;
int from_cur, from_prev = 0, to_cur, to_prev = 1;
boolean from_full = true, to_full = true;
byte[] from = mFrom.clone();
byte[] to = mTo.clone();
/* find a common prefix */
while (i < from.length && (from[i] & mBitmask[bit]) == (to[i] & mBitmask[bit]))
{
if (++bit == 8)
{
bit = 0;
i++;
}
}
prefix = i * 8 + bit;
/* at this point we know that the addresses are either equal, or that the
* current bits in the 'from' and 'to' addresses are 0 and 1, respectively.
* we now look at the rest of the bits as two binary trees (0=left, 1=right)
* where 'from' and 'to' are both leaf nodes. all leaf nodes between these
* nodes are addresses contained in the range. to collect them as subnets
* we follow the trees from both leaf nodes to their root node and record
* all complete subtrees (right for from, left for to) we come across as
* subnets. in that process host bits are zeroed out. if both addresses
* are equal we won't enter the loop below.
* 0_____|_____1 for the 'from' address we assume we start on a
* 0__|__ 1 0__|__1 left subtree (0) and follow the left edges until
* _|_ _|_ _|_ _|_ we reach the root of this subtree, which is
* | | | | | | | | either the root of this whole 'from'-subtree
* 0 1 0 1 0 1 0 1 (causing us to leave the loop) or the root node
* of the right subtree (1) of another node (which actually could be the
* leaf node we start from). that whole subtree gets recorded as subnet.
* next we follow the right edges to the root of that subtree which again is
* either the 'from'-root or the root node in the left subtree (0) of
* another node. the complete right subtree of that node is the next subnet
* we record. from there we assume that we are in that right subtree and
* recursively follow right edges to its root. for the 'to' address the
* procedure is exactly the same but with left and right reversed.
*/
if (++bit == 8)
{
bit = 0;
i++;
}
common_byte = i;
common_bit = bit;
netmask = from.length * 8;
for (i = from.length - 1; i >= common_byte; i--)
{
int bit_min = (i == common_byte) ? common_bit : 0;
for (bit = 7; bit >= bit_min; bit--)
{
byte mask = mBitmask[bit];
from_cur = from[i] & mask;
if (from_prev == 0 && from_cur != 0)
{ /* 0 -> 1: subnet is the whole current (right) subtree */
list.add(new IPRange(from.clone(), netmask));
from_full = false;
}
else if (from_prev != 0 && from_cur == 0)
{ /* 1 -> 0: invert bit to switch to right subtree and add it */
from[i] ^= mask;
list.add(new IPRange(from.clone(), netmask));
from_cur = 1;
}
/* clear the current bit */
from[i] &= ~mask;
from_prev = from_cur;
to_cur = to[i] & mask;
if (to_prev != 0 && to_cur == 0)
{ /* 1 -> 0: subnet is the whole current (left) subtree */
list.add(new IPRange(to.clone(), netmask));
to_full = false;
}
else if (to_prev == 0 && to_cur != 0)
{ /* 0 -> 1: invert bit to switch to left subtree and add it */
to[i] ^= mask;
list.add(new IPRange(to.clone(), netmask));
to_cur = 0;
}
/* clear the current bit */
to[i] &= ~mask;
to_prev = to_cur;
netmask--;
}
}
if (from_full && to_full)
{ /* full subnet (from=to or from=0.. and to=1.. after common prefix) - not reachable
* due to the shortcut at the top */
list.add(new IPRange(from.clone(), prefix));
}
else if (from_full)
{ /* full from subnet (from=0.. after prefix) */
list.add(new IPRange(from.clone(), prefix + 1));
}
else if (to_full)
{ /* full to subnet (to=1.. after prefix) */
list.add(new IPRange(to.clone(), prefix + 1));
}
}
Collections.sort(list);
return list;
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2012-2017 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
/**
* Class that represents a set of IP address ranges (not necessarily proper subnets) and allows
* modifying the set and enumerating the resulting subnets.
*/
public class IPRangeSet implements Iterable<IPRange>
{
private TreeSet<IPRange> mRanges = new TreeSet<>();
/**
* Parse the given string (space separated ranges in CIDR or range notation) and return the
* resulting set or {@code null} if the string was invalid. An empty set is returned if the given string
* is {@code null}.
*/
public static IPRangeSet fromString(String ranges)
{
IPRangeSet set = new IPRangeSet();
if (ranges != null)
{
for (String range : ranges.split("\\s+"))
{
try
{
set.add(new IPRange(range));
}
catch (Exception unused)
{ /* besides due to invalid strings exceptions might get thrown if the string
* contains a hostname (NetworkOnMainThreadException) */
return null;
}
}
}
return set;
}
/**
* Add a range to this set. Automatically gets merged with existing ranges.
*/
public void add(IPRange range)
{
if (mRanges.contains(range))
{
return;
}
reinsert:
while (true)
{
Iterator<IPRange> iterator = mRanges.iterator();
while (iterator.hasNext())
{
IPRange existing = iterator.next();
IPRange replacement = existing.merge(range);
if (replacement != null)
{
iterator.remove();
range = replacement;
continue reinsert;
}
}
mRanges.add(range);
break;
}
}
/**
* Add all ranges from the given set.
*/
public void add(IPRangeSet ranges)
{
if (ranges == this)
{
return;
}
for (IPRange range : ranges.mRanges)
{
add(range);
}
}
/**
* Add all ranges from the given collection to this set.
*/
public void addAll(Collection<? extends IPRange> coll)
{
for (IPRange range : coll)
{
add(range);
}
}
/**
* Remove the given range from this set. Existing ranges are automatically adjusted.
*/
public void remove(IPRange range)
{
ArrayList <IPRange> additions = new ArrayList<>();
Iterator<IPRange> iterator = mRanges.iterator();
while (iterator.hasNext())
{
IPRange existing = iterator.next();
List<IPRange> result = existing.remove(range);
if (result.size() == 0)
{
iterator.remove();
}
else if (!result.get(0).equals(existing))
{
iterator.remove();
additions.addAll(result);
}
}
mRanges.addAll(additions);
}
/**
* Remove the given ranges from ranges in this set.
*/
public void remove(IPRangeSet ranges)
{
if (ranges == this)
{
mRanges.clear();
return;
}
for (IPRange range : ranges.mRanges)
{
remove(range);
}
}
/**
* Get all the subnets derived from all the ranges in this set.
*/
public Iterable<IPRange> subnets()
{
return new Iterable<IPRange>()
{
@Override
public Iterator<IPRange> iterator()
{
return new Iterator<IPRange>()
{
private Iterator<IPRange> mIterator = mRanges.iterator();
private List<IPRange> mSubnets;
@Override
public boolean hasNext()
{
return (mSubnets != null && mSubnets.size() > 0) || mIterator.hasNext();
}
@Override
public IPRange next()
{
if (mSubnets == null || mSubnets.size() == 0)
{
IPRange range = mIterator.next();
mSubnets = range.toSubnets();
}
return mSubnets.remove(0);
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
@Override
public Iterator<IPRange> iterator()
{
return mRanges.iterator();
}
/**
* Returns the number of ranges, not subnets.
*/
public int size()
{
return mRanges.size();
}
@Override
public String toString()
{ /* we could use TextUtils, but that causes the unit tests to fail */
StringBuilder sb = new StringBuilder();
for (IPRange range : mRanges)
{
if (sb.length() > 0)
{
sb.append(" ");
}
sb.append(range.toString());
}
return sb.toString();
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2014-2019 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Utils
{
static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
/**
* Converts the given byte array to a hexadecimal string encoding.
*
* @param bytes byte array to convert
* @return hex string
*/
public static String bytesToHex(byte[] bytes)
{
char[] hex = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++)
{
int value = bytes[i];
hex[i*2] = HEXDIGITS[(value & 0xf0) >> 4];
hex[i*2+1] = HEXDIGITS[ value & 0x0f];
}
return new String(hex);
}
/**
* Validate the given proposal string
*
* @param ike true for IKE, false for ESP
* @param proposal proposal string
* @return true if valid
*/
public native static boolean isProposalValid(boolean ike, String proposal);
/**
* Parse an IP address without doing a name lookup
*
* @param address IP address string
* @return address bytes if valid
*/
private native static byte[] parseInetAddressBytes(String address);
/**
* Parse an IP address without doing a name lookup (as compared to InetAddress.fromName())
*
* @param address IP address string
* @return address if valid
* @throws UnknownHostException if address is invalid
*/
public static InetAddress parseInetAddress(String address) throws UnknownHostException
{
byte[] bytes = parseInetAddressBytes(address);
if (bytes == null)
{
throw new UnknownHostException();
}
return InetAddress.getByAddress(bytes);
}
}

View File

@@ -16,6 +16,8 @@ import com.wireguard.crypto.Key
import org.json.JSONObject
import java.util.Base64
import com.wireguard.config.*
import net.openvpn.ovpn3.ClientAPI_Config
import net.openvpn.ovpn3.ClientAPI_EvalConfig
import net.openvpn.ovpn3.ClientAPI_Event
@@ -72,6 +74,8 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
val jsonVpnConfig = mService.getVpnConfig()
val ovpnConfig = jsonVpnConfig.getJSONObject("openvpn_config_data").getString("config")
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
val resultingConfig = StringBuilder()
resultingConfig.append(ovpnConfig)
@@ -115,6 +119,7 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
eval_config(config)
val status = connect()
if (status.getError()) {
Log.i(tag, "connect() error: " + status.getError() + ": " + status.getMessage())
}
@@ -139,6 +144,31 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
override fun tun_builder_establish(): Int {
Log.v(tag, "tun_builder_establish")
val jsonVpnConfig = mService.getVpnConfig()
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
if (splitTunnelType == 1) {
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
val ipRange = IPRange(site)
mService.addRoute(ipRange.getFrom().getHostAddress(), ipRange.getPrefix())
}
}
if (splitTunnelType == 2) {
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
ipRangeSet.remove(IPRange("127.0.0.0/8"))
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
ipRangeSet.remove(IPRange(site))
}
ipRangeSet.subnets().forEach {
mService.addRoute(it.getFrom().getHostAddress(), it.getPrefix())
Thread.sleep(10)
}
mService.addRoute("2000::", 3)
}
return mService.establish()!!.detachFd()
}

View File

@@ -564,6 +564,7 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
return parseData
}
/**
* Create a Wireguard [Config] from a [json] string -
* The [json] will be created in AndroidVpnProtocol.cpp
@@ -571,29 +572,67 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
private fun buildWireguardConfig(obj: JSONObject, type: String): Config {
val confBuilder = Config.Builder()
val wireguardConfigData = obj.getJSONObject(type)
val splitTunnelType = obj.getInt("splitTunnelType")
val splitTunnelSites = obj.getJSONArray("splitTunnelSites")
val config = parseConfigData(wireguardConfigData.getString("config"))
val peerBuilder = Peer.Builder()
val peerConfig = config["Peer"]!!
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
peerConfig["PresharedKey"]?.let {
peerBuilder.setPreSharedKey(Key.fromBase64(it))
peerConfig["PresharedKey"]?.let { peerBuilder.setPreSharedKey(Key.fromBase64(it)) }
val allIpString = peerConfig["AllowedIPs"]
var allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
/* default value in template */
if (allIpString == "0.0.0.0/0, ::/0") {
allowedIPList = emptyList()
}
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
if (allowedIPList.isEmpty()) {
val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
peerBuilder.addAllowedIp(internet)
if (allowedIPList.isEmpty() && (splitTunnelType == 0)) {
/* AllowedIP is empty and splitTunnel is turnoff */
/* use VPN for whole Internet */
val internetV4 = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
peerBuilder.addAllowedIp(internetV4)
val internetV6 = InetNetwork.parse("::/0") // aka The whole internet.
peerBuilder.addAllowedIp(internetV6)
} else {
allowedIPList.forEach {
val network = InetNetwork.parse(it.trim())
peerBuilder.addAllowedIp(network)
if (!allowedIPList.isEmpty()) {
/* We have predefined AllowedIP in WG config */
/* It's have higher priority than system SplitTunnel */
allowedIPList.forEach {
val network = InetNetwork.parse(it.trim())
peerBuilder.addAllowedIp(network)
}
} else {
if (splitTunnelType == 1) {
/* Use system SplitTunnel */
/* VPN connection used only for defined IPs */
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
val internet = InetNetwork.parse(site)
peerBuilder.addAllowedIp(internet)
}
}
if (splitTunnelType == 2) {
/* Use system SplitTunnel */
/* VPN connection used for all Internet exclude defined IPs */
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
ipRangeSet.remove(IPRange("127.0.0.0/8"))
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
ipRangeSet.remove(IPRange(site))
}
val allowedIps = ipRangeSet.subnets().joinToString(", ") + ", 2000::/3"
peerBuilder.parseAllowedIPs(allowedIps)
}
}
}
val endpointConfig = peerConfig["Endpoint"]
val endpoint = InetEndpoint.parse(endpointConfig)
peerBuilder.setEndpoint(endpoint)
peerConfig["PersistentKeepalive"]?.let {
peerBuilder.setPersistentKeepalive(it.toInt())
}
peerConfig["PersistentKeepalive"]?.let { peerBuilder.setPersistentKeepalive(it.toInt()) }
confBuilder.addPeer(peerBuilder.build())
val ifaceBuilder = Interface.Builder()
@@ -603,7 +642,7 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
ifaceConfig["DNS"]!!.split(",").forEach {
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
}
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
if (type == "awg_config_data") {
ifaceBuilder.parseJc(ifaceConfig["Jc"])
@@ -624,14 +663,13 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
ifaceBuilder.parseH1("0")
ifaceBuilder.parseH2("0")
ifaceBuilder.parseH3("0")
ifaceBuilder.parseH4("0")
ifaceBuilder.parseH4("0")
}
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
(0 until jExcludedApplication.length()).toList().forEach {
(0 until jExcludedApplication.length()).toList().forEach {
val appName = jExcludedApplication.get(it).toString()
ifaceBuilder.excludeApplication(appName)
}*/
}*/
confBuilder.setInterface(ifaceBuilder.build())
return confBuilder.build()
@@ -746,13 +784,13 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
private fun startWireGuard(type: String) {
val wireguard_conf = buildWireguardConfig(mConfig!!, type + "_config_data")
Log.i(tag, "startWireGuard: wireguard_conf : $wireguard_conf")
if (currentTunnelHandle != -1) {
Log.e(tag, "Tunnel already up")
// Turn the tunnel down because this might be a switch
GoBackend.wgTurnOff(currentTunnelHandle)
}
val wgConfig: String = wireguard_conf.toWgUserspaceString()
val builder = Builder()
setupBuilder(wireguard_conf, builder)
builder.setSession("Amnezia")

View File

@@ -10,50 +10,37 @@ AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, QObject *pa
{
}
QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials,
DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
{
QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, errorCode);
QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, clientId, errorCode);
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
QString awgConfig = jsonConfig.value(config_key::config).toString();
ServerController serverController(m_settings);
QString serverConfig = serverController.getTextFileFromContainer(container, credentials, protocols::awg::serverConfigPath, errorCode);
QMap<QString, QString> serverConfigMap;
auto serverConfigLines = serverConfig.split("\n");
for (auto &line : serverConfigLines) {
QMap<QString, QString> configMap;
auto configLines = awgConfig.split("\n");
for (auto &line : configLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
configMap.insert(parts[0].trimmed(), parts[1].trimmed());
}
}
}
config.replace("$JUNK_PACKET_COUNT", serverConfigMap.value(config_key::junkPacketCount));
config.replace("$JUNK_PACKET_MIN_SIZE", serverConfigMap.value(config_key::junkPacketMinSize));
config.replace("$JUNK_PACKET_MAX_SIZE", serverConfigMap.value(config_key::junkPacketMaxSize));
config.replace("$INIT_PACKET_JUNK_SIZE", serverConfigMap.value(config_key::initPacketJunkSize));
config.replace("$RESPONSE_PACKET_JUNK_SIZE", serverConfigMap.value(config_key::responsePacketJunkSize));
config.replace("$INIT_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::initPacketMagicHeader));
config.replace("$RESPONSE_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::responsePacketMagicHeader));
config.replace("$UNDERLOAD_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::underloadPacketMagicHeader));
config.replace("$TRANSPORT_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::transportPacketMagicHeader));
jsonConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
jsonConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
jsonConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
jsonConfig[config_key::initPacketJunkSize] = serverConfigMap.value(config_key::initPacketJunkSize);
jsonConfig[config_key::responsePacketJunkSize] = serverConfigMap.value(config_key::responsePacketJunkSize);
jsonConfig[config_key::initPacketMagicHeader] = serverConfigMap.value(config_key::initPacketMagicHeader);
jsonConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
jsonConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
jsonConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader);
jsonConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
jsonConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
jsonConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
jsonConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
jsonConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
jsonConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
return QJsonDocument(jsonConfig).toJson();
}

View File

@@ -12,7 +12,7 @@ public:
AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
};
#endif // AWGCONFIGURATOR_H

View File

@@ -83,7 +83,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
}
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
{
ServerController serverController(m_settings);
QString config =
@@ -113,6 +113,8 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
QJsonObject jConfig;
jConfig[config_key::config] = config;
clientId = connData.clientId;
return QJsonDocument(jConfig).toJson();
}
@@ -131,10 +133,13 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
config.append("block-ipv6\n");
}
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway
}
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#ifndef Q_OS_ANDROID
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
#endif
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n");

View File

@@ -24,7 +24,7 @@ public:
};
QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
QString processConfigWithLocalSettings(QString jsonConfig);
QString processConfigWithExportSettings(QString jsonConfig);

View File

@@ -28,11 +28,11 @@ VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *pa
}
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode)
const QJsonObject &containerConfig, Proto proto, QString &clientId, ErrorCode *errorCode)
{
switch (proto) {
case Proto::OpenVpn:
return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, errorCode);
return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, clientId, errorCode);
case Proto::ShadowSocks:
return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode);
@@ -40,10 +40,10 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia
case Proto::Cloak: return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode);
case Proto::WireGuard:
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode);
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, clientId, errorCode);
case Proto::Awg:
return awgConfigurator->genAwgConfig(credentials, container, containerConfig, errorCode);
return awgConfigurator->genAwgConfig(credentials, container, containerConfig, clientId, errorCode);
case Proto::Ikev2: return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode);

View File

@@ -6,7 +6,6 @@
#include "configurator_base.h"
#include "core/defs.h"
class OpenVpnConfigurator;
class ShadowSocksConfigurator;
class CloakConfigurator;
@@ -16,14 +15,15 @@ class SshConfigurator;
class AwgConfigurator;
// Retrieve connection settings from server
class VpnConfigurator : ConfiguratorBase
class VpnConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
explicit VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, Proto proto, QString &clientId,
ErrorCode *errorCode = nullptr);
QPair<QString, QString> getDnsForConfig(int serverIndex);
QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
@@ -32,8 +32,8 @@ public:
QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
// workaround for containers which is not support normal configuration
void updateContainerConfigAfterInstallation(DockerContainer container,
QJsonObject &containerConfig, const QString &stdOut);
void updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig,
const QString &stdOut);
std::shared_ptr<OpenVpnConfigurator> openVpnConfigurator;
std::shared_ptr<ShadowSocksConfigurator> shadowSocksConfigurator;
@@ -42,6 +42,10 @@ public:
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
std::shared_ptr<SshConfigurator> sshConfigurator;
std::shared_ptr<AwgConfigurator> awgConfigurator;
signals:
void newVpnConfigCreated(const QString &clientId, const QString &clientName, const DockerContainer container,
ServerCredentials credentials);
};
#endif // VPN_CONFIGURATOR_H

View File

@@ -177,7 +177,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
}
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
{
ServerController serverController(m_settings);
QString scriptData = amnezia::scriptData(m_configTemplate, container);
@@ -205,6 +205,8 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
jConfig[config_key::psk_key] = connData.pskKey;
jConfig[config_key::server_pub_key] = connData.serverPubKey;
clientId = connData.clientPubKey;
return QJsonDocument(jConfig).toJson();
}

View File

@@ -26,7 +26,7 @@ public:
};
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
QString processConfigWithLocalSettings(QString config);
QString processConfigWithExportSettings(QString config);

View File

@@ -16,11 +16,11 @@ namespace amnezia
Q_NAMESPACE
enum DockerContainer {
None = 0,
OpenVpn,
ShadowSocks,
Cloak,
WireGuard,
Awg,
WireGuard,
OpenVpn,
Cloak,
ShadowSocks,
Ipsec,
// non-vpn

View File

@@ -36,7 +36,7 @@ enum ErrorCode
ServerPacketManagerError,
// Ssh connection errors
SshRequsetDeniedError, SshInterruptedError, SshInternalError,
SshRequestDeniedError, SshInterruptedError, SshInternalError,
SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError,
// Ssh sftp errors
@@ -47,7 +47,6 @@ enum ErrorCode
SshSftpNoMediaError,
// Local errors
FailedToSaveConfigData,
OpenVpnConfigMissing,
OpenVpnManagementServerError,
ConfigMissing,
@@ -67,7 +66,6 @@ enum ErrorCode
// 3rd party utils errors
OpenSslFailed,
OpenVpnExecutableCrashed,
ShadowSocksExecutableCrashed,
CloakExecutableCrashed,

View File

@@ -19,7 +19,7 @@ QString errorString(ErrorCode code){
case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo");
// Libssh errors
case(SshRequsetDeniedError): return QObject::tr("Ssh request was denied");
case(SshRequestDeniedError): return QObject::tr("Ssh request was denied");
case(SshInterruptedError): return QObject::tr("Ssh request was interrupted");
case(SshInternalError): return QObject::tr("Ssh internal error");
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
@@ -42,7 +42,6 @@ QString errorString(ErrorCode code){
case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive");
// Local errors
case (FailedToSaveConfigData): return QObject::tr("Failed to save config to disk");
case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing");
case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error");

View File

@@ -167,11 +167,8 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return ErrorCode::ServerContainerMissingError;
}
runScript(credentials,
replaceVars(QString("sudo shred %1").arg(tmpFileName), genVarsForScript(credentials, container)));
runScript(credentials, replaceVars(QString("sudo rm %1").arg(tmpFileName), genVarsForScript(credentials, container)));
runScript(credentials,
replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
return e;
}
@@ -337,7 +334,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
!= newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
return true;
}
if (container == DockerContainer::Awg) {
return true;
}
@@ -490,8 +487,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
const QJsonObject &amneziaWireguarConfig =
config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
Vars vars;
@@ -591,33 +587,21 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
// Amnezia wireguard vars
vars.append({ { "$AWG_SERVER_PORT",
amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
vars.append({ { "$JUNK_PACKET_COUNT",
amneziaWireguarConfig.value(config_key::junkPacketCount)
.toString(protocols::awg::defaultJunkPacketCount) } });
vars.append({ { "$JUNK_PACKET_MIN_SIZE",
amneziaWireguarConfig.value(config_key::junkPacketMinSize)
.toString(protocols::awg::defaultJunkPacketMinSize) } });
vars.append({ { "$JUNK_PACKET_MAX_SIZE",
amneziaWireguarConfig.value(config_key::junkPacketMaxSize)
.toString(protocols::awg::defaultJunkPacketMaxSize) } });
vars.append({ { "$INIT_PACKET_JUNK_SIZE",
amneziaWireguarConfig.value(config_key::initPacketJunkSize)
.toString(protocols::awg::defaultInitPacketJunkSize) } });
vars.append({ { "$JUNK_PACKET_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
vars.append({ { "$JUNK_PACKET_MIN_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMinSize).toString() } });
vars.append({ { "$JUNK_PACKET_MAX_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMaxSize).toString() } });
vars.append({ { "$INIT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::initPacketJunkSize).toString() } });
vars.append({ { "$RESPONSE_PACKET_JUNK_SIZE",
amneziaWireguarConfig.value(config_key::responsePacketJunkSize)
.toString(protocols::awg::defaultResponsePacketJunkSize) } });
amneziaWireguarConfig.value(config_key::responsePacketJunkSize).toString() } });
vars.append({ { "$INIT_PACKET_MAGIC_HEADER",
amneziaWireguarConfig.value(config_key::initPacketMagicHeader)
.toString(protocols::awg::defaultInitPacketMagicHeader) } });
amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } });
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER",
amneziaWireguarConfig.value(config_key::responsePacketMagicHeader)
.toString(protocols::awg::defaultResponsePacketMagicHeader) } });
amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).toString() } });
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER",
amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader)
.toString(protocols::awg::defaultUnderloadPacketMagicHeader) } });
amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER",
amneziaWireguarConfig.value(config_key::transportPacketMagicHeader)
.toString(protocols::awg::defaultTransportPacketMagicHeader) } });
amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
QString serverIp = Utils::getIPAddress(credentials.hostName);
if (!serverIp.isEmpty()) {
@@ -847,6 +831,34 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
containerConfig.insert(config_key::port, port);
containerConfig.insert(config_key::transport_proto, transportProto);
if (protocol == Proto::Awg) {
QString serverConfig = getTextFileFromContainer(container, credentials, protocols::awg::serverConfigPath, &errorCode);
QMap<QString, QString> serverConfigMap;
auto serverConfigLines = serverConfig.split("\n");
for (auto &line : serverConfigLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
}
}
}
containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
containerConfig[config_key::initPacketJunkSize] = serverConfigMap.value(config_key::initPacketJunkSize);
containerConfig[config_key::responsePacketJunkSize] = serverConfigMap.value(config_key::responsePacketJunkSize);
containerConfig[config_key::initPacketMagicHeader] = serverConfigMap.value(config_key::initPacketMagicHeader);
containerConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
containerConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader);
}
config.insert(config_key::container, ContainerProps::containerToString(container));
}
config.insert(ProtocolProps::protoToString(protocol), containerConfig);

View File

@@ -333,7 +333,7 @@ namespace libssh {
switch (errorCode) {
case(SSH_NO_ERROR): return ErrorCode::NoError;
case(SSH_REQUEST_DENIED): return ErrorCode::SshRequsetDeniedError;
case(SSH_REQUEST_DENIED): return ErrorCode::SshRequestDeniedError;
case(SSH_EINTR): return ErrorCode::SshInterruptedError;
case(SSH_FATAL): return ErrorCode::SshInternalError;
default: return ErrorCode::SshInternalError;

View File

@@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="x, &#195;&#151;, close">
<path id="Vector" d="M18 6L6 18" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M6 6L18 18" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 374 B

View File

@@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="search">
<path id="Vector" d="M11 19C15.4183 19 19 15.4183 19 11C19 6.58172 15.4183 3 11 3C6.58172 3 3 6.58172 3 11C3 15.4183 6.58172 19 11 19Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M21.0004 20.9984L16.6504 16.6484" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -115,8 +115,12 @@ void LocalSocketController::daemonConnected() {
}
void LocalSocketController::activate(const QJsonObject &rawConfig) {
QString protocolName = rawConfig.value("protocol").toString();
int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = rawConfig.value("splitTunnelSites").toArray();
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
QJsonObject json;
@@ -137,23 +141,79 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
QJsonArray jsAllowedIPAddesses;
QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray();
QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(","));
QJsonObject range_ipv6;
range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6);
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
// Use AllowedIP list from WG config bacouse of higer priority
for (auto v : plainAllowedIP) {
QString ipRange = v.toString();
qDebug() << "ipRange " << ipRange;
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
} else {
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
QJsonObject range_ipv6;
range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6);
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
qDebug() << "ipRange " << ipRange;
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
}
}
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
QJsonArray jsExcludedAddresses;
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
jsExcludedAddresses.append(ipRange);
}
}
json.insert("excludedAddresses", jsExcludedAddresses);

View File

@@ -28,6 +28,8 @@ struct MessageKey
static const char *host;
static const char *port;
static const char *isOnDemand;
static const char *SplitTunnelType;
static const char *SplitTunnelSites;
};
class IosController : public QObject

View File

@@ -29,6 +29,9 @@ const char* MessageKey::errorCode = "errorCode";
const char* MessageKey::host = "host";
const char* MessageKey::port = "port";
const char* MessageKey::isOnDemand = "is-on-demand";
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
switch (status) {
@@ -351,6 +354,13 @@ void IosController::startTunnel()
{
m_rxBytes = 0;
m_txBytes = 0;
int STT = m_rawConfig["splitTunnelType"].toInt();
QJsonArray splitTunnelSites = m_rawConfig["splitTunnelSites"].toArray();
QJsonDocument doc;
doc.setArray(splitTunnelSites);
QString STS(doc.toJson());
[m_currentTunnel setEnabled:YES];
[m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) {
@@ -376,8 +386,15 @@ void IosController::startTunnel()
NSString *actionValue = [NSString stringWithUTF8String:Action::start];
NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId];
NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @"";
NSString *SplitTunnelTypeKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelType];
NSString *SplitTunnelTypeValue = [NSString stringWithFormat:@"%d",STT];
NSString *SplitTunnelSitesKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelSites];
NSString *SplitTunnelSitesValue = STS.toNSString();
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue};
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue,
SplitTunnelTypeKey: SplitTunnelTypeValue, SplitTunnelSitesKey: SplitTunnelSitesValue};
sendVpnExtensionMessage(message);

View File

@@ -15,7 +15,7 @@ struct Constants {
static let ovpnConfigKey = "ovpn"
static let wireGuardConfigKey = "wireguard"
static let loggerTag = "NET"
static let kActionStart = "start"
static let kActionRestart = "restart"
static let kActionStop = "stop"
@@ -29,6 +29,8 @@ struct Constants {
static let kMessageKeyHost = "host"
static let kMessageKeyPort = "port"
static let kMessageKeyOnDemand = "is-on-demand"
static let kMessageKeySplitTunnelType = "SplitTunnelType"
static let kMessageKeySplitTunnelSites = "SplitTunnelSites"
}
class PacketTunnelProvider: NEPacketTunnelProvider {
@@ -38,7 +40,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
wg_log(logLevel.osLogLevel, message: message)
}
}()
private lazy var ovpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
@@ -49,9 +51,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
private var openVPNConfig: Data? = nil
private var SplitTunnelType: String? = nil
private var SplitTunnelSites: String? = nil
let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
var protoType: TunnelProtoType = .none
@@ -63,26 +67,34 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
let tmpStr = String(data: messageData, encoding: .utf8)!
wg_log(.error, message: tmpStr)
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
Logger.global?.log(message: "Failed to serialize message from app")
return
}
guard let completionHandler = completionHandler else {
Logger.global?.log(message: "Missing message completion handler")
return
}
guard let action = message[Constants.kMessageKeyAction] as? String else {
Logger.global?.log(message: "Missing action key in app message")
completionHandler(nil)
return
}
if action == Constants.kActionStatus {
handleStatusAppMessage(messageData, completionHandler: completionHandler)
}
if action == Constants.kActionStart {
SplitTunnelType = message[Constants.kMessageKeySplitTunnelType] as? String
SplitTunnelSites = message[Constants.kMessageKeySplitTunnelSites] as? String
}
let callbackWrapper: (NSNumber?) -> Void = { errorCode in
//let tunnelId = self.tunnelConfig?.id ?? ""
let response: [String: Any] = [
@@ -90,11 +102,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
Constants.kMessageKeyErrorCode: errorCode ?? NSNull(),
Constants.kMessageKeyTunnelId: 0
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
}
}
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
dispatchQueue.async {
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
@@ -118,8 +130,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
switch self.protoType {
case .wireguard:
self.startWireguard(activationAttemptId: activationAttemptId,
errorNotifier: errorNotifier,
completionHandler: completionHandler)
errorNotifier: errorNotifier,
completionHandler: completionHandler)
case .openvpn:
self.startOpenVPN(completionHandler: completionHandler)
case .shadowsocks:
@@ -156,7 +168,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
case .shadowsocks:
break
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
case .none:
break
@@ -168,12 +180,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
errorNotifier: ErrorNotifier,
completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
wg_log(.error, message: "Can't start WireGuard config missing")
completionHandler(nil)
return
}
let providerConfiguration = protocolConfiguration.providerConfiguration,
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
wg_log(.error, message: "Can't start WireGuard config missing")
completionHandler(nil)
return
}
let wgConfigStr = String(data: wgConfig, encoding: .utf8)!
@@ -182,7 +195,49 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
if (tunnelConfiguration.peers.first!.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ") == "0.0.0.0/0, ::/0") {
if (SplitTunnelType == "1") {
for index in tunnelConfiguration.peers.indices {
tunnelConfiguration.peers[index].allowedIPs.removeAll()
var allowedIPs = [IPAddressRange]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSArray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for allowedIPString in STSArray {
if let allowedIP = IPAddressRange(from: allowedIPString) {
allowedIPs.append(allowedIP)
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
}
} else {
if (SplitTunnelType == "2")
{
for index in tunnelConfiguration.peers.indices {
var excludeIPs = [IPAddressRange]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for excludeIPString in STSarray {
if let excludeIP = IPAddressRange(from: excludeIPString) {
excludeIPs.append(excludeIP)
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
}
}
}
}
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
// Start the tunnel
@@ -193,30 +248,30 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
switch adapterError {
case .cannotLocateTunnelFileDescriptor:
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
case .dnsResolution(let dnsErrors):
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
.joined(separator: ", ")
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
case .setNetworkSettings(let error):
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
case .startWireGuardBackend(let errorCode):
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
case .invalidState:
// Must never happen
fatalError()
@@ -226,27 +281,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
// TODO: handle errors properly
wg_log(.error, message: "Can't start startOpenVPN()")
wg_log(.error, message: "Can't start startOpenVPN()")
return
}
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
}
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile()
if let error = error {
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
}
completionHandler()
#if os(macOS)
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
// Remove it when they finally fix this upstream and the fix has been rolled out to
@@ -263,7 +318,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
ovpnAdapter.disconnect()
}
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
wgAdapter.getRuntimeConfiguration { settings in
@@ -278,8 +333,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
for component in components{
let pair = component.components(separatedBy: "=")
if pair.count == 2 {
settingsDictionary[pair[0]] = pair[1]
}
settingsDictionary[pair[0]] = pair[1]
}
}
let response: [String: Any] = [
@@ -309,7 +364,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
do {
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
@@ -318,7 +373,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
self.wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings = settings {
@@ -334,76 +389,76 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
}
}
private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
let bytesin = ovpnAdapter.transportStatistics.bytesIn
let bytesout = ovpnAdapter.transportStatistics.bytesOut
let response: [String: Any] = [
"rx_bytes" : bytesin,
"tx_bytes" : bytesout
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
let bytesin = ovpnAdapter.transportStatistics.bytesIn
let bytesout = ovpnAdapter.transportStatistics.bytesOut
let response: [String: Any] = [
"rx_bytes" : bytesin,
"tx_bytes" : bytesout
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
}
// TODO review
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, withShadowSocks viaSS: Bool = false, completionHandler: @escaping (Error?) -> Void) {
wg_log(.info, message: "setupAndlaunchOpenVPN")
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnConfiguration
if(str.contains("cloak")){
configuration.setPTCloak();
}
let evaluation: OpenVPNConfigurationEvaluation
do {
evaluation = try ovpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
if !evaluation.autologin {
wg_log(.info, message: "Implement login with user credentials")
}
vpnReachability.startTracking { [weak self] status in
guard status == .reachableViaWiFi else { return }
self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
}
startHandler = completionHandler
ovpnAdapter.connect(using: packetFlow)
// let ifaces = Interface.allInterfaces()
// .filter { $0.family == .ipv4 }
// .map { iface in iface.name }
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
// let ifaces = Interface.allInterfaces()
// .filter { $0.family == .ipv4 }
// .map { iface in iface.name }
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
}
// MARK: -- Network observing methods
private func startListeningForNetworkChanges() {
stopListeningForNetworkChanges()
addObserver(self, forKeyPath: Constants.kDefaultPathKey, options: .old, context: nil)
}
private func stopListeningForNetworkChanges() {
removeObserver(self, forKeyPath: Constants.kDefaultPathKey)
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard Constants.kDefaultPathKey != keyPath else { return }
// Since iOS 11, we have observed that this KVO event fires repeatedly when connecting over Wifi,
// even though the underlying network has not changed (i.e. `isEqualToPath` returns false),
@@ -412,28 +467,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
guard let lastPath: NWPath = change?[.oldKey] as? NWPath,
let defPath = defaultPath,
lastPath != defPath || lastPath.description != defPath.description else {
return
}
return
}
DispatchQueue.main.async { [weak self] in
guard let `self` = self, self.defaultPath != nil else { return }
self.handle(networkChange: self.defaultPath!) { _ in }
}
}
private func handle(networkChange changePath: NWPath, completion: @escaping (Error?) -> Void) {
wg_log(.info, message: "Tunnel restarted.")
startTunnel(options: nil, completionHandler: completion)
}
private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) {
dispatchPrecondition(condition: .onQueue(dispatchQueue))
let emptyTunnelConfiguration = TunnelConfiguration(
name: nil,
interface: InterfaceConfiguration(privateKey: PrivateKey()),
peers: []
)
wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in
self.dispatchQueue.async {
if let error {
@@ -445,9 +500,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
}
}
let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
self.setTunnelNetworkSettings(settings) { error in
completionHandler(error)
}
@@ -478,6 +533,50 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// send empty string to NEDNSSettings.matchDomains
networkSettings?.dnsSettings?.matchDomains = [""]
if (SplitTunnelType == "1") {
var ipv4IncludedRoutes = [NEIPv4Route]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for allowedIPString in STSarray {
if let allowedIP = IPAddressRange(from: allowedIPString){
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allowedIP.address)", subnetMask: "\(allowedIP.subnetMask())"))
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
} else {
if (SplitTunnelType == "2")
{
var ipv4ExcludedRoutes = [NEIPv4Route]()
var ipv4IncludedRoutes = [NEIPv4Route]()
var ipv6IncludedRoutes = [NEIPv6Route]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for excludeIPString in STSarray {
if let excludeIP = IPAddressRange(from: excludeIPString) {
ipv4ExcludedRoutes.append(NEIPv4Route(destinationAddress: "\(excludeIP.address)", subnetMask: "\(excludeIP.subnetMask())"))
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
if let allIPv4 = IPAddressRange(from: "0.0.0.0/0"){
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allIPv4.address)", subnetMask: "\(allIPv4.subnetMask())"))
}
if let allIPv6 = IPAddressRange(from: "::/0") {
ipv6IncludedRoutes.append(NEIPv6Route(destinationAddress: "\(allIPv6.address)", networkPrefixLength: NSNumber(value: allIPv6.networkPrefixLength)))
}
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes
networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes
}
}
// Set the network settings for the current tunneling session.
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
}
@@ -538,7 +637,7 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
wg_log(.info, message: logMessage)
}
}
extension WireGuardLogLevel {
var osLogLevel: OSLogType {
switch self {

View File

@@ -158,15 +158,15 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type,
return false;
}
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 1);
}
if (rtm->rtm_type == RTN_THROW) {
int index = if_nametoindex(getgatewayandiface().toUtf8());
if (index <= 0) {
logger.error() << "if_nametoindex() failed:" << strerror(errno);
return false;
}
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
struct in_addr ip4;
inet_pton(AF_INET, getgatewayandiface().toUtf8(), &ip4);
nlmsg_append_attr(nlmsg, sizeof(buf), RTA_GATEWAY, &ip4, sizeof(ip4));
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 0);
rtm->rtm_type = RTN_UNICAST;
}
struct sockaddr_nl nladdr;
@@ -334,7 +334,7 @@ QString LinuxRouteMonitor::getgatewayandiface()
}
}
close(sock);
return interface;
return gateway_address;
}
static bool buildAllowedIp(wg_allowedip* ip,

View File

@@ -236,6 +236,17 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
}
}
if (!config.m_excludedAddresses.empty()) {
for (const QString& i : config.m_excludedAddresses) {
logger.debug() << "range: " << i;
if (!allowTrafficToRange(i, HIGH_WEIGHT,
"Allow Ecxlude route", config.m_serverPublicKey)) {
return false;
}
}
}
result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
@@ -411,8 +422,8 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
}
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
int weight, const QString& title,
const QString& peer) {
int weight, const QString& title,
const QString& peer) {
bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol;
GUID layerOut =
isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
@@ -473,6 +484,57 @@ bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
return true;
}
bool WindowsFirewall::allowTrafficToRange(const IPAddress& addr, uint8_t weight,
const QString& title,
const QString& peer) {
QString description("Allow traffic %1 %2 ");
auto lower = addr.address();
auto upper = addr.broadcastAddress();
const bool isV4 = addr.type() == QAbstractSocket::IPv4Protocol;
const GUID layerKeyOut =
isV4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
const GUID layerKeyIn = isV4 ? FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
// Assemble the Filter base
FWPM_FILTER0 filter;
memset(&filter, 0, sizeof(filter));
filter.action.type = FWP_ACTION_PERMIT;
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = weight;
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
FWPM_FILTER_CONDITION0 cond[1] = {0};
FWP_RANGE0 ipRange;
QByteArray lowIpV6Buffer;
QByteArray highIpV6Buffer;
importAddress(lower, ipRange.valueLow, &lowIpV6Buffer);
importAddress(upper, ipRange.valueHigh, &highIpV6Buffer);
cond[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
cond[0].matchType = FWP_MATCH_RANGE;
cond[0].conditionValue.type = FWP_RANGE_TYPE;
cond[0].conditionValue.rangeValue = &ipRange;
filter.numFilterConditions = 1;
filter.filterCondition = cond;
filter.layerKey = layerKeyOut;
if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()),
peer)) {
return false;
}
filter.layerKey = layerKeyIn;
if (!enableFilter(&filter, title,
description.arg("from").arg(addr.toString()), peer)) {
return false;
}
return true;
}
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
// Allow outbound DHCPv4
{

View File

@@ -52,6 +52,9 @@ class WindowsFirewall final : public QObject {
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
const QString& title, const QString& peer = QString());
bool allowTrafficToRange(const IPAddress& addr, uint8_t weight,
const QString& title,
const QString& peer);
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
const QString& title);
bool allowDHCPTraffic(uint8_t weight, const QString& title);

View File

@@ -1,5 +1,7 @@
#include "protocols_defs.h"
#include <QRandomGenerator>
using namespace amnezia;
QDebug operator<<(QDebug debug, const amnezia::ProtocolEnumNS::Proto &p)
@@ -98,15 +100,28 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p)
}
}
int ProtocolProps::getPortForInstall(Proto p)
{
switch (p) {
case Awg:
case WireGuard:
case ShadowSocks:
case OpenVpn:
return QRandomGenerator::global()->bounded(30000, 50000);
default:
return defaultPort(p);
}
}
int ProtocolProps::defaultPort(Proto p)
{
switch (p) {
case Proto::Any: return -1;
case Proto::OpenVpn: return 1194;
case Proto::Cloak: return 443;
case Proto::ShadowSocks: return 6789;
case Proto::WireGuard: return 51820;
case Proto::Awg: return 55424;
case Proto::OpenVpn: return QString(protocols::openvpn::defaultPort).toInt();
case Proto::Cloak: return QString(protocols::cloak::defaultPort).toInt();
case Proto::ShadowSocks: return QString(protocols::shadowsocks::defaultPort).toInt();
case Proto::WireGuard: return QString(protocols::wireguard::defaultPort).toInt();
case Proto::Awg: return QString(protocols::awg::defaultPort).toInt();
case Proto::Ikev2: return -1;
case Proto::L2tp: return -1;

View File

@@ -43,6 +43,7 @@ namespace amnezia
constexpr char server_priv_key[] = "server_priv_key";
constexpr char server_pub_key[] = "server_pub_key";
constexpr char psk_key[] = "psk_key";
constexpr char allowed_ips[] = "allowed_ips";
constexpr char client_ip[] = "client_ip"; // internal ip address
@@ -78,6 +79,9 @@ namespace amnezia
constexpr char sftp[] = "sftp";
constexpr char awg[] = "awg";
constexpr char splitTunnelSites[] = "splitTunnelSites";
constexpr char splitTunnelType[] = "splitTunnelType";
}
namespace protocols
@@ -228,6 +232,8 @@ namespace amnezia
Q_INVOKABLE static ServiceType protocolService(Proto p);
Q_INVOKABLE static int getPortForInstall(Proto p);
Q_INVOKABLE static int defaultPort(Proto p);
Q_INVOKABLE static bool defaultPortChangeable(Proto p);

View File

@@ -16,8 +16,6 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf");
writeWireguardConfiguration(configuration);
// MZ
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
m_impl.reset(new LocalSocketController());
connect(m_impl.get(), &ControllerImpl::connected, this,
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
@@ -26,7 +24,6 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
connect(m_impl.get(), &ControllerImpl::disconnected, this,
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
m_impl->initialize(nullptr, nullptr);
#endif
}
WireguardProtocol::~WireguardProtocol()
@@ -37,68 +34,10 @@ WireguardProtocol::~WireguardProtocol()
void WireguardProtocol::stop()
{
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
stopMzImpl();
return;
#endif
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
qCritical() << "Wireguard executable missing!";
setLastError(ErrorCode::ExecutableMissing);
return;
}
m_wireguardStopProcess = IpcClient::CreatePrivilegedProcess();
if (!m_wireguardStopProcess) {
qCritical() << "IpcProcess replica is not created!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return;
}
m_wireguardStopProcess->waitForSource(1000);
if (!m_wireguardStopProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return;
}
m_wireguardStopProcess->setProgram(PermittedProcess::Wireguard);
m_wireguardStopProcess->setArguments(stopArgs());
qDebug() << stopArgs().join(" ");
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop errorOccurred" << error;
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState;
});
#ifdef Q_OS_LINUX
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardRunning();
if (result.returnValue()) {
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
} else {
qCritical() << "IPC client not initialized";
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
#endif
m_wireguardStopProcess->start();
m_wireguardStopProcess->waitForFinished(10000);
setConnectionState(Vpn::ConnectionState::Disconnected);
}
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
ErrorCode WireguardProtocol::startMzImpl()
{
m_impl->activate(m_rawConfig);
@@ -110,7 +49,6 @@ ErrorCode WireguardProtocol::stopMzImpl()
m_impl->deactivate();
return ErrorCode::NoError;
}
#endif
void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configuration)
{
@@ -124,21 +62,8 @@ void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configura
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
m_configFile.close();
#if 0
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->copyWireguardConfig(m_configFile.fileName());
if (result.returnValue()) {
qCritical() << "Failed to copy wireguard config";
return;
}
} else {
qCritical() << "IPC client not initialized";
return;
}
m_configFileName = "/etc/wireguard/wg99.conf";
#else
m_configFileName = m_configFile.fileName();
#endif
m_isConfigLoaded = true;
@@ -152,15 +77,9 @@ QString WireguardProtocol::configPath() const
return m_configFileName;
}
void WireguardProtocol::updateRouteGateway(QString line)
QString WireguardProtocol::serviceName() const
{
// TODO: fix for macos
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
if (!line.contains("/"))
return;
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
m_routeGateway.replace(" ", "");
qDebug() << "Set VPN route gateway" << m_routeGateway;
return "AmneziaVPN.WireGuard0";
}
ErrorCode WireguardProtocol::start()
@@ -170,112 +89,6 @@ ErrorCode WireguardProtocol::start()
return lastError();
}
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
return startMzImpl();
#endif
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
setLastError(ErrorCode::ExecutableMissing);
return lastError();
}
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardConfigExists(configPath());
if (result.returnValue()) {
setLastError(ErrorCode::ConfigMissing);
return lastError();
}
} else {
qCritical() << "IPC client not initialized";
setLastError(ErrorCode::InternalError);
return lastError();
}
setConnectionState(Vpn::ConnectionState::Connecting);
m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess();
if (!m_wireguardStartProcess) {
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_wireguardStartProcess->waitForSource(1000);
if (!m_wireguardStartProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_wireguardStartProcess->setProgram(PermittedProcess::Wireguard);
m_wireguardStartProcess->setArguments(startArgs());
qDebug() << startArgs().join(" ");
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error;
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol stateChanged" << newState;
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::finished, this,
[this]() { setConnectionState(Vpn::ConnectionState::Connected); });
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyRead, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAll();
reply.waitForFinished(1000);
qDebug() << "WireguardProtocol::WireguardProtocol readyRead" << reply.returnValue();
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardOutput, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardOutput();
reply.waitForFinished(1000);
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardOutput" << reply.returnValue();
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardError, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardError();
reply.waitForFinished(10);
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardError" << reply.returnValue();
});
m_wireguardStartProcess->start();
m_wireguardStartProcess->waitForFinished(10000);
return ErrorCode::NoError;
}
void WireguardProtocol::updateVpnGateway(const QString &line)
{
}
QString WireguardProtocol::serviceName() const
{
return "AmneziaVPN.WireGuard0";
}
QStringList WireguardProtocol::stopArgs()
{
#ifdef Q_OS_WIN
return { "--remove", configPath() };
#elif defined Q_OS_LINUX
return { "down", "wg99" };
#else
return {};
#endif
}
QStringList WireguardProtocol::startArgs()
{
#ifdef Q_OS_WIN
return { "--add", configPath() };
#elif defined Q_OS_LINUX
return { "up", "wg99" };
#else
return {};
#endif
}

View File

@@ -8,7 +8,6 @@
#include <QTimer>
#include "vpnprotocol.h"
#include "core/ipcclient.h"
#include "mozilla/controllerimpl.h"
@@ -23,33 +22,21 @@ public:
ErrorCode start() override;
void stop() override;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
ErrorCode startMzImpl();
ErrorCode stopMzImpl();
#endif
private:
QString configPath() const;
void writeWireguardConfiguration(const QJsonObject &configuration);
void updateRouteGateway(QString line);
void updateVpnGateway(const QString &line);
QString serviceName() const;
QStringList stopArgs();
QStringList startArgs();
private:
QString m_configFileName;
QFile m_configFile;
QSharedPointer<PrivilegedProcess> m_wireguardStartProcess;
QSharedPointer<PrivilegedProcess> m_wireguardStopProcess;
bool m_isConfigLoaded = false;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
QScopedPointer<ControllerImpl> m_impl;
#endif
};
#endif // WIREGUARDPROTOCOL_H

View File

@@ -222,5 +222,8 @@
<file>server_scripts/awg/configure_container.sh</file>
<file>server_scripts/awg/run_container.sh</file>
<file>server_scripts/awg/Dockerfile</file>
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
<file>images/controls/close.svg</file>
<file>images/controls/search.svg</file>
</qresource>
</RCC>

View File

@@ -1,5 +1,6 @@
if which apt-get > /dev/null 2>&1; then LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1; then LOCK_FILE="/var/run/dnf.pid";\
elif which yum > /dev/null 2>&1; then LOCK_FILE="/var/run/yum.pid";\
elif which pacman > /dev/null 2>&1; then LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi
if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi

View File

@@ -1,19 +1,20 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); docker_pkg="docker"; dist="centos";\
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="--noconfirm -S"; check_pkgs="> /dev/null 2>&1"; docker_pkg="docker"; dist="archlinux";\
else echo "Packet manager not found"; exit 1; fi;\
echo "Dist: $dist, Packet manager: $pm, Docker pkg: $docker_pkg";\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
if ! command -v sudo > /dev/null 2>&1; then $pm update -yq; $pm install -yq sudo; fi;\
if ! command -v fuser > /dev/null 2>&1; then sudo $pm install -yq psmisc; fi;\
if ! command -v lsof > /dev/null 2>&1; then sudo $pm install -yq lsof; fi;\
if ! command -v docker > /dev/null 2>&1; then sudo $pm update -yq; sudo $pm install -yq $docker_pkg;\
if [ "$dist" = "fedora" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\
if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\
if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\
if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\
if ! command -v docker > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
if [ "$dist" = "fedora" ] || [ "$dist" = "centos" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\
fi;\
if [ "$dist" = "debian" ]; then \
docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\
if [ -z "$docker_service" ]; then sudo $pm update -yq; sudo $pm install -yq curl $docker_pkg; fi;\
if [ -z "$docker_service" ]; then sudo $pm $check_pkgs; sudo $pm $silent_inst curl $docker_pkg; fi;\
sleep 3 && sudo systemctl start docker && sleep 3;\
fi;\
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker";exit 1;fi;\
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker"; exit 1; fi;\
docker --version

View File

@@ -233,10 +233,6 @@ QString Settings::routeModeString(RouteMode mode) const
Settings::RouteMode Settings::routeMode() const
{
// TODO implement for mobiles
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
return RouteMode::VpnAllSites;
#endif
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
}

View File

@@ -4,9 +4,13 @@
<context>
<name>AmneziaApplication</name>
<message>
<location filename="../amnezia_application.cpp" line="302"/>
<source>Split tunneling for WireGuard is not implemented, the option was disabled</source>
<translation>Раздельное туннелирование для &quot;Wireguard&quot; не реализовано,опция отключена</translation>
<translation type="vanished">Раздельное туннелирование для &quot;Wireguard&quot; не реализовано,опция отключена</translation>
</message>
<message>
<location filename="../amnezia_application.cpp" line="305"/>
<source>Split tunneling for %1 is not implemented, the option was disabled</source>
<translation>Раздельное туннелирование для %1 не реализовано, опция отключена</translation>
</message>
</context>
<context>
@@ -119,19 +123,23 @@
<name>HomeContainersListView</name>
<message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="58"/>
<source>Unable change protocol while there is an active connection</source>
<translation>Невозможно изменить протокол при активном соединении</translation>
</message>
<message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/>
<source>The selected protocol is not supported on the current platform</source>
<translation>Выбранный протокол не поддерживается на данном устройстве</translation>
</message>
<message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="76"/>
<source>Reconnect via VPN Procotol: </source>
<translation>Переподключение через VPN протокол: </translation>
<translation type="vanished">Переподключение через VPN протокол: </translation>
</message>
</context>
<context>
<name>ImportController</name>
<message>
<location filename="../ui/controllers/importController.cpp" line="429"/>
<location filename="../ui/controllers/importController.cpp" line="427"/>
<source>Scanned %1 of %2.</source>
<translation>Отсканировано %1 из%2.</translation>
</message>
@@ -139,58 +147,58 @@
<context>
<name>InstallController</name>
<message>
<location filename="../ui/controllers/installController.cpp" line="110"/>
<location filename="../ui/controllers/installController.cpp" line="161"/>
<location filename="../ui/controllers/installController.cpp" line="143"/>
<location filename="../ui/controllers/installController.cpp" line="193"/>
<source>%1 installed successfully. </source>
<translation>%1 успешно установлен. </translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="112"/>
<location filename="../ui/controllers/installController.cpp" line="163"/>
<location filename="../ui/controllers/installController.cpp" line="145"/>
<location filename="../ui/controllers/installController.cpp" line="195"/>
<source>%1 is already installed on the server. </source>
<translation>%1 уже установлен на сервер. </translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="115"/>
<location filename="../ui/controllers/installController.cpp" line="148"/>
<source>
Added containers that were already installed on the server</source>
<translation>
В приложение добавлены обнаруженные на сервере протоклы и сервисы</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="182"/>
<location filename="../ui/controllers/installController.cpp" line="214"/>
<source>
Already installed containers were found on the server. All installed containers have been added to the application</source>
<translation>
На сервере обнаружены установленные протоколы и сервисы, все они добавлены в приложение</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="266"/>
<location filename="../ui/controllers/installController.cpp" line="295"/>
<source>Settings updated successfully</source>
<translation>Настройки успешно обновлены</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="281"/>
<location filename="../ui/controllers/installController.cpp" line="310"/>
<source>Server &apos;%1&apos; was removed</source>
<translation>Сервер &apos;%1&apos; был удален</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="291"/>
<location filename="../ui/controllers/installController.cpp" line="320"/>
<source>All containers from server &apos;%1&apos; have been removed</source>
<translation>Все протоклы и сервисы были удалены с сервера &apos;%1&apos;</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="308"/>
<location filename="../ui/controllers/installController.cpp" line="337"/>
<source>%1 has been removed from the server &apos;%2&apos;</source>
<translation>%1 был удален с сервера &apos;%2&apos;</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="454"/>
<location filename="../ui/controllers/installController.cpp" line="483"/>
<source>Please login as the user</source>
<translation>Пожалуйста, войдите в систему от имени пользователя</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="481"/>
<location filename="../ui/controllers/installController.cpp" line="511"/>
<source>Server added successfully</source>
<translation>Сервер успешно добавлен</translation>
</message>
@@ -258,15 +266,20 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageHome</name>
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="344"/>
<location filename="../ui/qml/Pages2/PageHome.qml" line="354"/>
<source>VPN protocol</source>
<translation>VPN протокол</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="388"/>
<location filename="../ui/qml/Pages2/PageHome.qml" line="398"/>
<source>Servers</source>
<translation>Серверы</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="490"/>
<source>Unable change server while there is an active connection</source>
<translation>Невозможно изменить сервер при активном соединении</translation>
</message>
</context>
<context>
<name>PageProtocolAwgSettings</name>
@@ -337,9 +350,13 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="279"/>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="280"/>
<source>Continue</source>
@@ -388,195 +405,199 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageProtocolOpenVpnSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="76"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="77"/>
<source>OpenVPN settings</source>
<translation>Настройки OpenVPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="83"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="84"/>
<source>VPN Addresses Subnet</source>
<translation>VPN Адреса Подсеть</translation>
<translation>Подсеть для VPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="97"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="98"/>
<source>Network protocol</source>
<translation>Сетевой протокол</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="126"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="127"/>
<source>Port</source>
<translation>Порт</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="145"/>
<source>Auto-negotiate encryption</source>
<translation>Шифрование с автоматическим согласованием</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="162"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="163"/>
<source>Hash</source>
<translation>Хэш</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="170"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="171"/>
<source>SHA512</source>
<translation>SHA512</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="171"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="172"/>
<source>SHA384</source>
<translation>SHA384</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="172"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="173"/>
<source>SHA256</source>
<translation>SHA256</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="173"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="174"/>
<source>SHA3-512</source>
<translation>SHA3-512</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="175"/>
<source>SHA3-384</source>
<translation>SHA3-384</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="175"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="176"/>
<source>SHA3-256</source>
<translation>SHA3-256</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="176"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="177"/>
<source>whirlpool</source>
<translation>whirlpool</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="177"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="178"/>
<source>BLAKE2b512</source>
<translation>BLAKE2b512</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="178"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="179"/>
<source>BLAKE2s256</source>
<translation>BLAKE2s256</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="180"/>
<source>SHA1</source>
<translation>SHA1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="207"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="208"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="209"/>
<source>Cipher</source>
<translation>Шифрование</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="216"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="217"/>
<source>AES-256-GCM</source>
<translation>AES-256-GCM</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="217"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="218"/>
<source>AES-192-GCM</source>
<translation>AES-192-GCM</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="218"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="219"/>
<source>AES-128-GCM</source>
<translation>AES-128-GCM</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="219"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="220"/>
<source>AES-256-CBC</source>
<translation>AES-256-CBC</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="221"/>
<source>AES-192-CBC</source>
<translation>AES-192-CBC</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="221"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="222"/>
<source>AES-128-CBC</source>
<translation>AES-128-CBC</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="222"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="223"/>
<source>ChaCha20-Poly1305</source>
<translation>ChaCha20-Poly1305</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="223"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="224"/>
<source>ARIA-256-CBC</source>
<translation>ARIA-256-CBC</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="224"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="225"/>
<source>CAMELLIA-256-CBC</source>
<translation>CAMELLIA-256-CBC</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="225"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="226"/>
<source>none</source>
<translation>none</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="258"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="261"/>
<source>TLS auth</source>
<translation>TLS авторизация</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="273"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="276"/>
<source>Block DNS requests outside of VPN</source>
<translation>Блокировать DNS запросы за пределами VPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="292"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="295"/>
<source>Additional client configuration commands</source>
<translation>Дополнительные команды конфигурации клиента</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="308"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="340"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="311"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="343"/>
<source>Commands:</source>
<translation>Commands:</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="324"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="327"/>
<source>Additional server configuration commands</source>
<translation>Дополнительные команды конфигурации сервера</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="359"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="364"/>
<source>Remove OpenVPN</source>
<translation>Удалить OpenVPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="362"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="367"/>
<source>Remove OpenVpn from server?</source>
<translation>Удалить OpenVpn с сервера?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="363"/>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="368"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="364"/>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="369"/>
<source>Continue</source>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="365"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="370"/>
<source>Cancel</source>
<translation>Отменить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="384"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="389"/>
<source>Save and Restart Amnezia</source>
<translation>Сохранить и перезагрузить</translation>
</message>
@@ -599,27 +620,31 @@ Already installed containers were found on the server. All installed containers
<translation>Параметры подключения %1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="172"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="174"/>
<source>Remove </source>
<translation>Удалить </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="176"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="178"/>
<source>Remove %1 from server?</source>
<translation>Удалить %1 с сервера?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="177"/>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="178"/>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="obsolete">Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="180"/>
<source>Continue</source>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="181"/>
<source>Cancel</source>
<translation>Отменить</translation>
</message>
@@ -661,7 +686,7 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageServiceDnsSettings.qml" line="52"/>
<source>A DNS service is installed on your server, and it is only accessible via VPN.
</source>
<translation>На вашем сервере устанавливается DNS-сервис, доступ к нему возможен только через VPN.
<translation>На вашем сервере установлен DNS-сервис, доступ к нему возможен только через VPN.
</translation>
</message>
<message>
@@ -765,7 +790,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageServiceSftpSettings.qml" line="251"/>
<source>Remove SFTP and all data stored there?</source>
<translation>Удалить SFTP-хранилище и все хранящиеся на нем данные?</translation>
<translation>Удалить SFTP-хранилище и все хранящиеся на нем данные?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSftpSettings.qml" line="252"/>
@@ -985,8 +1010,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="73"/>
<source>Launch the application every time %1 starts</source>
<translation>Запускать приложение при каждом включении %1</translation>
<source>Launch the application every time the device is starts</source>
<translation>Запускать приложение при каждом включении</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="93"/>
@@ -1085,7 +1110,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="106"/>
<source>Backup file saved</source>
<translation type="unfinished"></translation>
<translation>Бэкап файл сохранен</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="122"/>
@@ -1123,7 +1148,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="41"/>
<source>Connection</source>
<translation>Подключение</translation>
<translation>Соединение</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="50"/>
@@ -1184,52 +1209,52 @@ Already installed containers were found on the server. All installed containers
<translation>DNS сервер</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="49"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="50"/>
<source>If AmneziaDNS is not used or installed</source>
<translation>Эти адреса будут использоваться, если не включен или не установлен AmneziaDNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="56"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="57"/>
<source>Primary DNS</source>
<translation>Первичный DNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="68"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="69"/>
<source>Secondary DNS</source>
<translation>Вторичный DNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="86"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="87"/>
<source>Restore default</source>
<translation>Восстановить по умолчанию</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="89"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="90"/>
<source>Restore default DNS settings?</source>
<translation>Восстановить настройки DNS по умолчанию?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="90"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="91"/>
<source>Continue</source>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="91"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="92"/>
<source>Cancel</source>
<translation>Отменить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="99"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="100"/>
<source>Settings have been reset</source>
<translation>Настройки сброшены</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="111"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="112"/>
<source>Save</source>
<translation>Сохранить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="120"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="121"/>
<source>Settings saved</source>
<translation>Сохранить настройки</translation>
</message>
@@ -1244,7 +1269,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="54"/>
<source>Save logs</source>
<translation>Сохранить логи</translation>
<translation>Сохранять логи</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="86"/>
@@ -1264,12 +1289,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="118"/>
<source>Logs file saved</source>
<translation type="unfinished"></translation>
<translation>Файл с логами сохранен</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="127"/>
<source>Save logs to file</source>
<translation>Сохранять логи в файл</translation>
<translation>Сохранить логи в файл</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="145"/>
@@ -1307,7 +1332,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="87"/>
<source>Clear Amnezia cache</source>
<translation>Очистить кэш Amnezia на сервере</translation>
<translation>Очистить кэш Amnezia</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="88"/>
@@ -1431,8 +1456,12 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="117"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation>Все пользователи, которым вы поделились VPN, больше не смогут к нему подключаться.</translation>
<translation type="vanished">Все пользователи, которым вы поделились VPN, больше не смогут к нему подключаться.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="118"/>
@@ -1471,75 +1500,75 @@ Already installed containers were found on the server. All installed containers
<translation>Раздельное VPN-туннелирование</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="128"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="121"/>
<source>Mode</source>
<translation>Режим</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="206"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="199"/>
<source>Remove </source>
<translation>Удалить </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="207"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="200"/>
<source>Continue</source>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="208"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="201"/>
<source>Cancel</source>
<translation>Отменить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="255"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="248"/>
<source>Site or IP</source>
<translation>Сайт или IP</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="299"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="292"/>
<source>Import/Export Sites</source>
<translation>Импорт/экспорт Сайтов</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="305"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="298"/>
<source>Import</source>
<translation>Импорт</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="310"/>
<source>Save site list</source>
<translation>Сохранить список сайтов</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="324"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/>
<source>Save sites</source>
<translation>Сохранить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="325"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="392"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="407"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="318"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="385"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="400"/>
<source>Sites files (*.json)</source>
<translation>Sites files (*.json)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="382"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="375"/>
<source>Import a list of sites</source>
<translation>Импортировать список с сайтами</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="388"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="381"/>
<source>Replace site list</source>
<translation>Заменить список сайтов</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="391"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="406"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="384"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="399"/>
<source>Open sites file</source>
<translation>Открыть список с сайтами</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="403"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="396"/>
<source>Add imported sites to existing ones</source>
<translation>Добавить импортированные сайты к существующим</translation>
</message>
@@ -1685,7 +1714,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>PageSetupWizardInstalling</name>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="57"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="65"/>
<source>The server has already been added to the application</source>
<translation>Сервер уже был добавлен в приложение</translation>
</message>
@@ -1698,28 +1727,28 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="vanished">занят установкой других протоколов или сервисов. Установка Amnesia </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="62"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="70"/>
<source>Amnezia has detected that your server is currently </source>
<translation type="unfinished"></translation>
<translation>Amnezia обнаружила, что ваш сервер в настоящее время </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="71"/>
<source>busy installing other software. Amnezia installation </source>
<translation type="unfinished"></translation>
<translation>занят установкой другого программного обеспечения. Установка Amnezia </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="64"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="72"/>
<source>will pause until the server finishes installing other software</source>
<translation>будет приостановлена до тех пор, пока сервер не завершит установку</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="121"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="129"/>
<source>Installing</source>
<translation>Установка</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="21"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="67"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="75"/>
<source>Usually it takes no more than 5 minutes</source>
<translation>Обычно это занимает не более 5 минут</translation>
</message>
@@ -1837,27 +1866,27 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>PageSetupWizardViewConfig</name>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="59"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="63"/>
<source>New connection</source>
<translation>Новое соединение</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="86"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="90"/>
<source>Do not use connection code from public sources. It could be created to intercept your data.</source>
<translation>Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватывать ваши данные.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="101"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="105"/>
<source>Collapse content</source>
<translation>Свернуть</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="101"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="105"/>
<source>Show content</source>
<translation>Показать содержимое ключа</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="148"/>
<source>Connect</source>
<translation>Подключиться</translation>
</message>
@@ -1875,9 +1904,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>WireGuard нативный формат</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
<source>VPN Access</source>
<translation>VPN-Доступ</translation>
<translation type="vanished">VPN-Доступ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="146"/>
@@ -1885,14 +1913,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>Соединение</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>VPN access without the ability to manage the server</source>
<translation>Доступ к VPN, без возможности управления сервером</translation>
<translation type="vanished">Доступ к VPN, без возможности управления сервером</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.</source>
<translation>Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки.</translation>
<translation type="vanished">Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="190"/>
@@ -1935,11 +1961,26 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<source>For the AmneziaVPN app</source>
<translation>Для AmneziaVPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
<source>Share VPN Access</source>
<translation>Поделиться VPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="158"/>
<source>Full access</source>
<translation>Полный доступ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>Share VPN access without the ability to manage the server</source>
<translation>Поделиться доступом к VPN, без возможности управления сервером</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings.</source>
<translation>Поделиться доступом к управлению сервером. Пользователь, с которым вы делитесь полным доступом к серверу, сможет добавлять и удалять любые протоколы и службы на сервере, а также изменять настройки.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="251"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="252"/>
@@ -2424,7 +2465,15 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
* Minimal configuration
* Recognised by DPI analysis systems
* Works over UDP network protocol, ports 500 and 4500.</source>
<translation type="unfinished"></translation>
<translation>IKEv2 в сочетании с уровнем шифрования IPSec это современный и стабильный протокол VPN.
Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах.
Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке.
* Доступно в AmneziaVPN только для Windows.
* Низкое энергопотребление, на мобильных устройствах
* Минимальная конфигурация
* Распознается системами DPI-анализа
* Работает по сетевому протоколу UDP, порты 500 и 4500.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="215"/>
@@ -2470,7 +2519,7 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<message>
<location filename="../containers/containers_defs.cpp" line="111"/>
<source>AmneziaWG - Special protocol from Amnezia, based on WireGuard. It&apos;s fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship.</source>
<translation type="unfinished"></translation>
<translation>AmneziaWG - Специальный протокол от Amnezia, основанный на протоколе WireGuard. Он такой же быстрый, как WireGuard, но очень устойчив к блокировкам. Рекомендуется для регионов с высоким уровнем цензуры.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="115"/>
@@ -2502,7 +2551,14 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Flexible customisation to suit user needs to work with different operating systems and devices
* Recognised by DPI analysis systems and therefore susceptible to blocking
* Can operate over both TCP and UDP network protocols.</source>
<translation type="unfinished"></translation>
<translation>OpenVPN однин из самых популярных и проверенных временем VPN-протоколов.
В нем используется уникальный протокол безопасности, опирающийся на протокол SSL/TLS для шифрования и обмена ключами. Кроме того, поддержка OpenVPN множества методов аутентификации делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Благодаря оптимальному соотношению производительности, безопасности и совместимости OpenVPN остается лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности.
* Доступность AmneziaVPN для всех платформ
* Нормальное энергопотребление на мобильных устройствах
* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами
* Распознается системами DPI-анализа и поэтому подвержен блокировке
* Может работать по сетевым протоколам TCP и UDP.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="145"/>
@@ -2514,7 +2570,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Configurable encryption protocol
* Detectable by some DPI systems
* Works over TCP network protocol.</source>
<translation type="unfinished"></translation>
<translation>Shadowsocks, создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению. Однако некоторые системы анализа трафика все же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG, или OpenVPN over Cloak.
* Доступен в AmneziaVPN только на ПК ноутбуках.
* Настраиваемый протокол шифрования
* Обнаруживается некоторыми DPI-системами
* Работает по сетевому протоколу TCP.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="155"/>
@@ -2536,7 +2597,24 @@ If there is a extreme level of Internet censorship in your region, we advise you
* Not recognised by DPI analysis systems
* Works over TCP network protocol, 443 port.
</source>
<translation type="unfinished"></translation>
<translation>OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от блокировок.
OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером.
Cloak защищает OpenVPN от обнаружения и блокировок.
Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению
Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем.
Если в вашем регионе существует экстремальный уровень цензуры в Интернете, мы советуем вам при первом подключении использовать только OpenVPN через Cloak
* Доступность AmneziaVPN на всех платформах
* Высокое энергопотребление на мобильных устройствах
* Гибкие настройки
* Не распознается системами DPI-анализа
* Работает по сетевому протоколу TCP, 443 порт.
</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="174"/>
@@ -2549,7 +2627,15 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures.
* Minimum number of settings
* Easily recognised by DPI analysis systems, susceptible to blocking
* Works over UDP network protocol.</source>
<translation type="unfinished"></translation>
<translation>WireGuard - относительно новый популярный VPN-протокол с упрощенной архитектурой.
Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных.
WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга.
* Доступность AmneziaVPN для всех платформ
* Низкое энергопотребление
* Минимальное количество настроек
* Легко распознается системами DPI-анализа, подвержен блокировке
* Работает по сетевому протоколу UDP.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="187"/>
@@ -2562,7 +2648,15 @@ This means that AmneziaWG keeps the fast performance of the original while addin
* Minimum number of settings
* Not recognised by DPI analysis systems, resistant to blocking
* Works over UDP network protocol.</source>
<translation type="unfinished"></translation>
<translation>AmneziaWG - усовершенствованная версия популярного VPN-протокола Wireguard. AmneziaWG опирается на фундамент, заложенный WireGuard, сохраняя упрощенную архитектуру и высокопроизводительные возможности работы на разных устройствах.
Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком.
Таким образом, AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение.
* Доступность AmneziaVPN на всех платформах
* Низкое энергопотребление
* Минимальное количество настроек
* Не распознается системами DPI-анализа, устойчив к блокировке
* Работает по сетевому протоколу UDP.</translation>
</message>
<message>
<source>AmneziaWG container</source>
@@ -2574,7 +2668,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<translation>Сервис обмена файлами Sftp - безопасный FTP-сервис</translation>
</message>
<message>
<location filename="../protocols/protocols_defs.cpp" line="75"/>
<location filename="../protocols/protocols_defs.cpp" line="77"/>
<source>Sftp service</source>
<translation>Сервис SFTP</translation>
</message>
@@ -2638,6 +2732,16 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<source>error 0x%1: %2</source>
<translation>error 0x%1: %2</translation>
</message>
<message>
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="39"/>
<source>WireGuard Configuration Highlighter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="82"/>
<source>&amp;Randomize colors</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SelectLanguageDrawer</name>
@@ -2863,7 +2967,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message>
<location filename="../containers/containers_defs.cpp" line="309"/>
<source>Medium or High</source>
<translation>Спедний или Высокий</translation>
<translation>Средний или Высокий</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="310"/>
@@ -2911,7 +3015,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message>
<location filename="../ui/qml/main2.qml" line="172"/>
<source>Private key passphrase</source>
<translation>Кодовая фраза для закрытого ключа</translation>
<translation>Кодовая фраза для закрытого ключа</translation>
</message>
<message>
<location filename="../ui/qml/main2.qml" line="191"/>

View File

@@ -4,9 +4,13 @@
<context>
<name>AmneziaApplication</name>
<message>
<location filename="../amnezia_application.cpp" line="302"/>
<source>Split tunneling for WireGuard is not implemented, the option was disabled</source>
<translation>WireGuard协议的VPN分离</translation>
<translation type="vanished">WireGuard协议的VPN分离</translation>
</message>
<message>
<location filename="../amnezia_application.cpp" line="305"/>
<source>Split tunneling for %1 is not implemented, the option was disabled</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -74,17 +78,17 @@
<message>
<location filename="../ui/qml/Components/ConnectionTypeSelectionDrawer.qml" line="30"/>
<source>Add new connection</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Components/ConnectionTypeSelectionDrawer.qml" line="38"/>
<source>Configure your server</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Components/ConnectionTypeSelectionDrawer.qml" line="52"/>
<source>Open config file, key or QR code</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Server IP, login and password</source>
@@ -130,19 +134,23 @@
<name>HomeContainersListView</name>
<message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="58"/>
<source>Unable change protocol while there is an active connection</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/>
<source>The selected protocol is not supported on the current platform</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="76"/>
<source>Reconnect via VPN Procotol: </source>
<translation>VPN基于协议: </translation>
<translation type="vanished">VPN基于协议: </translation>
</message>
</context>
<context>
<name>ImportController</name>
<message>
<location filename="../ui/controllers/importController.cpp" line="429"/>
<location filename="../ui/controllers/importController.cpp" line="427"/>
<source>Scanned %1 of %2.</source>
<translation> %1 of %2.</translation>
</message>
@@ -158,47 +166,47 @@
<translation type="obsolete"> </translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="110"/>
<location filename="../ui/controllers/installController.cpp" line="161"/>
<location filename="../ui/controllers/installController.cpp" line="143"/>
<location filename="../ui/controllers/installController.cpp" line="193"/>
<source>%1 installed successfully. </source>
<translation>%1 </translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="112"/>
<location filename="../ui/controllers/installController.cpp" line="163"/>
<location filename="../ui/controllers/installController.cpp" line="145"/>
<location filename="../ui/controllers/installController.cpp" line="195"/>
<source>%1 is already installed on the server. </source>
<translation> %1</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="115"/>
<location filename="../ui/controllers/installController.cpp" line="148"/>
<source>
Added containers that were already installed on the server</source>
<translation></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="182"/>
<location filename="../ui/controllers/installController.cpp" line="214"/>
<source>
Already installed containers were found on the server. All installed containers have been added to the application</source>
<translation>
</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="266"/>
<location filename="../ui/controllers/installController.cpp" line="295"/>
<source>Settings updated successfully</source>
<translation></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="281"/>
<location filename="../ui/controllers/installController.cpp" line="310"/>
<source>Server &apos;%1&apos; was removed</source>
<translation> &apos;%1&apos;</translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="291"/>
<location filename="../ui/controllers/installController.cpp" line="320"/>
<source>All containers from server &apos;%1&apos; have been removed</source>
<translation> &apos;%1&apos; </translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="308"/>
<location filename="../ui/controllers/installController.cpp" line="337"/>
<source>%1 has been removed from the server &apos;%2&apos;</source>
<translation>%1 &apos;%2&apos; </translation>
</message>
@@ -219,12 +227,12 @@ Already installed containers were found on the server. All installed containers
<translation type="obsolete"> </translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="454"/>
<location filename="../ui/controllers/installController.cpp" line="483"/>
<source>Please login as the user</source>
<translation></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="481"/>
<location filename="../ui/controllers/installController.cpp" line="511"/>
<source>Server added successfully</source>
<translation></translation>
</message>
@@ -292,15 +300,20 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageHome</name>
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="344"/>
<location filename="../ui/qml/Pages2/PageHome.qml" line="354"/>
<source>VPN protocol</source>
<translation>VPN协议</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="388"/>
<location filename="../ui/qml/Pages2/PageHome.qml" line="398"/>
<source>Servers</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="490"/>
<source>Unable change server while there is an active connection</source>
<translation></translation>
</message>
</context>
<context>
<name>PageProtocolAwgSettings</name>
@@ -371,8 +384,12 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="279"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation>使</translation>
<translation type="vanished">使</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="280"/>
@@ -422,199 +439,203 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageProtocolOpenVpnSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="76"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="77"/>
<source>OpenVPN settings</source>
<translation>OpenVPN </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="83"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="84"/>
<source>VPN Addresses Subnet</source>
<translation>VPN子网掩码</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="97"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="98"/>
<source>Network protocol</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="126"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="127"/>
<source>Port</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="145"/>
<source>Auto-negotiate encryption</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="162"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="163"/>
<source>Hash</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="170"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="171"/>
<source>SHA512</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="171"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="172"/>
<source>SHA384</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="172"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="173"/>
<source>SHA256</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="173"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="174"/>
<source>SHA3-512</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="175"/>
<source>SHA3-384</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="175"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="176"/>
<source>SHA3-256</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="176"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="177"/>
<source>whirlpool</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="177"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="178"/>
<source>BLAKE2b512</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="178"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="179"/>
<source>BLAKE2s256</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="180"/>
<source>SHA1</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="207"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="208"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="209"/>
<source>Cipher</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="216"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="217"/>
<source>AES-256-GCM</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="217"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="218"/>
<source>AES-192-GCM</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="218"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="219"/>
<source>AES-128-GCM</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="219"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="220"/>
<source>AES-256-CBC</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="221"/>
<source>AES-192-CBC</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="221"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="222"/>
<source>AES-128-CBC</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="222"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="223"/>
<source>ChaCha20-Poly1305</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="223"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="224"/>
<source>ARIA-256-CBC</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="224"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="225"/>
<source>CAMELLIA-256-CBC</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="225"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="226"/>
<source>none</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="258"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="261"/>
<source>TLS auth</source>
<translation>TLS认证</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="273"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="276"/>
<source>Block DNS requests outside of VPN</source>
<translation>VPN外的DNS请求</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="292"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="295"/>
<source>Additional client configuration commands</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="308"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="340"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="311"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="343"/>
<source>Commands:</source>
<translation>:</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="324"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="327"/>
<source>Additional server configuration commands</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="359"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="364"/>
<source>Remove OpenVPN</source>
<translation>OpenVPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="362"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="367"/>
<source>Remove OpenVpn from server?</source>
<translation>OpenVPN吗?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="363"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="368"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation>使</translation>
<translation type="vanished">使</translation>
</message>
<message>
<source>All users with whom you shared a connection will no longer be able to connect to it</source>
<translation type="obsolete"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="364"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="369"/>
<source>Continue</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="365"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="370"/>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="384"/>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="389"/>
<source>Save and Restart Amnezia</source>
<translation>Amnezia</translation>
</message>
@@ -641,19 +662,23 @@ Already installed containers were found on the server. All installed containers
<translation>%1 </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="172"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="174"/>
<source>Remove </source>
<translation> </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="176"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="178"/>
<source>Remove %1 from server?</source>
<translation> %1 ?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="177"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation>使</translation>
<translation type="vanished">使</translation>
</message>
<message>
<source> from server?</source>
@@ -664,12 +689,12 @@ Already installed containers were found on the server. All installed containers
<translation type="obsolete"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="178"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="180"/>
<source>Continue</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="181"/>
<source>Cancel</source>
<translation></translation>
</message>
@@ -860,7 +885,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageServiceTorWebsiteSettings.qml" line="112"/>
<source>When configuring WordPress set the this onion address as domain.</source>
<translation type="unfinished"></translation>
<translation> WordPress </translation>
</message>
<message>
<source>When configuring WordPress set the domain as this onion address.</source>
@@ -1046,9 +1071,13 @@ And if you don&apos;t like the app, all the more support it - the donation will
<translation type="obsolete"> </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="73"/>
<source>Launch the application every time %1 starts</source>
<translation>%1</translation>
<translation type="vanished">%1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="73"/>
<source>Launch the application every time the device is starts</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="93"/>
@@ -1151,7 +1180,7 @@ And if you don&apos;t like the app, all the more support it - the donation will
<message>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="106"/>
<source>Backup file saved</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="122"/>
@@ -1266,52 +1295,52 @@ And if you don&apos;t like the app, all the more support it - the donation will
<translation>DNS服务器</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="49"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="50"/>
<source>If AmneziaDNS is not used or installed</source>
<translation>使AmneziaDNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="56"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="57"/>
<source>Primary DNS</source>
<translation> DNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="68"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="69"/>
<source>Secondary DNS</source>
<translation> DNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="86"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="87"/>
<source>Restore default</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="89"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="90"/>
<source>Restore default DNS settings?</source>
<translation>DNS配置</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="90"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="91"/>
<source>Continue</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="91"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="92"/>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="99"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="100"/>
<source>Settings have been reset</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="111"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="112"/>
<source>Save</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="120"/>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="121"/>
<source>Settings saved</source>
<translation></translation>
</message>
@@ -1346,7 +1375,7 @@ And if you don&apos;t like the app, all the more support it - the donation will
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="118"/>
<source>Logs file saved</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="127"/>
@@ -1508,8 +1537,12 @@ And if you don&apos;t like the app, all the more support it - the donation will
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="117"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation>使</translation>
<translation type="vanished">使</translation>
</message>
<message>
<source> from server?</source>
@@ -1573,75 +1606,75 @@ And if you don&apos;t like the app, all the more support it - the donation will
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="128"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="121"/>
<source>Mode</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="206"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="199"/>
<source>Remove </source>
<translation> </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="207"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="200"/>
<source>Continue</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="208"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="201"/>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="255"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="248"/>
<source>Site or IP</source>
<translation>IP地址</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="299"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="292"/>
<source>Import/Export Sites</source>
<translation>/</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="305"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="298"/>
<source>Import</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="310"/>
<source>Save site list</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="324"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/>
<source>Save sites</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="325"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="392"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="407"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="318"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="385"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="400"/>
<source>Sites files (*.json)</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="382"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="375"/>
<source>Import a list of sites</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="388"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="381"/>
<source>Replace site list</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="391"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="406"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="384"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="399"/>
<source>Open sites file</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="403"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="396"/>
<source>Add imported sites to existing ones</source>
<translation></translation>
</message>
@@ -1701,7 +1734,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="44"/>
<source>Configure your server</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="51"/>
@@ -1716,7 +1749,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="66"/>
<source>Login to connect via SSH</source>
<translation>ssh账号</translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="76"/>
@@ -1732,7 +1765,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="115"/>
<source>All data you enter will remain strictly confidential
and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished"></translation>
<translation>
Amnezia </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="125"/>
@@ -1787,24 +1821,24 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<name>PageSetupWizardInstalling</name>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="21"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="67"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="75"/>
<source>Usually it takes no more than 5 minutes</source>
<translation>5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="57"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="65"/>
<source>The server has already been added to the application</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="62"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="70"/>
<source>Amnezia has detected that your server is currently </source>
<translation type="unfinished"></translation>
<translation>Amnezia </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="71"/>
<source>busy installing other software. Amnezia installation </source>
<translation type="unfinished"></translation>
<translation>Amnezia安装</translation>
</message>
<message>
<source>Amnesia has detected that your server is currently </source>
@@ -1815,12 +1849,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="vanished">Amnezia安装</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="64"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="72"/>
<source>will pause until the server finishes installing other software</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="121"/>
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="129"/>
<source>Installing</source>
<translation></translation>
</message>
@@ -1938,27 +1972,27 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>PageSetupWizardViewConfig</name>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="59"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="63"/>
<source>New connection</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="86"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="90"/>
<source>Do not use connection code from public sources. It could be created to intercept your data.</source>
<translation>使</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="101"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="105"/>
<source>Collapse content</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="101"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="105"/>
<source>Show content</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="148"/>
<source>Connect</source>
<translation></translation>
</message>
@@ -1992,8 +2026,22 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
<source>Share VPN Access</source>
<translation> VPN 访</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>Share VPN access without the ability to manage the server</source>
<translation> VPN 访</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings.</source>
<translation>访访</translation>
</message>
<message>
<source>VPN Access</source>
<translation>访VPN</translation>
<translation type="vanished">访VPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="146"/>
@@ -2006,14 +2054,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>访</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>VPN access without the ability to manage the server</source>
<translation>访VPN</translation>
<translation type="vanished">访VPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.</source>
<translation type="unfinished"></translation>
<translation type="vanished">访VPN外</translation>
</message>
<message>
<source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the servers, as well as change settings.</source>
@@ -2321,7 +2367,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QObject</name>
<message>
<location filename="../protocols/protocols_defs.cpp" line="75"/>
<location filename="../protocols/protocols_defs.cpp" line="77"/>
<source>Sftp service</source>
<translation>Sftp </translation>
</message>
@@ -2574,7 +2620,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message>
<location filename="../containers/containers_defs.cpp" line="111"/>
<source>AmneziaWG - Special protocol from Amnezia, based on WireGuard. It&apos;s fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship.</source>
<translation type="unfinished"></translation>
<translation>AmneziaWG - Amnezia WireGuard WireGuard </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="115"/>
@@ -2606,7 +2652,14 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Flexible customisation to suit user needs to work with different operating systems and devices
* Recognised by DPI analysis systems and therefore susceptible to blocking
* Can operate over both TCP and UDP network protocols.</source>
<translation type="unfinished"></translation>
<translation>OpenVPN VPN
SSL/TLS OpenVPN 使OpenVPN 广OpenVPN
* AmneziaVPN 使
*
* 使
* DPI分析系统识别
* TCP UDP </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="145"/>
@@ -2618,7 +2671,14 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Configurable encryption protocol
* Detectable by some DPI systems
* Works over TCP network protocol.</source>
<translation type="unfinished"></translation>
<translation>Shadowsocks SOCKS5 使 AEAD Shadowsocks HTTPS Shadowsocks Amnezia支持有限使AmneziaWG协议
* AmneziaVPN
*
*
* DPI
* TCP </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="155"/>
@@ -2640,7 +2700,23 @@ If there is a extreme level of Internet censorship in your region, we advise you
* Not recognised by DPI analysis systems
* Works over TCP network protocol, 443 port.
</source>
<translation type="unfinished"></translation>
<translation> OpenVPN Cloak
OpenVPN Internet VPN
Cloak OpenVPN
Cloak 便 VPN Web VPN 使
Cloak VPN
使 OpenVPN over Cloak
* AmneziaVPN 使
*
*
* DPI
* TCP 443 </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="174"/>
@@ -2653,7 +2729,15 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures.
* Minimum number of settings
* Easily recognised by DPI analysis systems, susceptible to blocking
* Works over UDP network protocol.</source>
<translation type="unfinished"></translation>
<translation> VPN
VPN 使 WireGuard OpenVPN
WireGuard VPN WireGuard (DPI)
* AmneziaVPN 使
*
*
* DPI分析系统识别
* UDP </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="187"/>
@@ -2666,7 +2750,15 @@ This means that AmneziaWG keeps the fast performance of the original while addin
* Minimum number of settings
* Not recognised by DPI analysis systems, resistant to blocking
* Works over UDP network protocol.</source>
<translation type="unfinished"></translation>
<translation>AmneziaWG VPN WireGuard
WireGuard AmneziaWG 使使
AmneziaWG 使 VPN
* AmneziaVPN 使
*
*
* DPI分析系统识别
* UDP </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="203"/>
@@ -2679,7 +2771,15 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
* Minimal configuration
* Recognised by DPI analysis systems
* Works over UDP network protocol, ports 500 and 4500.</source>
<translation type="unfinished"></translation>
<translation>IKEv2 IPSec 使 VPN
使
IKEv2 IKEv2
* Windows AmneziaVPN
*
*
* DPI分析系统认可
* UDP 500 4500 </translation>
</message>
<message>
<source>OpenVPN container</source>
@@ -2771,6 +2871,16 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<source>error 0x%1: %2</source>
<translation> 0x%1: %2</translation>
</message>
<message>
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="39"/>
<source>WireGuard Configuration Highlighter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="82"/>
<source>&amp;Randomize colors</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SelectLanguageDrawer</name>
@@ -3000,27 +3110,27 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<message>
<location filename="../containers/containers_defs.cpp" line="309"/>
<source>Medium or High</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="310"/>
<source>Extreme</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="318"/>
<source>I just want to increase the level of my privacy.</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="319"/>
<source>I want to bypass censorship. This option recommended in most cases.</source>
<translation type="unfinished"></translation>
<translation>使</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="321"/>
<source>Most VPN protocols are blocked. Recommended if other options are not working.</source>
<translation type="unfinished"></translation>
<translation> VPN </translation>
</message>
<message>
<source>High</source>

View File

@@ -8,7 +8,9 @@
#include <QImage>
#include <QStandardPaths>
#include "configurators/cloak_configurator.h"
#include "configurators/openvpn_configurator.h"
#include "configurators/shadowsocks_configurator.h"
#include "configurators/wireguard_configurator.h"
#include "core/errorstrings.h"
#include "systemController.h"
@@ -19,11 +21,13 @@
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const std::shared_ptr<Settings> &settings,
const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_clientManagementModel(clientManagementModel),
m_settings(settings),
m_configurator(configurator)
{
@@ -75,13 +79,12 @@ void ExportController::generateFullAccessConfigAndroid()
}
#endif
void ExportController::generateConnectionConfig()
void ExportController::generateConnectionConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials();
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
@@ -93,17 +96,25 @@ void ExportController::generateConnectionConfig()
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QJsonObject protocolConfig = m_settings->protocolConfig(serverIndex, container, protocol);
QString vpnConfig =
m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol, &errorCode);
QString clientId;
QString vpnConfig = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol,
clientId, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
protocolConfig.insert(config_key::last_config, vpnConfig);
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
if (protocol == Proto::OpenVpn || protocol == Proto::Awg || protocol == Proto::WireGuard) {
errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
}
}
QJsonObject config = m_settings->server(serverIndex);
QJsonObject config = m_settings->server(serverIndex); // todo change to servers_model
if (!errorCode) {
config.remove(config_key::userName);
config.remove(config_key::password);
@@ -126,7 +137,127 @@ void ExportController::generateConnectionConfig()
emit exportConfigChanged();
}
void ExportController::generateOpenVpnConfig()
void ExportController::generateOpenVpnConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials();
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString clientId;
QString config = m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig,
clientId, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::OpenVpn, config);
auto configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = configJson.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8());
errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
emit exportConfigChanged();
}
void ExportController::generateWireGuardConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials();
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
QString clientId;
ErrorCode errorCode = ErrorCode::NoError;
QString config = m_configurator->wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig,
clientId, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::WireGuard, config);
auto configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = configJson.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_config.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
emit exportConfigChanged();
}
void ExportController::generateShadowSocksConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container,
containerConfig, &errorCode);
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::ShadowSocks, config);
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
m_nativeConfigString =
QString("%1:%2@%3:%4")
.arg(configJson.value("method").toString(), configJson.value("password").toString(),
configJson.value("server").toString(), configJson.value("server_port").toString());
m_nativeConfigString = "ss://" + m_nativeConfigString.toUtf8().toBase64();
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_nativeConfigString.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
emit exportConfigChanged();
}
void ExportController::generateCloakConfig()
{
clearPreviousConfig();
@@ -142,47 +273,19 @@ void ExportController::generateOpenVpnConfig()
ErrorCode errorCode = ErrorCode::NoError;
QString config =
m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, &errorCode);
m_configurator->cloakConfigurator->genCloakConfig(credentials, container, containerConfig, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::OpenVpn, config);
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Cloak, config);
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
auto configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = configJson.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
configJson.remove(config_key::transport_proto);
configJson.insert("ProxyMethod", "shadowsocks");
emit exportConfigChanged();
}
void ExportController::generateWireGuardConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config = m_configurator->wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig,
&errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::WireGuard, config);
auto configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = configJson.value(config_key::config).toString().replace("\r", "").split("\n");
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
@@ -195,6 +298,11 @@ QString ExportController::getConfig()
return m_config;
}
QString ExportController::getNativeConfigString()
{
return m_nativeConfigString;
}
QList<QString> ExportController::getQrCodes()
{
return m_qrCodes;
@@ -205,6 +313,30 @@ void ExportController::exportConfig(const QString &fileName)
SystemController::saveFile(fileName, m_config);
}
void ExportController::updateClientManagementModel(const DockerContainer container, ServerCredentials credentials)
{
ErrorCode errorCode = m_clientManagementModel->updateModel(container, credentials);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorString(errorCode));
}
}
void ExportController::revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials)
{
ErrorCode errorCode = m_clientManagementModel->revokeClient(row, container, credentials);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorString(errorCode));
}
}
void ExportController::renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials)
{
ErrorCode errorCode = m_clientManagementModel->renameClient(row, clientName, container, credentials);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorString(errorCode));
}
}
QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &data)
{
double k = 850;
@@ -219,7 +351,7 @@ QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &dat
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
QString svg = QString::fromStdString(toSvgString(qr, 0));
QString svg = QString::fromStdString(toSvgString(qr, 1));
chunks.append(svgToBase64(svg));
}
@@ -239,5 +371,6 @@ int ExportController::getQrCodesCount()
void ExportController::clearPreviousConfig()
{
m_config.clear();
m_nativeConfigString.clear();
m_qrCodes.clear();
}

View File

@@ -6,6 +6,7 @@
#include "configurators/vpn_configurator.h"
#include "ui/models/containers_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/clientManagementModel.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/authResultReceiver.h"
#endif
@@ -16,27 +17,36 @@ class ExportController : public QObject
public:
explicit ExportController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const std::shared_ptr<Settings> &settings,
const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent = nullptr);
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY exportConfigChanged)
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged)
Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged)
Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged)
public slots:
void generateFullAccessConfig();
#if defined(Q_OS_ANDROID)
void generateFullAccessConfigAndroid();
#endif
void generateConnectionConfig();
void generateOpenVpnConfig();
void generateWireGuardConfig();
void generateConnectionConfig(const QString &clientName);
void generateOpenVpnConfig(const QString &clientName);
void generateWireGuardConfig(const QString &clientName);
void generateShadowSocksConfig();
void generateCloakConfig();
QString getConfig();
QString getNativeConfigString();
QList<QString> getQrCodes();
void exportConfig(const QString &fileName);
void updateClientManagementModel(const DockerContainer container, ServerCredentials credentials);
void revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials);
void renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials);
signals:
void generateConfig(int type);
void exportErrorOccurred(const QString &errorMessage);
@@ -55,10 +65,12 @@ private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
QString m_config;
QString m_nativeConfigString;
QList<QString> m_qrCodes;
#ifdef Q_OS_ANDROID

View File

@@ -144,8 +144,6 @@ void ImportController::importConfig()
if (credentials.isValid() || m_config.contains(config_key::containers)) {
m_serversModel->addServer(m_config);
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
emit importFinished();
} else {
qDebug() << "Failed to import profile";
@@ -263,6 +261,10 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
// return QJsonObject();
// }
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(","));
lastConfig[config_key::allowed_ips] = allowedIpsJsonArray;
QString protocolName = "wireguard";
if (!configMap.value(config_key::junkPacketCount).isEmpty()
&& !configMap.value(config_key::junkPacketMinSize).isEmpty()

View File

@@ -5,6 +5,7 @@
#include <QEventLoop>
#include <QJsonObject>
#include <QStandardPaths>
#include <QRandomGenerator>
#include "core/errorstrings.h"
#include "core/servercontroller.h"
@@ -73,6 +74,38 @@ void InstallController::install(DockerContainer container, int port, TransportPr
containerConfig.insert(config_key::transport_proto,
ProtocolProps::transportProtoToString(transportProto, protocol));
if (container == DockerContainer::Awg) {
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(3, 10));
QString junkPacketMinSize = QString::number(50);
QString junkPacketMaxSize = QString::number(1000);
QString initPacketJunkSize = QString::number(QRandomGenerator::global()->bounded(15, 150));
QString responsePacketJunkSize = QString::number(QRandomGenerator::global()->bounded(15, 150));
QSet<QString> headersValue;
while (headersValue.size() != 4) {
auto max = (std::numeric_limits<qint32>::max)();
headersValue.insert(QString::number(QRandomGenerator::global()->bounded(1, max)));
}
auto headersValueList = headersValue.values();
QString initPacketMagicHeader = headersValueList.at(0);
QString responsePacketMagicHeader = headersValueList.at(1);
QString underloadPacketMagicHeader = headersValueList.at(2);
QString transportPacketMagicHeader = headersValueList.at(3);
containerConfig[config_key::junkPacketCount] = junkPacketCount;
containerConfig[config_key::junkPacketMinSize] = junkPacketMinSize;
containerConfig[config_key::junkPacketMaxSize] = junkPacketMaxSize;
containerConfig[config_key::initPacketJunkSize] = initPacketJunkSize;
containerConfig[config_key::responsePacketJunkSize] = responsePacketJunkSize;
containerConfig[config_key::initPacketMagicHeader] = initPacketMagicHeader;
containerConfig[config_key::responsePacketMagicHeader] = responsePacketMagicHeader;
containerConfig[config_key::underloadPacketMagicHeader] = underloadPacketMagicHeader;
containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader;
}
if (container == DockerContainer::Sftp) {
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(10));
@@ -132,7 +165,6 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
m_serversModel->addServer(server);
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
emit installServerFinished(finishMessage);
return;
@@ -183,9 +215,6 @@ void InstallController::installContainer(DockerContainer container, QJsonObject
"All installed containers have been added to the application");
}
if (ContainerProps::containerService(container) == ServiceType::Vpn) {
m_containersModel->setData(m_containersModel->index(0, 0), container, ContainersModel::Roles::IsDefaultRole);
}
emit installContainerFinished(finishMessage, ContainerProps::containerService(container) == ServiceType::Other);
return;
}
@@ -475,8 +504,9 @@ void InstallController::addEmptyServer()
server.insert(config_key::port, m_currentlyInstalledServerCredentials.port);
server.insert(config_key::description, m_settings->nextAvailableServerName());
server.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
m_serversModel->addServer(server);
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
emit installServerFinished(tr("Server added successfully"));
}

View File

@@ -51,7 +51,9 @@ namespace PageLoader
PageProtocolWireGuardSettings,
PageProtocolAwgSettings,
PageProtocolIKev2Settings,
PageProtocolRaw
PageProtocolRaw,
PageShareFullAccess
};
Q_ENUM_NS(PageEnum)

View File

@@ -1,104 +1,373 @@
#include "clientManagementModel.h"
#include <QJsonDocument>
#include <QJsonObject>
ClientManagementModel::ClientManagementModel(QObject *parent) : QAbstractListModel(parent)
{
#include "core/servercontroller.h"
#include "logger.h"
}
void ClientManagementModel::clearData()
namespace
{
beginResetModel();
m_content.clear();
endResetModel();
}
Logger logger("ClientManagementModel");
void ClientManagementModel::setContent(const QVector<QVariant> &data)
{
beginResetModel();
m_content = data;
endResetModel();
}
QJsonObject ClientManagementModel::getContent(amnezia::Proto protocol)
{
QJsonObject clientsTable;
for (const auto &item : m_content) {
if (protocol == amnezia::Proto::OpenVpn) {
clientsTable[item.toJsonObject()["openvpnCertId"].toString()] = item.toJsonObject();
} else if (protocol == amnezia::Proto::WireGuard) {
clientsTable[item.toJsonObject()["wireguardPublicKey"].toString()] = item.toJsonObject();
}
namespace configKey {
constexpr char clientId[] = "clientId";
constexpr char clientName[] = "clientName";
constexpr char container[] = "container";
constexpr char userData[] = "userData";
}
return clientsTable;
}
ClientManagementModel::ClientManagementModel(std::shared_ptr<Settings> settings, QObject *parent)
: m_settings(settings), QAbstractListModel(parent)
{
}
int ClientManagementModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return static_cast<int>(m_content.size());
return static_cast<int>(m_clientsTable.size());
}
QVariant ClientManagementModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0
|| index.row() >= static_cast<int>(m_content.size())) {
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(m_clientsTable.size())) {
return QVariant();
}
if (role == NameRole) {
return m_content[index.row()].toJsonObject()["clientName"].toString();
} else if (role == OpenVpnCertIdRole) {
return m_content[index.row()].toJsonObject()["openvpnCertId"].toString();
} else if (role == OpenVpnCertDataRole) {
return m_content[index.row()].toJsonObject()["openvpnCertData"].toString();
} else if (role == WireGuardPublicKey) {
return m_content[index.row()].toJsonObject()["wireguardPublicKey"].toString();
auto client = m_clientsTable.at(index.row()).toObject();
auto userData = client.value(configKey::userData).toObject();
switch (role) {
case ClientNameRole: return userData.value(configKey::clientName).toString();
}
return QVariant();
}
void ClientManagementModel::setData(const QModelIndex &index, QVariant data, int role)
ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCredentials credentials)
{
if (!index.isValid() || index.row() < 0
|| index.row() >= static_cast<int>(m_content.size())) {
return;
beginResetModel();
m_clientsTable = QJsonArray();
ServerController serverController(m_settings);
ErrorCode error = ErrorCode::NoError;
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
const QByteArray clientsTableString =
serverController.getTextFileFromContainer(container, credentials, clientsTableFile, &error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the clientsTable file from the server";
endResetModel();
return error;
}
auto client = m_content[index.row()].toJsonObject();
if (role == NameRole) {
client["clientName"] = data.toString();
} else if (role == OpenVpnCertIdRole) {
client["openvpnCertId"] = data.toString();
} else if (role == OpenVpnCertDataRole) {
client["openvpnCertData"] = data.toString();
} else if (role == WireGuardPublicKey) {
client["wireguardPublicKey"] = data.toString();
} else {
return;
}
if (m_content[index.row()] != client) {
m_content[index.row()] = client;
emit dataChanged(index, index);
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
if (m_clientsTable.isEmpty()) {
int count = 0;
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks
|| container == DockerContainer::Cloak) {
error = getOpenVpnClients(serverController, container, credentials, count);
} else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
error = getWireGuardClients(serverController, container, credentials, count);
}
if (error != ErrorCode::NoError) {
endResetModel();
return error;
}
const QByteArray newClientsTableString = QJsonDocument(m_clientsTable).toJson();
if (clientsTableString != newClientsTableString) {
error = serverController.uploadTextFileToContainer(container, credentials, newClientsTableString,
clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
}
}
}
endResetModel();
return error;
}
bool ClientManagementModel::removeRows(int row)
ErrorCode ClientManagementModel::getOpenVpnClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count)
{
ErrorCode error = ErrorCode::NoError;
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
const QString getOpenVpnClientsList =
"sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'";
QString script = serverController.replaceVars(getOpenVpnClientsList,
serverController.genVarsForScript(credentials, container));
error = serverController.runScript(credentials, script, cbReadStdOut);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to retrieve the list of issued certificates on the server";
return error;
}
if (!stdOut.isEmpty()) {
QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts);
certsIds.removeAll("AmneziaReq.crt");
for (auto &openvpnCertId : certsIds) {
openvpnCertId.replace(".crt", "");
if (!isClientExists(openvpnCertId)) {
QJsonObject client;
client[configKey::clientId] = openvpnCertId;
QJsonObject userData;
userData[configKey::clientName] = QString("Client %1").arg(count);
client[configKey::userData] = userData;
m_clientsTable.push_back(client);
count++;
}
}
}
return error;
}
ErrorCode ClientManagementModel::getWireGuardClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count)
{
ErrorCode error = ErrorCode::NoError;
const QString wireGuardConfigFile =
QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg");
const QString wireguardConfigString =
serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the wg conf file from the server";
return error;
}
auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts);
QStringList wireguardKeys;
for (const auto &line : configLines) {
auto configPair = line.split(" = ", Qt::SkipEmptyParts);
if (configPair.front() == "PublicKey") {
wireguardKeys.push_back(configPair.back());
}
}
for (auto &wireguardKey : wireguardKeys) {
if (!isClientExists(wireguardKey)) {
QJsonObject client;
client[configKey::clientId] = wireguardKey;
QJsonObject userData;
userData[configKey::clientName] = QString("Client %1").arg(count);
client[configKey::userData] = userData;
m_clientsTable.push_back(client);
count++;
}
}
return error;
}
bool ClientManagementModel::isClientExists(const QString &clientId)
{
for (const QJsonValue &value : qAsConst(m_clientsTable)) {
if (value.isObject()) {
QJsonObject obj = value.toObject();
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == clientId) {
return true;
}
}
}
return false;
}
ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QString &clientName,
const DockerContainer container, ServerCredentials credentials)
{
ErrorCode error;
error = updateModel(container, credentials);
if (error != ErrorCode::NoError) {
return error;
}
for (int i = 0; i < m_clientsTable.size(); i++) {
if (m_clientsTable.at(i).toObject().value(configKey::clientId) == clientId) {
return renameClient(i, clientName, container, credentials);
}
}
beginResetModel();
QJsonObject client;
client[configKey::clientId] = clientId;
QJsonObject userData;
userData[configKey::clientName] = clientName;
client[configKey::userData] = userData;
m_clientsTable.push_back(client);
endResetModel();
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
ServerController serverController(m_settings);
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
}
return error;
}
ErrorCode ClientManagementModel::renameClient(const int row, const QString &clientName, const DockerContainer container,
ServerCredentials credentials)
{
auto client = m_clientsTable.at(row).toObject();
auto userData = client[configKey::userData].toObject();
userData[configKey::clientName] = clientName;
client[configKey::userData] = userData;
m_clientsTable.replace(row, client);
emit dataChanged(index(row, 0), index(row, 0));
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
ServerController serverController(m_settings);
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
ErrorCode error =
serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
}
return error;
}
ErrorCode ClientManagementModel::revokeClient(const int row, const DockerContainer container,
ServerCredentials credentials)
{
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks
|| container == DockerContainer::Cloak) {
return revokeOpenVpn(row, container, credentials);
} else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
return revokeWireGuard(row, container, credentials);
}
return ErrorCode::NoError;
}
ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContainer container,
ServerCredentials credentials)
{
auto client = m_clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c '"
"cd /opt/amnezia/openvpn ;\\"
"easyrsa revoke %1 ;\\"
"easyrsa gen-crl ;\\"
"cp pki/crl.pem .'")
.arg(clientId);
ServerController serverController(m_settings);
const QString script =
serverController.replaceVars(getOpenVpnCertData, serverController.genVarsForScript(credentials, container));
ErrorCode error = serverController.runScript(credentials, script);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to revoke the certificate";
return error;
}
beginRemoveRows(QModelIndex(), row, row);
m_content.removeAt(row);
m_clientsTable.removeAt(row);
endRemoveRows();
return true;
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
return ErrorCode::NoError;
}
ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerContainer container,
ServerCredentials credentials)
{
ErrorCode error;
ServerController serverController(m_settings);
const QString wireGuardConfigFile =
QString("/opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg");
const QString wireguardConfigString =
serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the wg conf file from the server";
return error;
}
auto client = m_clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts);
for (auto &section : configSections) {
if (section.contains(clientId)) {
configSections.removeOne(section);
break;
}
}
QString newWireGuardConfig = configSections.join("[");
newWireGuardConfig.insert(0, "[");
error = serverController.uploadTextFileToContainer(container, credentials, newWireGuardConfig, wireGuardConfigFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the wg conf file to the server";
return error;
}
beginRemoveRows(QModelIndex(), row, row);
m_clientsTable.removeAt(row);
endRemoveRows();
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
const QString script = "sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'";
error = serverController.runScript(
credentials,
serverController.replaceVars(script.arg(wireGuardConfigFile),
serverController.genVarsForScript(credentials, container)));
if (error != ErrorCode::NoError) {
logger.error() << "Failed to execute the command 'wg syncconf' on the server";
return error;
}
return ErrorCode::NoError;
}
QHash<int, QByteArray> ClientManagementModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "clientName";
roles[OpenVpnCertIdRole] = "openvpnCertId";
roles[OpenVpnCertDataRole] = "openvpnCertData";
roles[WireGuardPublicKey] = "wireguardPublicKey";
roles[ClientNameRole] = "clientName";
return roles;
}

View File

@@ -2,36 +2,48 @@
#define CLIENTMANAGEMENTMODEL_H
#include <QAbstractListModel>
#include <QJsonArray>
#include "protocols/protocols_defs.h"
#include "core/servercontroller.h"
#include "settings.h"
class ClientManagementModel : public QAbstractListModel
{
Q_OBJECT
public:
enum ClientRoles {
NameRole = Qt::UserRole + 1,
OpenVpnCertIdRole,
OpenVpnCertDataRole,
WireGuardPublicKey,
enum Roles {
ClientNameRole = Qt::UserRole + 1,
};
ClientManagementModel(QObject *parent = nullptr);
ClientManagementModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
void clearData();
void setContent(const QVector<QVariant> &data);
QJsonObject getContent(amnezia::Proto protocol);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setData(const QModelIndex &index, QVariant data, int role = Qt::DisplayRole);
bool removeRows(int row);
public slots:
ErrorCode updateModel(DockerContainer container, ServerCredentials credentials);
ErrorCode appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
ServerCredentials credentials);
ErrorCode renameClient(const int row, const QString &userName, const DockerContainer container,
ServerCredentials credentials);
ErrorCode revokeClient(const int index, const DockerContainer container, ServerCredentials credentials);
protected:
QHash<int, QByteArray> roleNames() const override;
private:
QVector<QVariant> m_content;
bool isClientExists(const QString &clientId);
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, ServerCredentials credentials);
ErrorCode revokeWireGuard(const int row, const DockerContainer container, ServerCredentials credentials);
ErrorCode getOpenVpnClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count);
ErrorCode getWireGuardClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count);
QJsonArray m_clientsTable;
std::shared_ptr<Settings> m_settings;
};
#endif // CLIENTMANAGEMENTMODEL_H

View File

@@ -22,10 +22,6 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
DockerContainer container = ContainerProps::allContainers().at(index.row());
switch (role) {
case NameRole:
// return ContainerProps::containerHumanNames().value(container);
case DescriptionRole:
// return ContainerProps::containerDescriptions().value(container);
case ConfigRole: {
m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject());
m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
@@ -35,19 +31,15 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
break;
}
}
case ServiceTypeRole:
// return ContainerProps::containerService(container);
case DockerContainerRole:
// return container;
case IsInstalledRole:
// return m_settings->containers(m_currentlyProcessedServerIndex).contains(container);
case IsDefaultRole: {
case IsDefaultRole: { //todo remove
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
m_defaultContainerIndex = container;
emit defaultContainerChanged();
}
default: break;
}
emit containersModelUpdated();
emit dataChanged(index, index);
return true;
}
@@ -117,6 +109,14 @@ QString ContainersModel::getDefaultContainerName()
return ContainerProps::containerHumanNames().value(m_defaultContainerIndex);
}
void ContainersModel::setDefaultContainer(int index)
{
auto container = static_cast<DockerContainer>(index);
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
m_defaultContainerIndex = container;
emit defaultContainerChanged();
}
int ContainersModel::getCurrentlyProcessedContainerIndex()
{
return m_currentlyProcessedContainerIndex;

View File

@@ -46,6 +46,7 @@ public:
public slots:
DockerContainer getDefaultContainer();
QString getDefaultContainerName();
void setDefaultContainer(int index);
void setCurrentlyProcessedServerIndex(const int index);
@@ -72,6 +73,7 @@ protected:
signals:
void defaultContainerChanged();
void containersModelUpdated();
private:
QMap<DockerContainer, QJsonObject> m_containers;

View File

@@ -96,7 +96,7 @@ void ServersModel::setDefaultServerIndex(const int index)
{
m_settings->setDefaultServer(index);
m_defaultServerIndex = m_settings->defaultServerIndex();
emit defaultServerIndexChanged();
emit defaultServerIndexChanged(m_defaultServerIndex);
}
const int ServersModel::getDefaultServerIndex()
@@ -145,6 +145,11 @@ QString ServersModel::getCurrentlyProcessedServerHostName()
return qvariant_cast<QString>(data(m_currentlyProcessedServerIndex, HostNameRole));
}
const ServerCredentials ServersModel::getCurrentlyProcessedServerCredentials()
{
return serverCredentials(m_currentlyProcessedServerIndex);
}
bool ServersModel::isDefaultServerCurrentlyProcessed()
{
return m_defaultServerIndex == m_currentlyProcessedServerIndex;
@@ -193,6 +198,12 @@ bool ServersModel::isDefaultServerConfigContainsAmneziaDns()
return primaryDns == protocols::dns::amneziaDnsIp;
}
void ServersModel::updateContainersConfig()
{
auto server = m_settings->server(m_currentlyProcessedServerIndex);
m_servers.replace(m_currentlyProcessedServerIndex, server);
}
QHash<int, QByteArray> ServersModel::roleNames() const
{
QHash<int, QByteArray> roles;

View File

@@ -53,18 +53,21 @@ public slots:
int getCurrentlyProcessedServerIndex();
QString getCurrentlyProcessedServerHostName();
const ServerCredentials getCurrentlyProcessedServerCredentials();
void addServer(const QJsonObject &server);
void removeServer();
bool isDefaultServerConfigContainsAmneziaDns();
void updateContainersConfig();
protected:
QHash<int, QByteArray> roleNames() const override;
signals:
void currentlyProcessedServerIndexChanged(const int index);
void defaultServerIndexChanged();
void defaultServerIndexChanged(const int index);
void defaultServerNameChanged();
private:

View File

@@ -3,7 +3,14 @@
SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent)
: QAbstractListModel(parent), m_settings(settings)
{
m_currentRouteMode = m_settings->routeMode();
auto routeMode = m_settings->routeMode();
if (routeMode == Settings::RouteMode::VpnAllSites) {
m_isSplitTunnelingEnabled = false;
m_currentRouteMode = Settings::RouteMode::VpnOnlyForwardSites;
} else {
m_isSplitTunnelingEnabled = true;
m_currentRouteMode = routeMode;
}
fillSites();
}
@@ -93,6 +100,21 @@ void SitesModel::setRouteMode(int routeMode)
emit routeModeChanged();
}
bool SitesModel::isSplitTunnelingEnabled()
{
return m_isSplitTunnelingEnabled;
}
void SitesModel::toggleSplitTunneling(bool enabled)
{
if (enabled) {
setRouteMode(m_currentRouteMode);
} else {
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
}
m_isSplitTunnelingEnabled = enabled;
}
QVector<QPair<QString, QString> > SitesModel::getCurrentSites()
{
return m_sites;

View File

@@ -31,6 +31,9 @@ public slots:
int getRouteMode();
void setRouteMode(int routeMode);
bool isSplitTunnelingEnabled();
void toggleSplitTunneling(bool enabled);
QVector<QPair<QString, QString>> getCurrentSites();
signals:
@@ -44,6 +47,7 @@ private:
std::shared_ptr<Settings> m_settings;
bool m_isSplitTunnelingEnabled;
Settings::RouteMode m_currentRouteMode;
QVector<QPair<QString, QString>> m_sites;

View File

@@ -142,6 +142,7 @@ Button {
PageController.setTriggeredBtConnectButton(true)
ServersModel.currentlyProcessedIndex = ServersModel.getDefaultServerIndex()
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardEasy)
return

View File

@@ -50,34 +50,26 @@ ListView {
imageSource: "qrc:/images/controls/download.svg"
showImage: !isInstalled
checkable: isInstalled
checkable: isInstalled && !ConnectionController.isConnected && isSupported
checked: isDefault
onPressed: function(mouse) {
if (!isSupported) {
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
}
}
onClicked: {
if (checked) {
var needReconnected = false
if (!isDefault) {
needReconnected = true
}
if (ConnectionController.isConnected && isInstalled) {
PageController.showNotificationMessage(qsTr("Unable change protocol while there is an active connection"))
return
}
if (checked) {
isDefault = true
menuContent.currentIndex = index
containersDropDown.menuVisible = false
if (needReconnected && (ConnectionController.isConnected || ConnectionController.isConnectionInProgress)) {
PageController.showNotificationMessage(qsTr("Reconnect via VPN Procotol: ") + name)
PageController.goToPageHome()
ConnectionController.openConnection()
}
} else {
if (!isSupported && isInstalled) {
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
return
}
ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(index))
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)

View File

@@ -17,9 +17,11 @@ DrawerType {
property var noButtonFunction
width: parent.width
height: parent.height * 0.5
height: content.implicitHeight + 32
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right

View File

@@ -112,6 +112,30 @@ DrawerType {
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 8
visible: nativeConfigString.text !== ""
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
borderWidth: 1
text: qsTr("Copy config string")
imageSource: "qrc:/images/controls/copy.svg"
onClicked: {
nativeConfigString.selectAll()
nativeConfigString.copy()
nativeConfigString.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 24
@@ -170,6 +194,12 @@ DrawerType {
}
TextField {
id: nativeConfigString
visible: false
text: ExportController.nativeConfigString
}
TextArea {
id: configText
Layout.fillWidth: true
@@ -213,7 +243,6 @@ DrawerType {
Image {
anchors.fill: parent
anchors.margins: 2
smooth: false
source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : ""

View File

@@ -81,6 +81,7 @@ RadioButton {
Text {
text: root.headerText
wrapMode: Text.WordWrap
color: "#D7D8DB"
font.pixelSize: 25
font.weight: 700
@@ -110,6 +111,7 @@ RadioButton {
Text {
text: root.footerText
wrapMode: Text.WordWrap
visible: root.footerText !== ""
color: "#878B91"
font.pixelSize: 13

View File

@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Controls
import "../Config"
Drawer {
id: drawer
property bool needCloseButton: true
@@ -39,6 +41,18 @@ Drawer {
border.color: "#2C2D30"
border.width: 1
Rectangle {
visible: GC.isMobile()
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 10
width: 20
height: 2
color: "#2C2D30"
}
}
Overlay.modal: Rectangle {

View File

@@ -30,17 +30,13 @@ Switch {
property string hoveredIndicatorBackgroundColor: Qt.rgba(1, 1, 1, 0.08)
property string defaultIndicatorBackgroundColor: "transparent"
implicitWidth: content.implicitWidth + switcher.implicitWidth
implicitHeight: content.implicitHeight
hoverEnabled: enabled ? true : false
indicator: Rectangle {
id: switcher
anchors.left: content.right
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 4
implicitWidth: 52
implicitHeight: 32
@@ -90,11 +86,12 @@ Switch {
contentItem: ColumnLayout {
id: content
anchors.fill: parent
anchors.rightMargin: switcher.implicitWidth
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
ListItemTitleType {
Layout.fillWidth: true
rightPadding: indicator.width
text: root.text
color: root.enabled ? root.textColor : root.textDisabledColor
@@ -104,6 +101,7 @@ Switch {
id: description
Layout.fillWidth: true
rightPadding: indicator.width
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor

View File

@@ -32,7 +32,7 @@ PageType {
function onRestorePageHomeState(isContainerInstalled) {
buttonContent.state = "expanded"
if (isContainerInstalled) {
containersDropDown.menuVisible = true
containersDropDown.rootButtonClickedFunction()
}
}
function onForceCloseDrawer() {
@@ -241,8 +241,18 @@ PageType {
}
]
DividerType {
Layout.topMargin: 10
Layout.fillWidth: false
Layout.preferredWidth: 20
Layout.preferredHeight: 2
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: (buttonContent.collapsedVisibility || buttonContent.expandedVisibility)
}
RowLayout {
Layout.topMargin: 24
Layout.topMargin: 14
Layout.leftMargin: 24
Layout.rightMargin: 24
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
@@ -305,7 +315,7 @@ PageType {
Header1TextType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.topMargin: 14
Layout.leftMargin: 16
Layout.rightMargin: 16
@@ -471,10 +481,16 @@ PageType {
}
checked: index === serversMenuContent.currentIndex
checkable: !ConnectionController.isConnected
ButtonGroup.group: serversRadioButtonGroup
onClicked: {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection"))
return
}
serversMenuContent.currentIndex = index
ServersModel.currentlyProcessedIndex = index

View File

@@ -276,7 +276,7 @@ PageType {
onClicked: {
questionDrawer.headerText = qsTr("Remove AmneziaWG from server?")
questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")

View File

@@ -5,6 +5,7 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerEnum 1.0
import "./"
import "../Controls2"
@@ -252,6 +253,8 @@ PageType {
ColumnLayout {
id: checkboxLayout
anchors.fill: parent
CheckBoxType {
Layout.fillWidth: true
@@ -351,6 +354,8 @@ PageType {
Layout.leftMargin: -8
implicitHeight: 32
visible: ContainersModel.getCurrentlyProcessedContainerIndex() === ContainerEnum.OpenVpn
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
@@ -360,7 +365,7 @@ PageType {
onClicked: {
questionDrawer.headerText = qsTr("Remove OpenVpn from server?")
questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")

View File

@@ -169,12 +169,14 @@ PageType {
width: parent.width
visible: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
text: qsTr("Remove ") + ContainersModel.getCurrentlyProcessedContainerName()
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")

View File

@@ -70,7 +70,7 @@ PageType {
Layout.margins: 16
text: qsTr("Auto start")
descriptionText: qsTr("Launch the application every time %1 starts").arg(Qt.platform.os)
descriptionText: qsTr("Launch the application every time the device is starts")
checked: SettingsController.isAutoStartEnabled()
onCheckedChanged: {

View File

@@ -94,7 +94,7 @@ PageType {
DividerType {}
LabelWithButtonType {
visible: !GC.isMobile()
visible: true
Layout.fillWidth: true
@@ -108,11 +108,11 @@ PageType {
}
DividerType {
visible: !GC.isMobile()
visible: GC.isDesktop()
}
LabelWithButtonType {
visible: !GC.isMobile()
visible: false
Layout.fillWidth: true
@@ -125,7 +125,7 @@ PageType {
}
DividerType {
visible: !GC.isMobile()
visible: false
}
}
}

View File

@@ -46,6 +46,7 @@ PageType {
}
ParagraphTextType {
Layout.fillWidth: true
text: qsTr("If AmneziaDNS is not used or installed")
}

View File

@@ -114,7 +114,7 @@ PageType {
clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")

View File

@@ -93,22 +93,15 @@ PageType {
SwitcherType {
id: switcher
property int lastActiveRouteMode: routeMode.onlyForwardSites
enabled: root.pageEnabled
Layout.fillWidth: true
Layout.rightMargin: 16
checked: SitesModel.routeMode !== routeMode.allSites
onToggled: {
if (checked) {
SitesModel.routeMode = lastActiveRouteMode
} else {
lastActiveRouteMode = SitesModel.routeMode
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
SitesModel.routeMode = routeMode.allSites
}
checked: SitesModel.isSplitTunnelingEnabled()
onToggled: {
SitesModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
}
}
}
@@ -123,7 +116,7 @@ PageType {
drawerHeight: 0.4375
enabled: switcher.checked && root.pageEnabled
enabled: root.pageEnabled
headerText: qsTr("Mode")
@@ -165,7 +158,7 @@ PageType {
anchors.topMargin: 16
contentHeight: col.implicitHeight + addSiteButton.implicitHeight + addSiteButton.anchors.bottomMargin + addSiteButton.anchors.topMargin
enabled: switcher.checked && root.pageEnabled
enabled: root.pageEnabled
Column {
id: col

View File

@@ -54,7 +54,7 @@ PageType {
regularExpression: InstallController.ipAddressPortRegExp()
}
onTextFieldTextChanged: {
onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
}
}
@@ -81,6 +81,10 @@ PageType {
clickedFunc: function() {
hidePassword = !hidePassword
}
onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
}
}
BasicButtonType {
@@ -90,6 +94,7 @@ PageType {
text: qsTr("Continue")
onClicked: function() {
forceActiveFocus()
if (!isCredentialsFilled()) {
return
}
@@ -112,8 +117,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 12
text: qsTr("All data you enter will remain strictly confidential
and will not be shared or disclosed to the Amnezia or any third parties")
text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties")
}
}
}

View File

@@ -24,23 +24,25 @@ PageType {
target: InstallController
function onInstallContainerFinished(finishedMessage, isServiceInstall) {
PageController.goToStartPage()
if (!ConnectionController.isConnected && !isServiceInstall) {
ContainersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex())
}
PageController.closePage() // close installing page
PageController.closePage() // close protocol settings page
if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageHome)) {
PageController.restorePageHomeState(true)
} else if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSettings)) {
PageController.goToPage(PageEnum.PageSettingsServersList, false)
PageController.goToPage(PageEnum.PageSettingsServerInfo, false)
if (isServiceInstall) {
PageController.goToPageSettingsServerServices()
}
} else {
PageController.goToPage(PageEnum.PageHome)
}
PageController.showNotificationMessage(finishedMessage)
}
function onInstallServerFinished(finishedMessage) {
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
}
PageController.goToStartPage()
if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSetupWizardStart)) {
PageController.replaceStartPage()

View File

@@ -224,7 +224,7 @@ PageType {
if (ProtocolProps.defaultPort(defaultContainerProto) < 0) {
port.visible = false
} else {
port.textFieldText = ProtocolProps.defaultPort(defaultContainerProto)
port.textFieldText = ProtocolProps.getPortForInstall(defaultContainerProto)
}
transportProtoSelector.currentIndex = ProtocolProps.defaultTransportProto(defaultContainerProto)

View File

@@ -134,7 +134,7 @@ PageType {
text: qsTr("I have nothing")
onClicked: Qt.openUrlExternally("https://ru-docs.amnezia.org/guides/hosting-instructions")
onClicked: Qt.openUrlExternally("https://amnezia.org/instructions/0_starter-guide")
}
}

View File

@@ -24,6 +24,10 @@ PageType {
}
function onImportFinished() {
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
}
PageController.goToStartPage()
if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSetupWizardStart)) {
PageController.replaceStartPage()

View File

@@ -18,15 +18,28 @@ PageType {
enum ConfigType {
AmneziaConnection,
AmneziaFullAccess,
OpenVpn,
WireGuard
WireGuard,
ShadowSocks,
Cloak
}
signal revokeConfig(int index)
onRevokeConfig: function(index) {
PageController.showBusyIndicator(true)
ExportController.revokeConfig(index,
ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
PageController.showBusyIndicator(false)
}
Connections {
target: ExportController
function onGenerateConfig(type) {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.needCloseButton = false
shareConnectionDrawer.open()
@@ -34,28 +47,34 @@ PageType {
PageController.showBusyIndicator(true)
switch (type) {
case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(); break;
case PageShare.ConfigType.AmneziaFullAccess: {
if (Qt.platform.os === "android") {
ExportController.generateFullAccessConfigAndroid();
} else {
ExportController.generateFullAccessConfig();
}
break;
}
case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(clientNameTextField.textFieldText); break;
case PageShare.ConfigType.OpenVpn: {
ExportController.generateOpenVpnConfig();
ExportController.generateOpenVpnConfig(clientNameTextField.textFieldText)
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
shareConnectionDrawer.configExtension = ".ovpn"
shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
break;
break
}
case PageShare.ConfigType.WireGuard: {
ExportController.generateWireGuardConfig();
ExportController.generateWireGuardConfig(clientNameTextField.textFieldText)
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
break;
break
}
case PageShare.ConfigType.ShadowSocks: {
ExportController.generateShadowSocksConfig()
shareConnectionDrawer.configCaption = qsTr("Save ShadowSocks config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks"
break
}
case PageShare.ConfigType.Cloak: {
ExportController.generateCloakConfig()
shareConnectionDrawer.configCaption = qsTr("Save Cloak config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
break
}
}
@@ -73,8 +92,7 @@ PageType {
}
}
property string fullConfigServerSelectorText
property string connectionServerSelectorText
property bool isSearchBarVisible: false
property bool showContent: false
property bool shareButtonEnabled: true
property list<QtObject> connectionTypesModel: [
@@ -96,6 +114,16 @@ PageType {
property string name: qsTr("WireGuard native format")
property var type: PageShare.ConfigType.WireGuard
}
QtObject {
id: shadowSocksConnectionFormat
property string name: qsTr("ShadowSocks native format")
property var type: PageShare.ConfigType.ShadowSocks
}
QtObject {
id: cloakConnectionFormat
property string name: qsTr("Cloak native format")
property var type: PageShare.ConfigType.Cloak
}
FlickableType {
anchors.top: parent.top
@@ -118,7 +146,52 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 24
headerText: qsTr("VPN Access")
headerText: qsTr("Share VPN Access")
actionButtonImage: "qrc:/images/controls/more-vertical.svg"
actionButtonFunction: function() {
shareFullAccessDrawer.open()
}
DrawerType {
id: shareFullAccessDrawer
width: root.width
height: root.height * 0.45
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
spacing: 0
Header2Type {
Layout.fillWidth: true
Layout.bottomMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Share full access to the server and VPN")
descriptionText: qsTr("Use for your own devices, or share with those you trust to manage the server.")
}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Share")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageShareFullAccess)
shareFullAccessDrawer.close()
}
}
}
}
}
Rectangle {
@@ -147,20 +220,21 @@ PageType {
onClicked: {
accessTypeSelector.currentIndex = 0
serverSelector.text = root.connectionServerSelectorText
}
}
HorizontalRadioButton {
checked: root.currentIndex === 1
checked: accessTypeSelector.currentIndex === 1
implicitWidth: (root.width - 32) / 2
text: qsTr("Full access")
text: qsTr("Users")
onClicked: {
accessTypeSelector.currentIndex = 1
serverSelector.text = root.fullConfigServerSelectorText
root.shareButtonEnabled = true
PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
PageController.showBusyIndicator(false)
}
}
}
@@ -171,16 +245,30 @@ PageType {
Layout.topMargin: 24
Layout.bottomMargin: 24
text: accessTypeSelector.currentIndex === 0 ? qsTr("VPN access without the ability to manage the server") :
qsTr("Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.")
visible: accessTypeSelector.currentIndex === 0
text: qsTr("Share VPN access without the ability to manage the server")
color: "#878B91"
}
TextFieldWithHeaderType {
id: clientNameTextField
Layout.fillWidth: true
Layout.topMargin: 16
visible: accessTypeSelector.currentIndex === 0
headerText: qsTr("User name")
textFieldText: "New client"
checkEmptyText: true
}
DropDownType {
id: serverSelector
signal severSelectorIndexChanged
property int currentIndex: 0
property int currentIndex: -1
Layout.fillWidth: true
Layout.topMargin: 16
@@ -207,8 +295,6 @@ PageType {
]
}
currentIndex: 0
clickedFunction: function() {
handler()
@@ -217,22 +303,17 @@ PageType {
serverSelector.severSelectorIndexChanged()
}
if (accessTypeSelector.currentIndex !== 0) {
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
}
serverSelector.menuVisible = false
}
Component.onCompleted: {
handler()
serverSelector.severSelectorIndexChanged()
serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ?
proxyServersModel.mapFromSource(ServersModel.defaultIndex) : 0
serverSelectorListView.triggerCurrentItem()
}
function handler() {
serverSelector.text = selectedText
root.fullConfigServerSelectorText = selectedText
root.connectionServerSelectorText = selectedText
ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex)
}
}
@@ -241,8 +322,6 @@ PageType {
DropDownType {
id: protocolSelector
visible: accessTypeSelector.currentIndex === 0
Layout.fillWidth: true
Layout.topMargin: 16
@@ -275,22 +354,18 @@ PageType {
currentIndex: 0
clickedFunction: function() {
protocolSelectorListView.currentItem.y
handler()
protocolSelector.menuVisible = false
}
Component.onCompleted: {
if (accessTypeSelector.currentIndex === 0) {
handler()
}
}
Connections {
target: serverSelector
function onSeverSelectorIndexChanged() {
protocolSelectorListView.currentIndex = 0
protocolSelectorListView.currentIndex = proxyContainersModel.mapFromSource(ContainersModel.getDefaultContainer())
protocolSelectorListView.triggerCurrentItem()
}
}
@@ -304,13 +379,17 @@ PageType {
}
protocolSelector.text = selectedText
root.connectionServerSelectorText = serverSelector.text
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(currentIndex))
fillConnectionTypeModel()
if (accessTypeSelector.currentIndex === 1) {
PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
PageController.showBusyIndicator(false)
}
}
function fillConnectionTypeModel() {
@@ -320,8 +399,15 @@ PageType {
if (index === ContainerProps.containerFromString("amnezia-openvpn")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-awg")) {
} else if (index === ContainerProps.containerFromString("amnezia-wireguard")) {
root.connectionTypesModel.push(wireGuardConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-shadowsocks")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-openvpn-cloak")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
root.connectionTypesModel.push(cloakConnectionFormat)
}
}
}
@@ -378,18 +464,235 @@ PageType {
Layout.topMargin: 40
enabled: shareButtonEnabled
visible: accessTypeSelector.currentIndex === 0
text: qsTr("Share")
imageSource: "qrc:/images/controls/share-2.svg"
onClicked: {
if (accessTypeSelector.currentIndex === 0) {
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
} else {
ExportController.generateConfig(PageShare.ConfigType.AmneziaFullAccess)
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
}
}
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 16
visible: accessTypeSelector.currentIndex === 1 && !root.isSearchBarVisible
headerText: qsTr("Users")
actionButtonImage: "qrc:/images/controls/search.svg"
actionButtonFunction: function() {
root.isSearchBarVisible = true
}
}
RowLayout {
Layout.topMargin: 24
Layout.bottomMargin: 16
visible: accessTypeSelector.currentIndex === 1 && root.isSearchBarVisible
TextFieldWithHeaderType {
id: searchTextField
Layout.fillWidth: true
textFieldPlaceholderText: qsTr("Search")
}
ImageButtonType {
image: "qrc:/images/controls/close.svg"
imageColor: "#D7D8DB"
onClicked: function() {
root.isSearchBarVisible = false
searchTextField.textFieldText = ""
}
}
}
ListView {
id: clientsListView
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
visible: accessTypeSelector.currentIndex === 1
model: SortFilterProxyModel {
id: proxyClientManagementModel
sourceModel: ClientManagementModel
filters: RegExpFilter {
roleName: "clientName"
pattern: ".*" + searchTextField.textFieldText + ".*"
caseSensitivity: Qt.CaseInsensitive
}
}
clip: true
interactive: false
delegate: Item {
implicitWidth: clientsListView.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: -16
anchors.leftMargin: -16
LabelWithButtonType {
Layout.fillWidth: true
text: clientName
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
clientInfoDrawer.open()
}
}
DividerType {}
DrawerType {
id: clientInfoDrawer
width: root.width
height: root.height * 0.5
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 8
Header2Type {
Layout.fillWidth: true
Layout.bottomMargin: 24
headerText: clientName
descriptionText: serverSelector.text
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 24
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
borderWidth: 1
text: qsTr("Rename")
onClicked: function() {
clientNameEditDrawer.open()
}
DrawerType {
id: clientNameEditDrawer
width: root.width
height: root.height * 0.35
onVisibleChanged: {
if (clientNameEditDrawer.visible) {
clientNameEditor.textField.forceActiveFocus()
}
}
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
anchors.leftMargin: 16
anchors.rightMargin: 16
TextFieldWithHeaderType {
id: clientNameEditor
Layout.fillWidth: true
headerText: qsTr("Client name")
textFieldText: clientName
textField.maximumLength: 30
}
BasicButtonType {
Layout.fillWidth: true
text: qsTr("Save")
onClicked: {
if (clientNameEditor.textFieldText !== clientName) {
PageController.showBusyIndicator(true)
ExportController.renameClient(index,
clientNameEditor.textFieldText,
ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
PageController.showBusyIndicator(false)
clientNameEditDrawer.close()
}
}
}
}
}
}
BasicButtonType {
Layout.fillWidth: true
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
borderWidth: 1
text: qsTr("Revoke")
onClicked: function() {
questionDrawer.headerText = qsTr("Revoke the config for a user - ") + clientName + "?"
questionDrawer.descriptionText = qsTr("The user will no longer be able to connect to your server.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.close()
clientInfoDrawer.close()
root.revokeConfig(index)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.close()
}
questionDrawer.open()
}
}
}
}
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}
MouseArea {
anchors.fill: parent
onPressed: function(mouse) {
forceActiveFocus()
mouse.accepted = false
}
}
}

View File

@@ -0,0 +1,155 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Components"
PageType {
id: root
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
FlickableType {
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 0
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 24
headerText: qsTr("Full access to the server and VPN")
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") +
qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ")
color: "#878B91"
}
DropDownType {
id: serverSelector
signal severSelectorIndexChanged
property int currentIndex: 0
Layout.fillWidth: true
Layout.topMargin: 16
drawerHeight: 0.4375
descriptionText: qsTr("Server")
headerText: qsTr("Server")
listView: ListViewWithRadioButtonType {
id: serverSelectorListView
rootWidth: root.width
imageSource: "qrc:/images/controls/check.svg"
model: SortFilterProxyModel {
id: proxyServersModel
sourceModel: ServersModel
filters: [
ValueFilter {
roleName: "hasWriteAccess"
value: true
}
]
}
currentIndex: 0
clickedFunction: function() {
handler()
if (serverSelector.currentIndex !== serverSelectorListView.currentIndex) {
serverSelector.currentIndex = serverSelectorListView.currentIndex
}
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
serverSelector.menuVisible = false
}
Component.onCompleted: {
handler()
}
function handler() {
serverSelector.text = selectedText
ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex)
}
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 40
text: qsTr("Share")
imageSource: "qrc:/images/controls/share-2.svg"
onClicked: function() {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.needCloseButton = false
shareConnectionDrawer.open()
shareConnectionDrawer.contentVisible = false
PageController.showBusyIndicator(true)
if (Qt.platform.os === "android") {
ExportController.generateFullAccessConfigAndroid();
} else {
ExportController.generateFullAccessConfig();
}
PageController.showBusyIndicator(false)
shareConnectionDrawer.needCloseButton = true
PageController.showTopCloseButton(true)
shareConnectionDrawer.contentVisible = true
}
}
ShareConnectionDrawer {
id: shareConnectionDrawer
}
}
}
}

View File

@@ -68,7 +68,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
}
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
@@ -227,12 +227,15 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser
configData = lastVpnConfig.value(proto);
configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData);
} else {
configData = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, proto, errorCode);
QString clientId;
configData = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, proto, clientId, errorCode);
if (errorCode && *errorCode) {
return "";
}
emit m_configurator->newVpnConfigCreated(clientId, "unnamed client", container, credentials);
QString configDataBeforeLocalProcessing = configData;
configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData);
@@ -323,12 +326,14 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
ErrorCode e = ErrorCode::NoError;
m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig, &e);
emit newVpnConfigurationCreated();
if (e) {
emit connectionStateChanged(Vpn::ConnectionState::Error);
return;
}
appendSplitTunnelingConfig();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration));
if (!m_vpnProtocol) {
@@ -363,6 +368,26 @@ void VpnConnection::createProtocolConnections()
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
}
void VpnConnection::appendSplitTunnelingConfig()
{
auto routeMode = m_settings->routeMode();
auto sites = m_settings->getVpnIps(routeMode);
QJsonArray sitesJsonArray;
for (const auto &site : sites) {
sitesJsonArray.append(site);
}
// Allow traffic to Amezia DNS
if (routeMode == Settings::VpnOnlyForwardSites){
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
}
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
}
#ifdef Q_OS_ANDROID
void VpnConnection::restoreConnection()
{

View File

@@ -79,8 +79,6 @@ signals:
void serviceIsNotReady();
void newVpnConfigurationCreated();
protected slots:
void onBytesChanged(quint64 receivedBytes, quint64 sentBytes);
void onConnectionStateChanged(Vpn::ConnectionState state);
@@ -112,6 +110,8 @@ private:
#endif
void createProtocolConnections();
void appendSplitTunnelingConfig();
};
#endif // VPNCONNECTION_H

View File

@@ -96,16 +96,16 @@ if [ "${MAC_CERT_PW+x}" ]; then
security find-identity -p codesigning
echo "Signing App bundle..."
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "Developer ID Application: Privacy Technologies OU (X7UJ388FXK)" $BUNDLE_DIR
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $BUNDLE_DIR
/usr/bin/codesign --verify -vvvv $BUNDLE_DIR || true
spctl -a -vvvv $BUNDLE_DIR || true
if [ "${NOTARIZE_APP+x}" ]; then
echo "Notarizing App bundle..."
/usr/bin/ditto -c -k --keepParent $BUNDLE_DIR $PROJECT_DIR/Bundle_to_notarize.zip
xcrun altool --notarize-app -f $PROJECT_DIR/Bundle_to_notarize.zip -t osx --primary-bundle-id "$APP_DOMAIN" -u "$APPLE_DEV_EMAIL" -p $APPLE_DEV_PASSWORD
xcrun notarytool submit $PROJECT_DIR/Bundle_to_notarize.zip --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
rm $PROJECT_DIR/Bundle_to_notarize.zip
sleep 600
sleep 300
xcrun stapler staple $BUNDLE_DIR
xcrun stapler validate $BUNDLE_DIR
spctl -a -vvvv $BUNDLE_DIR || true
@@ -130,15 +130,15 @@ $QIF_BIN_DIR/binarycreator --offline-only -v -c $BUILD_DIR/installer/config/maco
if [ "${MAC_CERT_PW+x}" ]; then
echo "Signing installer bundle..."
security unlock-keychain -p $TEMP_PASS $KEYCHAIN
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "Developer ID Application: Privacy Technologies OU (X7UJ388FXK)" $INSTALLER_BUNDLE_DIR
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $INSTALLER_BUNDLE_DIR
/usr/bin/codesign --verify -vvvv $INSTALLER_BUNDLE_DIR || true
if [ "${NOTARIZE_APP+x}" ]; then
echo "Notarizing installer bundle..."
/usr/bin/ditto -c -k --keepParent $INSTALLER_BUNDLE_DIR $PROJECT_DIR/Installer_bundle_to_notarize.zip
xcrun altool --notarize-app -f $PROJECT_DIR/Installer_bundle_to_notarize.zip -t osx --primary-bundle-id "$APP_DOMAIN" -u "$APPLE_DEV_EMAIL" -p $APPLE_DEV_PASSWORD
xcrun notarytool submit $PROJECT_DIR/Installer_bundle_to_notarize.zip --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
rm $PROJECT_DIR/Installer_bundle_to_notarize.zip
sleep 600
sleep 300
xcrun stapler staple $INSTALLER_BUNDLE_DIR
xcrun stapler validate $INSTALLER_BUNDLE_DIR
spctl -a -vvvv $INSTALLER_BUNDLE_DIR || true
@@ -151,13 +151,13 @@ hdiutil create -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app
if [ "${MAC_CERT_PW+x}" ]; then
echo "Signing DMG installer..."
security unlock-keychain -p $TEMP_PASS $KEYCHAIN
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "Developer ID Application: Privacy Technologies OU (X7UJ388FXK)" $DMG_FILENAME
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $DMG_FILENAME
/usr/bin/codesign --verify -vvvv $DMG_FILENAME || true
if [ "${NOTARIZE_APP+x}" ]; then
echo "Notarizing DMG installer..."
xcrun altool --notarize-app -f $DMG_FILENAME -t osx --primary-bundle-id $APP_DOMAIN -u $APPLE_DEV_EMAIL -p $APPLE_DEV_PASSWORD
sleep 600
xcrun notarytool submit $DMG_FILENAME --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
sleep 300
xcrun stapler staple $DMG_FILENAME
xcrun stapler validate $DMG_FILENAME
fi