mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-30 07:10:51 +03:00
Compare commits
26 Commits
cryptpad-s
...
fixbug/ios
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deb9511b62 | ||
|
|
c66d8ecca0 | ||
|
|
db535f7e7d | ||
|
|
89f30d8c31 | ||
|
|
8bce432824 | ||
|
|
f3539b2632 | ||
|
|
7a96c212f3 | ||
|
|
2d5dc54e0f | ||
|
|
cef4c262e9 | ||
|
|
34309261a8 | ||
|
|
657eeb40c7 | ||
|
|
b4938c2cc9 | ||
|
|
524fefc5cb | ||
|
|
73f13404bb | ||
|
|
4188640c1d | ||
|
|
4ec20e9f37 | ||
|
|
ac61dd1498 | ||
|
|
2923fdaaf0 | ||
|
|
ff63cd24e5 | ||
|
|
bfa5fe4eb7 | ||
|
|
5fc68cca83 | ||
|
|
fcb7b8fa8d | ||
|
|
a81e32ff95 | ||
|
|
c897052107 | ||
|
|
4d0efc7ea5 | ||
|
|
a77842c9e3 |
15
.github/workflows/deploy.yml
vendored
15
.github/workflows/deploy.yml
vendored
@@ -238,14 +238,13 @@ jobs:
|
||||
IOS_APP_PROVISIONING_PROFILE: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }}
|
||||
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
|
||||
|
||||
# - name: 'Upload appstore .ipa and dSYMs to artifacts'
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: app-store ipa & dsyms
|
||||
# path: |
|
||||
# ${{ github.workspace }}/AmneziaVPN-iOS.ipa
|
||||
# ${{ github.workspace }}/*.app.dSYM.zip
|
||||
# retention-days: 7
|
||||
- name: 'Upload unsigned .ipa to artifacts'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN-unsigned-ipa
|
||||
path: |
|
||||
${{ github.workspace }}/build-ios/AmneziaVPN_unsigned.ipa
|
||||
retention-days: 7
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
|
||||
2
.github/workflows/tag-deploy.yml
vendored
2
.github/workflows/tag-deploy.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
# push:
|
||||
# tags:
|
||||
# - **
|
||||
# - "**"
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.10.0)
|
||||
set(AMNEZIAVPN_VERSION 4.8.11.0)
|
||||
|
||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 2093)
|
||||
set(APP_ANDROID_VERSION_CODE 2095)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -53,18 +53,8 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
|
||||
|
||||
AmneziaApplication::~AmneziaApplication()
|
||||
{
|
||||
if (m_vpnConnection) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectFromVpn", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteLater", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
m_vpnConnectionThread.quit();
|
||||
|
||||
if (!m_vpnConnectionThread.wait(5000)) {
|
||||
m_vpnConnectionThread.terminate();
|
||||
m_vpnConnectionThread.wait();
|
||||
}
|
||||
|
||||
if (m_engine) {
|
||||
QObject::disconnect(m_engine, 0, 0, 0);
|
||||
delete m_engine;
|
||||
|
||||
@@ -46,6 +46,7 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/AmneziaSceneDelegateHooks.mm
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -26,9 +26,8 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
|
||||
|
||||
initNotificationHandler();
|
||||
|
||||
auto locale = m_settings->getAppLanguage();
|
||||
m_translator.reset(new QTranslator());
|
||||
updateTranslator(locale);
|
||||
updateTranslator(m_settings->getAppLanguage());
|
||||
}
|
||||
|
||||
void CoreController::initModels()
|
||||
|
||||
@@ -32,17 +32,41 @@
|
||||
<false/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>AmneziaVPNLaunchScreen</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<true/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneClassName</key>
|
||||
<string>UIWindowScene</string>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>QIOSWindowSceneDelegate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array/>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array/>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Light</string>
|
||||
<key>com.wireguard.ios.app_group_id</key>
|
||||
|
||||
@@ -264,13 +264,13 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
|
||||
// && !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
|
||||
// && !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
|
||||
/* && !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined()
|
||||
@@ -278,27 +278,27 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
&& !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()) {
|
||||
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()*/) {
|
||||
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
||||
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
||||
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
||||
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
||||
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
||||
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
|
||||
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
|
||||
// json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
|
||||
// json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
|
||||
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
||||
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
|
||||
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
|
||||
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
|
||||
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
|
||||
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
|
||||
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
|
||||
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
|
||||
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
|
||||
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
|
||||
// json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
|
||||
// json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
|
||||
// json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
|
||||
// json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
|
||||
// json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
|
||||
// json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
|
||||
// json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
|
||||
// json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
|
||||
// json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
|
||||
}
|
||||
|
||||
write(json);
|
||||
|
||||
82
client/platforms/ios/AmneziaSceneDelegateHooks.mm
Normal file
82
client/platforms/ios/AmneziaSceneDelegateHooks.mm
Normal file
@@ -0,0 +1,82 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <objc/runtime.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
#include <QString>
|
||||
|
||||
#include "ios_controller.h"
|
||||
|
||||
using SceneOpenURLContexts = void (*)(id, SEL, UIScene *, NSSet<UIOpenURLContext *> *);
|
||||
|
||||
static SceneOpenURLContexts g_originalSceneOpenURLContexts = nullptr;
|
||||
|
||||
static void amnezia_handleURL(NSURL *url)
|
||||
{
|
||||
if (!url || !url.isFileURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString filePath(url.path.UTF8String);
|
||||
if (filePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (filePath.contains("backup")) {
|
||||
IosController::Instance()->importBackupFromOutside(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray data = file.readAll();
|
||||
IosController::Instance()->importConfigFromOutside(QString::fromUtf8(data));
|
||||
});
|
||||
}
|
||||
|
||||
static void amnezia_scene_openURLContexts(id self, SEL _cmd, UIScene *scene, NSSet<UIOpenURLContext *> *contexts)
|
||||
{
|
||||
if (g_originalSceneOpenURLContexts) {
|
||||
g_originalSceneOpenURLContexts(self, _cmd, scene, contexts);
|
||||
}
|
||||
|
||||
if (!contexts || contexts.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
for (UIOpenURLContext *context in contexts) {
|
||||
amnezia_handleURL(context.URL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@interface AmneziaSceneDelegateHooks : NSObject
|
||||
@end
|
||||
|
||||
@implementation AmneziaSceneDelegateHooks
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
Class cls = objc_getClass("QIOSWindowSceneDelegate");
|
||||
if (!cls) {
|
||||
return;
|
||||
}
|
||||
|
||||
SEL selector = @selector(scene:openURLContexts:);
|
||||
Method method = class_getInstanceMethod(cls, selector);
|
||||
if (method) {
|
||||
g_originalSceneOpenURLContexts = reinterpret_cast<SceneOpenURLContexts>(method_getImplementation(method));
|
||||
method_setImplementation(method, reinterpret_cast<IMP>(amnezia_scene_openURLContexts));
|
||||
} else {
|
||||
const char *types = "v@:@@";
|
||||
class_addMethod(cls, selector, reinterpret_cast<IMP>(amnezia_scene_openURLContexts), types);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -73,7 +73,7 @@ extension PacketTunnelProvider {
|
||||
startHandler = completionHandler
|
||||
ovpnAdapter?.connect(using: packetFlow)
|
||||
}
|
||||
|
||||
|
||||
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
|
||||
@@ -169,8 +169,20 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
||||
networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes
|
||||
networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes
|
||||
}
|
||||
}
|
||||
if splitTunnelType == 0 || splitTunnelType == nil {
|
||||
// Full tunnel: send all traffic via VPN
|
||||
if let ipv4Settings = networkSettings?.ipv4Settings {
|
||||
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
|
||||
NSLog("[Route] Added default IPv4 route (0.0.0.0/0)")
|
||||
}
|
||||
|
||||
if let ipv6Settings = networkSettings?.ipv6Settings {
|
||||
let ipv6DefaultRoute = NEIPv6Route(destinationAddress: "::", networkPrefixLength: 0)
|
||||
ipv6Settings.includedRoutes = [ipv6DefaultRoute]
|
||||
NSLog("[Route] Added default IPv6 route (::/0)")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set the network settings for the current tunneling session.
|
||||
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@@ -27,17 +27,49 @@ const char* MessageKey::isOnDemand = "is-on-demand";
|
||||
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
||||
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
||||
|
||||
#if !MACOS_NE
|
||||
static UIViewController* getViewController() {
|
||||
NSArray *windows = [[UIApplication sharedApplication]windows];
|
||||
for (UIWindow *window in windows) {
|
||||
if (window.isKeyWindow) {
|
||||
UIApplication *application = [UIApplication sharedApplication];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
for (UIScene *scene in application.connectedScenes) {
|
||||
if (scene.activationState != UISceneActivationStateForegroundActive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (![scene isKindOfClass:[UIWindowScene class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
||||
|
||||
for (UIWindow *window in windowScene.windows) {
|
||||
if (window.isKeyWindow && window.rootViewController) {
|
||||
return window.rootViewController;
|
||||
}
|
||||
}
|
||||
|
||||
for (UIWindow *window in windowScene.windows) {
|
||||
if (!window.isHidden && window.rootViewController) {
|
||||
return window.rootViewController;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (UIWindow *window in application.windows) {
|
||||
if (window.isKeyWindow && window.rootViewController) {
|
||||
return window.rootViewController;
|
||||
}
|
||||
}
|
||||
|
||||
for (UIWindow *window in application.windows) {
|
||||
if (window.rootViewController) {
|
||||
return window.rootViewController;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||
switch (status) {
|
||||
@@ -128,6 +160,39 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
|
||||
m_rawConfig = configuration;
|
||||
m_serverAddress = configuration.value(config_key::hostName).toString().toNSString();
|
||||
|
||||
if (proto == amnezia::Proto::OpenVpn) {
|
||||
QJsonObject ovpn = configuration["openvpn_config_data"].toObject();
|
||||
QString ovpnConfig = ovpn["config"].toString();
|
||||
QStringList unsupportedDirectives = {
|
||||
"resolv-retry",
|
||||
"persist-key",
|
||||
"persist-tun",
|
||||
"block-ipv6",
|
||||
"redirect-gateway"
|
||||
};
|
||||
|
||||
QStringList lines = ovpnConfig.split('\n');
|
||||
QStringList filteredLines;
|
||||
for (const QString &line : lines) {
|
||||
QString trimmedLine = line.trimmed();
|
||||
|
||||
bool shouldIgnore = false;
|
||||
for (const QString &bad : unsupportedDirectives) {
|
||||
if (trimmedLine.startsWith(bad)) {
|
||||
shouldIgnore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldIgnore) {
|
||||
filteredLines.append(line);
|
||||
}
|
||||
}
|
||||
ovpnConfig = filteredLines.join("\n");
|
||||
ovpn["config"] = ovpnConfig;
|
||||
m_rawConfig["openvpn_config_data"] = ovpn;
|
||||
}
|
||||
|
||||
QString tunnelName;
|
||||
if (configuration.value(config_key::description).toString().isEmpty()) {
|
||||
tunnelName = QString("%1 %2")
|
||||
@@ -251,21 +316,6 @@ void IosController::checkStatus()
|
||||
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
||||
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
||||
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
||||
|
||||
uint64_t last_handshake_time_sec = 0;
|
||||
#if !MACOS_NE
|
||||
if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) {
|
||||
last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue];
|
||||
} else {
|
||||
qDebug() << "Key last_handshake_time_sec is missing or null";
|
||||
}
|
||||
|
||||
if (last_handshake_time_sec < 0) {
|
||||
disconnectVpn();
|
||||
qDebug() << "Invalid handshake time, disconnecting VPN.";
|
||||
}
|
||||
#endif
|
||||
|
||||
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
||||
m_rxBytes = rxBytes;
|
||||
m_txBytes = txBytes;
|
||||
@@ -524,8 +574,6 @@ bool IosController::setupWireGuard()
|
||||
|
||||
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
||||
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
||||
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
|
||||
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
|
||||
|
||||
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
||||
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
||||
@@ -624,23 +672,11 @@ bool IosController::setupAwg()
|
||||
|
||||
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
||||
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
||||
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
|
||||
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
|
||||
|
||||
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
||||
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
||||
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
|
||||
|
||||
wgConfig.insert(config_key::specialJunk1, config[config_key::specialJunk1]);
|
||||
wgConfig.insert(config_key::specialJunk2, config[config_key::specialJunk2]);
|
||||
wgConfig.insert(config_key::specialJunk3, config[config_key::specialJunk3]);
|
||||
wgConfig.insert(config_key::specialJunk4, config[config_key::specialJunk4]);
|
||||
wgConfig.insert(config_key::specialJunk5, config[config_key::specialJunk5]);
|
||||
wgConfig.insert(config_key::controlledJunk1, config[config_key::controlledJunk1]);
|
||||
wgConfig.insert(config_key::controlledJunk2, config[config_key::controlledJunk2]);
|
||||
wgConfig.insert(config_key::controlledJunk3, config[config_key::controlledJunk3]);
|
||||
wgConfig.insert(config_key::specialHandshakeTimeout, config[config_key::specialHandshakeTimeout]);
|
||||
|
||||
QJsonDocument wgConfigDoc(wgConfig);
|
||||
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
||||
|
||||
@@ -820,14 +856,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
||||
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
||||
[sharingItems addObject:logFileUrl];
|
||||
}
|
||||
#if !MACOS_NE
|
||||
|
||||
UIViewController *qtController = getViewController();
|
||||
if (!qtController) return;
|
||||
|
||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||
#endif
|
||||
|
||||
__block bool isAccepted = false;
|
||||
#if !MACOS_NE
|
||||
|
||||
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||||
isAccepted = completed;
|
||||
emit finished();
|
||||
@@ -839,17 +875,15 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
||||
popController.sourceView = qtController.view;
|
||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
|
||||
return isAccepted;
|
||||
}
|
||||
|
||||
QString IosController::openFile() {
|
||||
#if !MACOS_NE
|
||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
||||
|
||||
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
||||
@@ -859,10 +893,9 @@ QString IosController::openFile() {
|
||||
if (!qtController) return;
|
||||
|
||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||
|
||||
#endif
|
||||
|
||||
__block QString filePath;
|
||||
#if !MACOS_NE
|
||||
|
||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||
if (path) {
|
||||
filePath = QString::fromUtf8(path.UTF8String);
|
||||
@@ -871,11 +904,11 @@ QString IosController::openFile() {
|
||||
}
|
||||
emit finished();
|
||||
};
|
||||
#endif
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
|
||||
519
client/server_scripts/check_server.sh
Executable file
519
client/server_scripts/check_server.sh
Executable file
@@ -0,0 +1,519 @@
|
||||
#!/bin/sh
|
||||
|
||||
LOG_DATE=$(date -u +'%Y%m%d-%H%M%S')
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
LOG_FILE="${SCRIPT_DIR}/server-diagnostics-${LOG_DATE}.log"
|
||||
|
||||
# Logging function (sh compatible)
|
||||
log_and_display() {
|
||||
if [ "$1" = "-n" ]; then
|
||||
shift
|
||||
printf "%s" "$*" | tee -a "$LOG_FILE"
|
||||
else
|
||||
echo "$1" | tee -a "$LOG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Redirect stderr to stdout for logging
|
||||
exec 2>&1
|
||||
|
||||
header() {
|
||||
log_and_display ""
|
||||
log_and_display "=== $1 ==="
|
||||
}
|
||||
|
||||
# Pause for cancellation
|
||||
log_and_display ""
|
||||
log_and_display "VPN Server Diagnostics will start in 9s. Press Ctrl+C to cancel."
|
||||
sleep 9
|
||||
|
||||
log_and_display ""
|
||||
header "STARTING VPN SERVER DIAGNOSTICS"
|
||||
log_and_display ""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 1. Basic system information
|
||||
# ------------------------------------------------------------------------------
|
||||
header "System Information"
|
||||
|
||||
# Uptime
|
||||
UPTIME_STR=$(awk '{printf "%d:%02d:%02d", int($1/3600), int(($1%3600)/60), int($1%60)}' /proc/uptime 2>/dev/null || echo "unknown")
|
||||
log_and_display "Uptime (H:M:S): $UPTIME_STR"
|
||||
|
||||
# Date/time UTC
|
||||
DATE_UTC=$(date -u +'%d %b %Y|%T' 2>/dev/null || echo "unknown")
|
||||
log_and_display "Date|Time (UTC): $DATE_UTC"
|
||||
|
||||
# Init system (PID 1)
|
||||
INIT_NAME=$(cat /proc/1/status 2>/dev/null | head -1 | awk '{print $2}' 2>/dev/null || echo "unknown")
|
||||
log_and_display "Init system (PID 1): $INIT_NAME"
|
||||
|
||||
# Locale
|
||||
if echo "$LANG" | grep -E '^(en_US.UTF-8|C.UTF-8|C)$' >/dev/null 2>&1; then
|
||||
log_and_display "Locale: $LANG"
|
||||
else
|
||||
log_and_display "Locale: $LANG (not en_US.UTF-8, C.UTF-8 or C)"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 2. Package manager detection
|
||||
# ------------------------------------------------------------------------------
|
||||
header "Package Manager Information"
|
||||
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
log_and_display "Package Manager: APT"
|
||||
PM="apt-get"
|
||||
PM_VER_OPT="--version"
|
||||
DOCKER_PKG="docker.io"
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
log_and_display "Package Manager: DNF"
|
||||
PM="dnf"
|
||||
PM_VER_OPT="--version"
|
||||
DOCKER_PKG="docker"
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
log_and_display "Package Manager: YUM"
|
||||
PM="yum"
|
||||
PM_VER_OPT="--version"
|
||||
DOCKER_PKG="docker"
|
||||
elif command -v zypper >/dev/null 2>&1; then
|
||||
log_and_display "Package Manager: ZYPPER"
|
||||
PM="zypper"
|
||||
PM_VER_OPT="--version"
|
||||
DOCKER_PKG="docker"
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
log_and_display "Package Manager: PACMAN"
|
||||
PM="pacman"
|
||||
PM_VER_OPT="--version"
|
||||
DOCKER_PKG="docker"
|
||||
elif command -v opkg >/dev/null 2>&1; then
|
||||
log_and_display "Package Manager: OPKG - Not supported on this platform"
|
||||
PM="opkg"
|
||||
PM_VER_OPT="--version"
|
||||
DOCKER_PKG="docker"
|
||||
else
|
||||
log_and_display "Package Manager: Unknown"
|
||||
# fallback
|
||||
PM="uname"
|
||||
PM_VER_OPT="-a"
|
||||
DOCKER_PKG="docker"
|
||||
fi
|
||||
|
||||
# Check package versions
|
||||
log_and_display ""
|
||||
log_and_display "Package versions:"
|
||||
|
||||
# Check sudo
|
||||
if [ "$PM" = "apt-get" ]; then
|
||||
sudo_version=$(dpkg -s "sudo" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
|
||||
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
|
||||
sudo_version=$(rpm -q "sudo" 2>/dev/null || echo "not installed")
|
||||
elif [ "$PM" = "pacman" ]; then
|
||||
sudo_version=$(pacman -Q "sudo" 2>/dev/null || echo "not installed")
|
||||
elif [ "$PM" = "opkg" ]; then
|
||||
sudo_version=$(opkg info "sudo" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
|
||||
else
|
||||
sudo_version="unknown"
|
||||
fi
|
||||
log_and_display " sudo: $sudo_version"
|
||||
|
||||
# Check Docker package
|
||||
if [ "$PM" = "apt-get" ]; then
|
||||
docker_pkg_version=$(dpkg -s "$DOCKER_PKG" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
|
||||
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
|
||||
docker_pkg_version=$(rpm -q "$DOCKER_PKG" 2>/dev/null || echo "not installed")
|
||||
elif [ "$PM" = "pacman" ]; then
|
||||
docker_pkg_version=$(pacman -Q "$DOCKER_PKG" 2>/dev/null || echo "not installed")
|
||||
elif [ "$PM" = "opkg" ]; then
|
||||
docker_pkg_version=$(opkg info "$DOCKER_PKG" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
|
||||
else
|
||||
docker_pkg_version="unknown"
|
||||
fi
|
||||
log_and_display " $DOCKER_PKG: $docker_pkg_version"
|
||||
|
||||
# Check lsof
|
||||
if [ "$PM" = "apt-get" ]; then
|
||||
lsof_version=$(dpkg -s "lsof" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
|
||||
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
|
||||
lsof_version=$(rpm -q "lsof" 2>/dev/null || echo "not installed")
|
||||
elif [ "$PM" = "pacman" ]; then
|
||||
lsof_version=$(pacman -Q "lsof" 2>/dev/null || echo "not installed")
|
||||
elif [ "$PM" = "opkg" ]; then
|
||||
lsof_version=$(opkg info "lsof" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
|
||||
else
|
||||
lsof_version="unknown"
|
||||
fi
|
||||
log_and_display " lsof: $lsof_version"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 3. Additional system information (hostnamectl / /proc/version)
|
||||
# ------------------------------------------------------------------------------
|
||||
header "OS / Kernel Information"
|
||||
|
||||
if command -v hostnamectl >/dev/null 2>&1; then
|
||||
hostnamectl 2>/dev/null | grep -E 'Operating System:|Virtualization:|Kernel:|Architecture:' | sed 's/^[ \t]*//;s/:/: /' | while read line; do
|
||||
log_and_display " $line"
|
||||
done
|
||||
else
|
||||
log_and_display "Operating System: $(cat /proc/version 2>/dev/null || echo 'unknown')"
|
||||
fi
|
||||
|
||||
# CPU threads
|
||||
CPU_THREADS=$(nproc 2>/dev/null || grep -c "^processor" /proc/cpuinfo 2>/dev/null || echo "unknown")
|
||||
log_and_display " CPU threads: $CPU_THREADS"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 4. Memory (RAM) check
|
||||
# ------------------------------------------------------------------------------
|
||||
header "Memory Information"
|
||||
|
||||
if command -v free >/dev/null 2>&1; then
|
||||
# Remove extra spaces in header
|
||||
free -h 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
|
||||
elif command -v vmstat >/dev/null 2>&1; then
|
||||
vmstat -S M -s 2>/dev/null | grep -iE 'total memory|total swap' | sed 's/ *//' | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
|
||||
else
|
||||
grep -iE 'MemTotal|SwapTotal' /proc/meminfo 2>/dev/null | sed 's/ \+/ /' | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
|
||||
fi
|
||||
|
||||
if command -v free >/dev/null 2>&1; then
|
||||
log_and_display ""
|
||||
log_and_display "Detailed Memory Info:"
|
||||
free -h 2>/dev/null | awk 'NR==2{printf " Used: %s / %s (%.1f%%)\n", $3, $2, $3/$2*100}' 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error calculating memory usage"
|
||||
free -h 2>/dev/null | awk 'NR==3{printf " Swap: %s / %s (%.1f%%)\n", $3, $2, $2>0 ? $3/$2*100 : 0}' 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error calculating swap usage"
|
||||
fi
|
||||
|
||||
# Disk usage
|
||||
header "Disk Usage"
|
||||
df -h 2>/dev/null | awk '
|
||||
BEGIN {print " Filesystem Size Used Avail Use% Mounted"}
|
||||
NR>1 {printf " %-10s %5s %5s %5s %4s %s\n", $1, $2, $3, $4, $5, $6}' | tee -a "$LOG_FILE" || log_and_display " Error getting disk usage"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 5. Current user and sudo check
|
||||
# ------------------------------------------------------------------------------
|
||||
header "User Check"
|
||||
|
||||
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///')
|
||||
USER_GROUP=$(groups "$CUR_USER" 2>/dev/null || echo "")
|
||||
USER_GOOD=0
|
||||
|
||||
log_and_display -n "Current user: $CUR_USER => "
|
||||
|
||||
if [ "$CUR_USER" = "root" ]; then
|
||||
log_and_display "passed.. (is root)"
|
||||
USER_GOOD="r" # root
|
||||
else
|
||||
if echo "$USER_GROUP" | grep -qE '(^|[[:space:]])sudo($|[[:space:]])'; then
|
||||
log_and_display "passed.. (in sudo group)"
|
||||
USER_GOOD=1
|
||||
elif echo "$USER_GROUP" | grep -qE '(^|[[:space:]])wheel($|[[:space:]])'; then
|
||||
log_and_display "passed.. (in wheel group)"
|
||||
USER_GOOD=1
|
||||
elif echo "$USER_GROUP" | grep -qE '(^|[[:space:]])docker($|[[:space:]])'; then
|
||||
log_and_display "failed.. (only in docker group)"
|
||||
USER_GOOD="d"
|
||||
else
|
||||
log_and_display "failed.. (not a member of the sudo or wheel groups)"
|
||||
USER_GOOD=0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if password is required for sudo
|
||||
if [ "$USER_GOOD" = "0" ] || [ "$USER_GOOD" = "d" ]; then
|
||||
log_and_display -n "Passwd request: "
|
||||
log_and_display "check skipped (not sudoer)"
|
||||
else
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
# Try sudo without password - more thorough check
|
||||
PASSWD_REQUEST=$(sudo -K 2>&1 && sudo -nu $CUR_USER $PM $PM_VER_OPT 2>&1 >/dev/null && sudo -n $PM $PM_VER_OPT 2>&1 >/dev/null)
|
||||
if [ -n "$PASSWD_REQUEST" ]; then
|
||||
USER_GOOD=0
|
||||
log_and_display -n "Passwd request: "
|
||||
log_and_display "failed.. ($PASSWD_REQUEST)" \
|
||||
| sed "s/$CUR_USER/User/g;s/$(hostname 2>/dev/null || echo 'Server')/Server/g;s/ user / /g"
|
||||
else
|
||||
log_and_display -n "Passwd request: "
|
||||
log_and_display "passed.. (not required)"
|
||||
fi
|
||||
else
|
||||
if [ "$USER_GOOD" = "r" ]; then
|
||||
log_and_display -n "Passwd request: "
|
||||
log_and_display "check skipped (sudo not installed, but root user)"
|
||||
else
|
||||
log_and_display "Warning! The sudo package must be pre-installed!"
|
||||
USER_GOOD=0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Home directory check
|
||||
log_and_display -n "Home dir: "
|
||||
if cd ~ 2>/dev/null; then
|
||||
log_and_display "passed.. (accessible)"
|
||||
else
|
||||
log_and_display "failed.. (not accessible)"
|
||||
fi
|
||||
log_and_display "Default shell: $SHELL"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 6. Important components check (sudo, lsof, fuser, apparmor)
|
||||
# ------------------------------------------------------------------------------
|
||||
header "Component Checks"
|
||||
|
||||
log_and_display -n " sudo: "
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
log_and_display "passed.. (installed)"
|
||||
else
|
||||
log_and_display "not installed"
|
||||
fi
|
||||
|
||||
log_and_display -n " lsof: "
|
||||
if command -v lsof >/dev/null 2>&1; then
|
||||
log_and_display "passed.. (installed)"
|
||||
else
|
||||
log_and_display "not installed"
|
||||
fi
|
||||
|
||||
log_and_display -n " fuser: "
|
||||
if command -v fuser >/dev/null 2>&1; then
|
||||
log_and_display "passed.. (installed)"
|
||||
else
|
||||
log_and_display "psmisc not installed"
|
||||
fi
|
||||
|
||||
log_and_display -n "apparmor: "
|
||||
AA_ENABLED=$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null || echo "N")
|
||||
if [ "$AA_ENABLED" = "Y" ]; then
|
||||
if command -v apparmor_parser >/dev/null 2>&1; then
|
||||
log_and_display "passed.. (used)"
|
||||
else
|
||||
log_and_display "failed.. (installation required)"
|
||||
fi
|
||||
else
|
||||
if command -v apparmor_parser >/dev/null 2>&1; then
|
||||
log_and_display "passed.. (not used)"
|
||||
else
|
||||
log_and_display "passed.. (not required)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 7. SELinux check
|
||||
# ------------------------------------------------------------------------------
|
||||
header "SELinux Check"
|
||||
|
||||
if command -v getenforce >/dev/null 2>&1; then
|
||||
SELINUX_STATUS=$(getenforce 2>/dev/null || echo "unknown")
|
||||
if [ "$SELINUX_STATUS" = "Enforcing" ]; then
|
||||
log_and_display "SELinux status: $SELINUX_STATUS (strict mode)"
|
||||
elif [ "$SELINUX_STATUS" = "Permissive" ]; then
|
||||
log_and_display "SELinux status: $SELINUX_STATUS (permissive mode)"
|
||||
else
|
||||
log_and_display "SELinux status: $SELINUX_STATUS (disabled)"
|
||||
fi
|
||||
else
|
||||
log_and_display "SELinux: not found (or not applicable)"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 8. Docker + Docker/Podman service check
|
||||
# ------------------------------------------------------------------------------
|
||||
header "Docker / Podman Status"
|
||||
CHECK_CONTAINERS=0
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
log_and_display "Docker: $DOCKER_PKG not installed"
|
||||
else
|
||||
# If user is in sudoers, use sudo without password
|
||||
if [ "$USER_GOOD" = "1" ]; then
|
||||
SUD="sudo -n"
|
||||
elif [ "$USER_GOOD" = "r" ]; then
|
||||
SUD="" # root
|
||||
else
|
||||
SUD=""
|
||||
fi
|
||||
|
||||
DOCKER_VERSION=$($SUD docker -v 2>/dev/null || echo 'docker -v error')
|
||||
log_and_display "Installed: $DOCKER_VERSION"
|
||||
|
||||
# Check for podman
|
||||
if echo "$DOCKER_VERSION" | grep -qi "podman"; then
|
||||
log_and_display " WARNING: Podman detected - not supported at the moment!"
|
||||
log_and_display " Podman (podman-docker) is not supported and is installed by mistake"
|
||||
docker_service="podman.socket"
|
||||
else
|
||||
docker_service="docker.service"
|
||||
fi
|
||||
log_and_display " service: $docker_service"
|
||||
|
||||
# Check status
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
docker_status=$(systemctl is-active "$docker_service" 2>/dev/null || echo "unknown")
|
||||
docker_loading=$(systemctl is-enabled "$docker_service" 2>/dev/null || echo "unknown")
|
||||
else
|
||||
docker_status="unknown (systemctl not found)"
|
||||
docker_loading="unknown"
|
||||
fi
|
||||
|
||||
if [ "$docker_status" = "active" ]; then
|
||||
log_and_display " status: passed.. ($docker_status)"
|
||||
CHECK_CONTAINERS=1
|
||||
else
|
||||
log_and_display " status: incorrect.. ($docker_status)"
|
||||
CHECK_CONTAINERS=0
|
||||
fi
|
||||
|
||||
if [ "$docker_loading" = "enabled" ]; then
|
||||
log_and_display " loading: good (startup $docker_loading)"
|
||||
else
|
||||
log_and_display " loading: bad (startup $docker_loading)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 9. Docker pull test + container check with improved Docker Hub verification
|
||||
# ------------------------------------------------------------------------------
|
||||
header "Docker Hub: pull hello-world test"
|
||||
|
||||
if [ "$CHECK_CONTAINERS" = "1" ] && [ "$USER_GOOD" != "0" ]; then
|
||||
# First check Docker Hub availability
|
||||
log_and_display "Checking Docker Hub connectivity..."
|
||||
|
||||
# Try to execute docker pull with timeout
|
||||
if timeout 30 $SUD docker pull docker.io/library/hello-world >/dev/null 2>&1; then
|
||||
log_and_display "Docker Hub: available"
|
||||
|
||||
# Start container for testing
|
||||
if $SUD docker run --rm docker.io/library/hello-world >/dev/null 2>&1; then
|
||||
log_and_display "Hello-world container: successfully started and completed"
|
||||
else
|
||||
log_and_display "Hello-world container: startup error"
|
||||
fi
|
||||
else
|
||||
log_and_display "Docker Hub: unavailable or blocked (possibly exceeded download limit)"
|
||||
log_and_display "Docker Hub has download limits, try again later"
|
||||
fi
|
||||
|
||||
log_and_display ""
|
||||
total_cont=$($SUD docker ps -aq 2>/dev/null | wc -l || echo "0")
|
||||
active_cont=$($SUD docker ps -q 2>/dev/null | wc -l || echo "0")
|
||||
amnezia_cont=$($SUD docker ps -a 2>/dev/null | grep -c amnezia || echo "0")
|
||||
|
||||
log_and_display "Containers check: Total $total_cont / Active $active_cont / Amnezia $amnezia_cont"
|
||||
$SUD docker ps -a --format "{{.Names}} ({{.Image}}) ({{.Status}}) ({{.Ports}})" 2>/dev/null | grep amnezia || true
|
||||
|
||||
# Peers check
|
||||
if $SUD docker ps 2>/dev/null | grep -qE '\<(amnezia-awg|amnezia-wireguard)\>'; then
|
||||
log_and_display ""
|
||||
log_and_display "Peers check (beta):"
|
||||
if $SUD docker ps 2>/dev/null | grep -q amnezia-awg; then
|
||||
AMNEZIA_WG_CONTAINER=$($SUD docker ps 2>/dev/null | grep amnezia-awg | awk '{print $1}' | head -1)
|
||||
if [ -n "$AMNEZIA_WG_CONTAINER" ]; then
|
||||
WG_PEERS=$($SUD docker exec -it "$AMNEZIA_WG_CONTAINER" wg show 2>/dev/null | grep -c 'peer' || echo "0")
|
||||
log_and_display "AmneziaWG peers: $WG_PEERS"
|
||||
fi
|
||||
fi
|
||||
if $SUD docker ps 2>/dev/null | grep -q amnezia-wireguard; then
|
||||
WIREGUARD_CONTAINER=$($SUD docker ps 2>/dev/null | grep amnezia-wireguard | awk '{print $1}' | head -1)
|
||||
if [ -n "$WIREGUARD_CONTAINER" ]; then
|
||||
WG_PEERS=$($SUD docker exec -it "$WIREGUARD_CONTAINER" wg show 2>/dev/null | grep -c 'peer' || echo "0")
|
||||
log_and_display "WireGuard peers: $WG_PEERS"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_and_display "skipped.."
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 10. Additional improvements
|
||||
# ------------------------------------------------------------------------------
|
||||
#
|
||||
# 10.1. CPU and memory load check (Load average, top processes)
|
||||
#
|
||||
header "CPU & Memory usage (top)"
|
||||
|
||||
# Load average (last 1,5,15 minutes)
|
||||
LOAD_AVG=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' || echo "unknown")
|
||||
log_and_display "Load average: $LOAD_AVG"
|
||||
|
||||
log_and_display ""
|
||||
log_and_display "Top 5 processes by CPU:"
|
||||
ps aux 2>/dev/null | sort -k3 -nr | head -n 6 | awk '{printf "%s %s %s %s %s\n", $1,$2,$3"%",$4"%",$11}' | column -t 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting CPU processes"
|
||||
|
||||
log_and_display ""
|
||||
log_and_display "Top 5 processes by MEM:"
|
||||
ps aux 2>/dev/null | sort -k4 -nr | head -n 6 | awk '{printf "%s %s %s %s %s\n", $1,$2,$3"%",$4"%",$11}' | column -t 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting MEM processes"
|
||||
|
||||
# 10.2. System logs check (latest critical messages)
|
||||
header "Last 10 critical/error messages (journalctl)"
|
||||
|
||||
if command -v journalctl >/dev/null 2>&1; then
|
||||
journalctl -p 3 -n 10 --no-pager 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting system logs"
|
||||
else
|
||||
log_and_display "journalctl not found (non-systemd system?)"
|
||||
fi
|
||||
|
||||
# 10.3. System package versions check (examples)
|
||||
|
||||
# Open ports check
|
||||
header "Network Ports Check"
|
||||
if command -v netstat >/dev/null 2>&1; then
|
||||
log_and_display "Listening ports:"
|
||||
netstat -tlnp 2>/dev/null | grep LISTEN | head -10 | while read line; do
|
||||
log_and_display " $line"
|
||||
done
|
||||
elif command -v ss >/dev/null 2>&1; then
|
||||
log_and_display "Listening ports:"
|
||||
ss -tlnp 2>/dev/null | head -10 | while read line; do
|
||||
log_and_display " $line"
|
||||
done
|
||||
else
|
||||
log_and_display "netstat/ss not found"
|
||||
fi
|
||||
|
||||
# SSH check
|
||||
header "SSH Service Check"
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
ssh_status=$(systemctl is-active ssh 2>/dev/null || systemctl is-active sshd 2>/dev/null || echo "not found")
|
||||
if [ "$ssh_status" = "active" ]; then
|
||||
log_and_display "SSH service: $ssh_status"
|
||||
else
|
||||
log_and_display "SSH service: $ssh_status"
|
||||
fi
|
||||
else
|
||||
log_and_display "systemctl not found"
|
||||
fi
|
||||
|
||||
# Time check
|
||||
header "Time Synchronization"
|
||||
if command -v timedatectl >/dev/null 2>&1; then
|
||||
timedatectl status 2>/dev/null | grep -E "System clock|NTP service" | while read line; do
|
||||
log_and_display " $line"
|
||||
done
|
||||
else
|
||||
log_and_display " System time: $(date 2>/dev/null || echo 'unknown')"
|
||||
fi
|
||||
|
||||
# Kernel check
|
||||
header "Kernel Information"
|
||||
log_and_display "Kernel version: $(uname -r 2>/dev/null || echo 'unknown')"
|
||||
log_and_display "Kernel architecture: $(uname -m 2>/dev/null || echo 'unknown')"
|
||||
if [ -f /proc/cmdline ]; then
|
||||
log_and_display "Kernel parameters:"
|
||||
cat /proc/cmdline 2>/dev/null | tr ' ' '\n' | head -5 | while read param; do
|
||||
log_and_display " $param"
|
||||
done
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Completion
|
||||
# ------------------------------------------------------------------------------
|
||||
log_and_display ""
|
||||
header "FINISH"
|
||||
log_and_display ""
|
||||
log_and_display "Diagnostics completed. Log saved to: $LOG_FILE"
|
||||
log_and_display ""
|
||||
|
||||
# Variable cleanup
|
||||
pm="" && opt="" && docker_pkg="" && CUR_USER="" && USER_GOOD="" && USER_GROUP="" && PASSWD_REQUEST="" && CHECK_CONTAINERS="" && SUD="" && docker_service="" && docker_status="" && docker_loading=""
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
|
||||
QLocale getAppLanguage()
|
||||
{
|
||||
QString localeStr = m_settings.value("Conf/appLanguage").toString();
|
||||
QString localeStr = m_settings.value("Conf/appLanguage", QLocale::system().name()).toString();
|
||||
return QLocale(localeStr);
|
||||
};
|
||||
void setAppLanguage(QLocale locale)
|
||||
|
||||
@@ -274,7 +274,7 @@ void ImportController::processNativeWireGuardConfig()
|
||||
auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::WireGuard)).toObject();
|
||||
auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||
|
||||
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
|
||||
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
|
||||
QString junkPacketMinSize = QString::number(10);
|
||||
QString junkPacketMaxSize = QString::number(50);
|
||||
clientProtocolConfig[config_key::junkPacketCount] = junkPacketCount;
|
||||
|
||||
@@ -73,7 +73,7 @@ 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(2, 5));
|
||||
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
|
||||
QString junkPacketMinSize = QString::number(10);
|
||||
QString junkPacketMaxSize = QString::number(50);
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ void PageController::onShowErrorMessage(ErrorCode errorCode)
|
||||
{
|
||||
const auto fullErrorMessage = errorString(errorCode);
|
||||
const auto errorMessage = fullErrorMessage.mid(fullErrorMessage.indexOf(". ") + 1); // remove ErrorCode %1.
|
||||
const auto errorUrl = QStringLiteral("https://docs.amnezia.org/troubleshooting/error-codes/#error-%1-%2").arg(static_cast<int>(errorCode)).arg(utils::enumToString(errorCode).toLower());
|
||||
const auto errorUrl = QStringLiteral("troubleshooting/error-codes/#error-%1-%2").arg(static_cast<int>(errorCode)).arg(utils::enumToString(errorCode).toLower());
|
||||
const auto fullMessage = QStringLiteral("<a href=\"%1\" style=\"color: #FBB26A;\">ErrorCode: %2</a>. %3").arg(errorUrl).arg(static_cast<int>(errorCode)).arg(errorMessage);
|
||||
|
||||
emit showErrorMessage(fullMessage);
|
||||
|
||||
@@ -151,6 +151,7 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||
config["Conf/autoStart"] = Autostart::isAutostart();
|
||||
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
|
||||
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
|
||||
config["Conf/useAmneziaDns"] = isAmneziaDnsEnabled();
|
||||
|
||||
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
|
||||
}
|
||||
@@ -186,7 +187,8 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
|
||||
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
|
||||
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toString().toLower() == "true";
|
||||
bool appSplittunnelingEnabled =
|
||||
newConfigData.value("Conf/appsSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
|
||||
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
@@ -198,12 +200,13 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||
m_appSplitTunnelingModel->clearAppsList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
|
||||
#endif
|
||||
|
||||
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
|
||||
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toString().toLower() == "true";
|
||||
bool siteSplittunnelingEnabled =
|
||||
newConfigData.value("Conf/sitesSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
|
||||
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
||||
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
||||
|
||||
@@ -214,6 +217,11 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||
m_settings->setStrictKillSwitchEnabled(false);
|
||||
#endif
|
||||
|
||||
bool amneziaDnsEnabled = newConfigData.contains("Conf/useAmneziaDns")
|
||||
? newConfigData.value("Conf/useAmneziaDns").toBool()
|
||||
: m_settings->useAmneziaDns();
|
||||
emit amneziaDnsToggled(amneziaDnsEnabled);
|
||||
|
||||
emit restoreBackupFinished();
|
||||
} else {
|
||||
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
|
||||
|
||||
@@ -173,6 +173,7 @@ void ServersModel::resetModel()
|
||||
m_servers = m_settings->serversArray();
|
||||
m_defaultServerIndex = m_settings->defaultServerIndex();
|
||||
m_processedServerIndex = m_defaultServerIndex;
|
||||
m_isAmneziaDnsEnabled = m_settings->useAmneziaDns();
|
||||
endResetModel();
|
||||
emit defaultServerIndexChanged(m_defaultServerIndex);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ Menu {
|
||||
MenuItem {
|
||||
text: qsTr("&Paste")
|
||||
shortcut: StandardKey.Paste
|
||||
// Fix calling paste from clipboard when launching app on android
|
||||
enabled: Qt.platform.os === "android" ? true : textObj.canPaste
|
||||
// Fix calling paste from clipboard when launching app on android/ios
|
||||
enabled: (Qt.platform.os === "android" || Qt.platform.os === "ios") ? true : textObj.canPaste
|
||||
onTriggered: textObj.paste()
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ Popup {
|
||||
Layout.fillWidth: true
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentDocsUrl(link))
|
||||
}
|
||||
|
||||
text: root.text
|
||||
|
||||
@@ -37,6 +37,22 @@ Item {
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -6,6 +6,8 @@ import Qt.labs.platform 1.1
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
@@ -17,6 +19,33 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property var processedServer
|
||||
|
||||
Connections {
|
||||
target: ServersModel
|
||||
|
||||
function onProcessedServerChanged() {
|
||||
root.processedServer = proxyServersModel.get(0)
|
||||
}
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: proxyServersModel
|
||||
objectName: "proxyServersModel"
|
||||
|
||||
sourceModel: ServersModel
|
||||
filters: [
|
||||
ValueFilter {
|
||||
roleName: "isCurrentlyProcessed"
|
||||
value: true
|
||||
}
|
||||
]
|
||||
|
||||
Component.onCompleted: {
|
||||
root.processedServer = proxyServersModel.get(0)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.prepareVpnKeyExport()
|
||||
@@ -40,7 +69,7 @@ PageType {
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
text: qsTr("Amnezia Premium\nsubscription key")
|
||||
text: qsTr(root.processedServer.name + "\nsubscription key")
|
||||
font.pixelSize: 32
|
||||
font.bold: true
|
||||
color: AmneziaStyle.color.paleGray
|
||||
@@ -56,7 +85,7 @@ PageType {
|
||||
text: qsTr("Copy key")
|
||||
leftImageSource: "qrc:/images/controls/copy.svg"
|
||||
|
||||
onClicked: {
|
||||
clickedFunc: function() {
|
||||
ApiConfigsController.copyVpnKeyToClipboard()
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
@@ -77,13 +106,13 @@ PageType {
|
||||
text: qsTr("Save key as a file")
|
||||
leftImageSource: "qrc:/images/controls/share-2.svg"
|
||||
|
||||
onClicked: {
|
||||
clickedFunc: function() {
|
||||
var fileName = GC.isMobile()
|
||||
? "amnezia_vpn_key.vpn"
|
||||
? root.processedServer.name.toLowerCase().replace(/\s+/g, "_") + "_key.vpn"
|
||||
: SystemController.getFileName(
|
||||
qsTr("Save AmneziaVPN config"),
|
||||
qsTr("Config files (*.vpn)"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_vpn_key",
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + root.processedServer.name.toLowerCase().replace(/\s+/g, "_") + "_key",
|
||||
true,
|
||||
".vpn"
|
||||
)
|
||||
@@ -110,7 +139,7 @@ PageType {
|
||||
text: qsTr("Show key text")
|
||||
leftImageSource: "qrc:/images/controls/eye.svg"
|
||||
|
||||
onClicked: {
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.prepareVpnKeyExport()
|
||||
PageController.showBusyIndicator(false)
|
||||
@@ -177,7 +206,7 @@ PageType {
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Amnezia Premium Subscription key")
|
||||
headerText: qsTr(root.processedServer.name + " Subscription key")
|
||||
}
|
||||
|
||||
TextArea {
|
||||
|
||||
@@ -145,11 +145,25 @@ PageType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WarningType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
textString: qsTr("Only \"Apps from the list should not have access via VPN\" mode is available on Windows")
|
||||
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||
|
||||
enabled: (Qt.platform.os === "windows") && root.pageEnabled
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: addAppButton.top
|
||||
anchors.left: parent.left
|
||||
|
||||
@@ -164,6 +164,8 @@ PageType {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.bottom: addSiteButton.top
|
||||
|
||||
@@ -603,12 +603,14 @@ PageType {
|
||||
visible: accessTypeSelector.currentIndex === 1
|
||||
|
||||
property bool isFocusable: true
|
||||
property bool freezeFilter: false
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyClientManagementModel
|
||||
sourceModel: ClientManagementModel
|
||||
filters: RegExpFilter {
|
||||
roleName: "clientName"
|
||||
enabled: !clientsListView.freezeFilter
|
||||
pattern: ".*" + searchTextField.textField.text + ".*"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
@@ -791,12 +793,14 @@ PageType {
|
||||
}
|
||||
|
||||
if (clientNameEditor.textField.text !== clientName) {
|
||||
clientsListView.freezeFilter = true
|
||||
PageController.showBusyIndicator(true)
|
||||
ExportController.renameClient(proxyClientManagementModel.mapToSource(index),
|
||||
clientNameEditor.textField.text,
|
||||
ContainersModel.getProcessedContainerIndex(),
|
||||
ServersModel.getProcessedServerCredentials())
|
||||
PageController.showBusyIndicator(false)
|
||||
Qt.callLater(function(){ clientsListView.freezeFilter = false })
|
||||
clientNameEditDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,66 +34,23 @@ clang -v
|
||||
# Generate XCodeProj
|
||||
$QT_BIN_DIR/qt-cmake . -B $BUILD_DIR -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR -DDEPLOY=ON
|
||||
|
||||
KEYCHAIN=amnezia.build.ios.keychain
|
||||
KEYCHAIN_FILE=$HOME/Library/Keychains/${KEYCHAIN}-db
|
||||
|
||||
# Setup keychain
|
||||
if [ "${IOS_SIGNING_CERT_BASE64+x}" ]; then
|
||||
echo "Import certificate"
|
||||
cd $BUILD_DIR
|
||||
xcodebuild archive \
|
||||
-project AmneziaVPN.xcodeproj \
|
||||
-scheme AmneziaVPN \
|
||||
-configuration Release \
|
||||
-archivePath ./build/AmneziaVPN.xcarchive \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
CODE_SIGN_IDENTITY="" \
|
||||
CODE_SIGNING_REQUIRED=NO
|
||||
|
||||
TRUST_CERT_CER=$BUILD_DIR/trust-cert.cer
|
||||
SIGNING_CERT_P12=$BUILD_DIR/signing-cert.p12
|
||||
mkdir -p Payload
|
||||
|
||||
echo $IOS_TRUST_CERT_BASE64 | base64 --decode > $TRUST_CERT_CER
|
||||
echo $IOS_SIGNING_CERT_BASE64 | base64 --decode > $SIGNING_CERT_P12
|
||||
cp -R ./build/AmneziaVPN.xcarchive/Products/Applications/AmneziaVPN.app Payload/
|
||||
|
||||
shasum -a 256 $TRUST_CERT_CER
|
||||
shasum -a 256 $SIGNING_CERT_P12
|
||||
zip -r AmneziaVPN_unsigned.ipa Payload
|
||||
|
||||
KEYCHAIN_PASS=$IOS_SIGNING_CERT_PASSWORD
|
||||
rm -rf Payload
|
||||
|
||||
security create-keychain -p $KEYCHAIN_PASS $KEYCHAIN || true
|
||||
security default-keychain -s $KEYCHAIN
|
||||
security unlock-keychain -p $KEYCHAIN_PASS $KEYCHAIN
|
||||
|
||||
security default-keychain
|
||||
security list-keychains
|
||||
|
||||
security import $TRUST_CERT_CER -k $KEYCHAIN -P "" -T /usr/bin/codesign
|
||||
security import $SIGNING_CERT_P12 -k $KEYCHAIN -P $IOS_SIGNING_CERT_PASSWORD -T /usr/bin/codesign
|
||||
|
||||
security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k $KEYCHAIN_PASS $KEYCHAIN
|
||||
security find-identity -p codesigning
|
||||
security set-keychain-settings $KEYCHAIN_FILE
|
||||
security set-keychain-settings -t 3600 $KEYCHAIN_FILE
|
||||
security unlock-keychain -p $KEYCHAIN_PASS $KEYCHAIN_FILE
|
||||
|
||||
# Copy provisioning prifiles
|
||||
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles/"
|
||||
|
||||
echo $IOS_APP_PROVISIONING_PROFILE | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision
|
||||
echo $IOS_NE_PROVISIONING_PROFILE | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision
|
||||
|
||||
shasum -a 256 ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision
|
||||
shasum -a 256 ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision
|
||||
|
||||
profile_uuid=`grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision | grep -io "[-A-F0-9]\{36\}"`
|
||||
profile_ne_uuid=`grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision | grep -io "[-A-F0-9]\{36\}"`
|
||||
|
||||
mv ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$profile_uuid.mobileprovision
|
||||
mv ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$profile_ne_uuid.mobileprovision
|
||||
else
|
||||
echo "Failed to import certificate, aborting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build project
|
||||
xcodebuild \
|
||||
"OTHER_CODE_SIGN_FLAGS=--keychain '$KEYCHAIN_FILE'" \
|
||||
-configuration Release \
|
||||
-scheme AmneziaVPN \
|
||||
-destination "generic/platform=iOS,name=Any iOS'" \
|
||||
-project $BUILD_DIR/AmneziaVPN.xcodeproj
|
||||
|
||||
# restore keychain
|
||||
security default-keychain -s login.keychain
|
||||
echo " Build setup completed successfully."
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
APP_NAME=AmneziaVPN
|
||||
ORG_NAME=AmneziaVPN.ORG
|
||||
LOG_FOLDER=/var/log/$APP_NAME
|
||||
LOG_FILE="$LOG_FOLDER/post-uninstall.log"
|
||||
APP_PATH=/opt/$APP_NAME
|
||||
@@ -64,6 +65,24 @@ if test -f /usr/share/pixmaps/$APP_NAME.png; then
|
||||
|
||||
fi
|
||||
|
||||
### Remove the service log file (keep post-uninstall.log)
|
||||
if test -f "$LOG_FOLDER/AmneziaVPN-service.log"; then
|
||||
sudo rm -f "$LOG_FOLDER/AmneziaVPN-service.log" >> $LOG_FILE 2>&1
|
||||
fi
|
||||
|
||||
### Remove user logs for current user only
|
||||
TARGET_HOME="$HOME"
|
||||
if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then
|
||||
TARGET_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
|
||||
fi
|
||||
if test -d "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME/log"; then
|
||||
rm -rf "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME/log" >> $LOG_FILE 2>&1
|
||||
fi
|
||||
|
||||
# Try to remove empty app and organization directories under user share
|
||||
if rmdir "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME" 2>/dev/null; then :; fi
|
||||
if rmdir "$TARGET_HOME/.local/share/$ORG_NAME" 2>/dev/null; then :; fi
|
||||
|
||||
if command -v steamos-readonly &> /dev/null; then
|
||||
sudo steamos-readonly enable >> $LOG_FILE
|
||||
echo "steamos-readonly enabled" >> $LOG_FILE
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
set AmneziaPath=%~dp0
|
||||
echo %AmneziaPath%
|
||||
|
||||
"%AmneziaPath%\AmneziaVPN.exe" -c
|
||||
rem Define directories for logs
|
||||
set "ORG_DIR=%AppData%\AmneziaVPN.ORG"
|
||||
set "USER_APP_DIR=%ORG_DIR%\AmneziaVPN"
|
||||
set "USER_LOG_DIR=%USER_APP_DIR%\log"
|
||||
set "SYS_APP_DIR=%ProgramData%\AmneziaVPN"
|
||||
set "SYS_LOG_DIR=%SYS_APP_DIR%\log"
|
||||
set "SYS_LOG_FILE=%SYS_LOG_DIR%\AmneziaVPN-service.log"
|
||||
|
||||
timeout /t 1
|
||||
sc stop AmneziaVPN-service
|
||||
sc delete AmneziaVPN-service
|
||||
@@ -9,4 +16,17 @@ sc stop AmneziaWGTunnel$AmneziaVPN
|
||||
sc delete AmneziaWGTunnel$AmneziaVPN
|
||||
taskkill /IM "AmneziaVPN-service.exe" /F
|
||||
taskkill /IM "AmneziaVPN.exe" /F
|
||||
|
||||
rem Delete the service log file under ProgramData
|
||||
if exist "%SYS_LOG_FILE%" del /F /Q "%SYS_LOG_FILE%"
|
||||
if exist "%SYS_LOG_DIR%" rmdir /S /Q "%SYS_LOG_DIR%"
|
||||
rem Try to remove application dir if empty
|
||||
rd "%SYS_APP_DIR%" 2>nul
|
||||
|
||||
rem Delete client logs under current user's AppData\Roaming (Organization\Application)
|
||||
if exist "%USER_LOG_DIR%" rmdir /S /Q "%USER_LOG_DIR%"
|
||||
rem Try to remove app and org directories if empty
|
||||
rd "%USER_APP_DIR%" 2>nul
|
||||
rd "%ORG_DIR%" 2>nul
|
||||
|
||||
exit /b 0
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
set AmneziaPath=%~dp0
|
||||
echo %AmneziaPath%
|
||||
|
||||
"%AmneziaPath%\AmneziaVPN.exe" -c
|
||||
rem Define directories for logs
|
||||
set "ORG_DIR=%AppData%\AmneziaVPN.ORG"
|
||||
set "USER_APP_DIR=%ORG_DIR%\AmneziaVPN"
|
||||
set "USER_LOG_DIR=%USER_APP_DIR%\log"
|
||||
set "SYS_APP_DIR=%ProgramData%\AmneziaVPN"
|
||||
set "SYS_LOG_DIR=%SYS_APP_DIR%\log"
|
||||
set "SYS_LOG_FILE=%SYS_LOG_DIR%\AmneziaVPN-service.log"
|
||||
|
||||
timeout /t 1
|
||||
sc stop AmneziaVPN-service
|
||||
sc delete AmneziaVPN-service
|
||||
@@ -9,4 +16,17 @@ sc stop AmneziaWGTunnel$AmneziaVPN
|
||||
sc delete AmneziaWGTunnel$AmneziaVPN
|
||||
taskkill /IM "AmneziaVPN-service.exe" /F
|
||||
taskkill /IM "AmneziaVPN.exe" /F
|
||||
|
||||
rem Delete the service log file under ProgramData
|
||||
if exist "%SYS_LOG_FILE%" del /F /Q "%SYS_LOG_FILE%"
|
||||
if exist "%SYS_LOG_DIR%" rmdir /S /Q "%SYS_LOG_DIR%"
|
||||
rem Try to remove application dir if empty
|
||||
rd "%SYS_APP_DIR%" 2>nul
|
||||
|
||||
rem Delete client logs under current user's AppData\Roaming (Organization\Application)
|
||||
if exist "%USER_LOG_DIR%" rmdir /S /Q "%USER_LOG_DIR%"
|
||||
rem Try to remove app and org directories if empty
|
||||
rd "%USER_APP_DIR%" 2>nul
|
||||
rd "%ORG_DIR%" 2>nul
|
||||
|
||||
exit /b 0
|
||||
|
||||
Reference in New Issue
Block a user