Compare commits

...

11 Commits

Author SHA1 Message Date
vladimir.kuznetsov 073491ccb4 fixed anchors warning on PageHome 2024-03-25 21:52:38 +05:00
pokamest 561b62cd40 Merge pull request #705 from amnezia-vpn/bugfix/import-error-handling
fixed error handling for config import
2024-03-23 13:23:31 +00:00
pokamest 1284ed4d84 Merge pull request #706 from amnezia-vpn/translations/connection-label-fix
Fix connection button labels
2024-03-23 00:30:10 +00:00
Andrey Zaharow 6f34443191 Fix connection button labels 2024-03-21 21:34:51 +01:00
vladimir.kuznetsov 02f186c54e fixed error handling for config import 2024-03-21 23:32:11 +05:00
alexeyq2 784c6cf585 Fix AWG/WG on Linux - IPv6 gateway address is ULA now (#701) 2024-03-21 15:03:00 +00:00
pokamest 14f132e127 Merge pull request #703 from amnezia-vpn/feature/linux-ipc-fix
Increase timeout for IPC command
2024-03-21 13:29:14 +00:00
Mykola Baibuz 9cb624e681 Increase timeout for IPC command 2024-03-20 23:10:29 +02:00
isamnezia 516e3da7e2 Fix open log crash and side log improvements (#694)
Fix open log crash
2024-03-20 15:35:36 +00:00
Andrey Zaharow 0e83586cae Fix UI for Burmese language (#682)
* Fix UI for Burmese language
2024-03-20 15:20:09 +00:00
Nethius 95bdae68f4 Auto disable logs after 14 days (#610)
Auto disable logs after 14 days
2024-03-20 14:22:29 +00:00
39 changed files with 596 additions and 439 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.4.1.4
project(${PROJECT} VERSION 4.4.2.1
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
+1 -1
View File
@@ -71,7 +71,7 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
}
QRemoteObjectPendingReply<int> futureResult = Instance()->m_ipcClient->createPrivilegedProcess();
futureResult.waitForFinished(1000);
futureResult.waitForFinished(5000);
int pid = futureResult.returnValue();
+2 -1
View File
@@ -84,7 +84,8 @@ target_sources(networkextension PRIVATE
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPNAdapterDelegate.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
)
+8 -4
View File
@@ -124,10 +124,14 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key));
json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip));
// todo review wg ipv6
#ifdef Q_OS_MACOS
json.insert("deviceIpv6Address", "dead::1");
#endif
// set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable.
// this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4.
// Otherwise some OSes (Linux) try IPv6 forever and hang.
// https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193)
// https://man7.org/linux/man-pages/man5/gai.conf.5.html
json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it
json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key));
json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key));
json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName));
+31 -4
View File
@@ -2,6 +2,8 @@ import Foundation
import os.log
struct Log {
static let osLog = Logger()
private static let IsLoggingEnabledKey = "IsLoggingEnabled"
static var isLoggingEnabled: Bool {
get {
@@ -29,16 +31,23 @@ struct Log {
return dateFormatter
}()
var records: [Record]
var records = [Record]()
var lastRecordDate = Date.distantPast
init() {
self.records = []
}
init(_ str: String) {
self.records = str.split(whereSeparator: \.isNewline)
.compactMap {
Record(String($0))
records = str.split(whereSeparator: \.isNewline)
.map {
if let record = Record(String($0)) {
lastRecordDate = record.date
return record
} else {
return Record(date: lastRecordDate, level: .error, message: "LOG: \($0)")
}
}
}
@@ -60,6 +69,24 @@ struct Log {
self.init(str)
}
static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) {
guard isLoggingEnabled else { return }
let date = Date()
let level = Record.Level(from: type)
let messages = message.split(whereSeparator: \.isNewline)
for index in 0..<messages.count {
let message = String(messages[index])
if index != 0 && message.first != " " {
Record(date: date, level: level, message: "\(title) \(message)").save(at: url)
} else {
Record(date: date, level: level, message: "\(title)\(message)").save(at: url)
}
}
}
static func clear(at url: URL) {
if FileManager.default.fileExists(atPath: url.path) {
guard let fileHandle = try? FileHandle(forUpdating: url) else { return }
+26 -5
View File
@@ -30,6 +30,8 @@ extension Log {
}
func save(at url: URL) {
osLog.log(level: level.osLogType, "\(message)")
guard let data = "\n\(description)".data(using: .utf8) else { return }
if !FileManager.default.fileExists(atPath: url.path) {
@@ -64,19 +66,38 @@ extension Log.Record {
init(from osLogType: OSLogType) {
switch osLogType {
case OSLogType.default:
case .default:
self = .info
case OSLogType.info:
case .info:
self = .info
case OSLogType.debug:
case .debug:
self = .debug
case OSLogType.error:
case .error:
self = .error
case OSLogType.fault:
case .fault:
self = .fatal
default:
self = .info
}
}
var osLogType: OSLogType {
switch self {
case .info:
return .info
case .debug:
return .debug
case .error:
return .error
case .fatal:
return .fault
case .warning:
return .info
case .critical:
return .fault
case .system:
return .fault
}
}
}
}
+10 -10
View File
@@ -1,18 +1,18 @@
import Foundation
import os.log
public func wg_log(_ type: OSLogType, staticMessage: StaticString) {
guard Log.isLoggingEnabled else { return }
Log.Record(date: Date(), level: Log.Record.Level(from: type), message: "\(staticMessage)").save(at: Log.neLogURL)
public func wg_log(_ type: OSLogType, title: String = "", staticMessage: StaticString) {
neLog(type, title: "WG: \(title)", message: "\(staticMessage)")
}
public func wg_log(_ type: OSLogType, message: String) {
log(type, message: message)
public func wg_log(_ type: OSLogType, title: String = "", message: String) {
neLog(type, title: "WG: \(title)", message: message)
}
public func log(_ type: OSLogType, message: String) {
guard Log.isLoggingEnabled else { return }
Log.Record(date: Date(), level: Log.Record.Level(from: type), message: message).save(at: Log.neLogURL)
public func ovpnLog(_ type: OSLogType, title: String = "", message: String) {
neLog(type, title: "OVPN: \(title)", message: message)
}
public func neLog(_ type: OSLogType, title: String = "", message: String) {
Log.log(type, title: "NE: \(title)", message: message)
}
@@ -2,6 +2,106 @@ import Foundation
import NetworkExtension
import OpenVPNAdapter
struct OpenVPNConfig: Decodable {
let config: String
let splitTunnelType: Int
let splitTunnelSites: [String]
var str: String {
"splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) config: \(config)"
}
}
extension PacketTunnelProvider {
func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
ovpnLog(.error, message: "Can't start")
return
}
do {
// ovpnLog(.info, message: "providerConfiguration: \(String(decoding: openVPNConfigData, as: UTF8.self))")
let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData)
ovpnLog(.info, title: "config: ", message: openVPNConfig.str)
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
} catch {
ovpnLog(.error, message: "Can't parse config: \(error.localizedDescription)")
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
ovpnLog(.error, message: "Can't parse config: \(underlyingError.localizedDescription)")
}
return
}
}
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data,
withShadowSocks viaSS: Bool = false,
completionHandler: @escaping (Error?) -> Void) {
ovpnLog(.info, message: "Setup and launch")
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 {
ovpnLog(.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 }
// ovpn_log(.error, message: "Available TUN Interfaces: \(ifaces)")
}
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: []))
}
func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
ovpnAdapter.disconnect()
}
}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
// `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
@@ -116,6 +216,8 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// Use this method to process any log message returned by OpenVPN library.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
// Handle log messages
wg_log(.info, message: logMessage)
ovpnLog(.info, message: logMessage)
}
}
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
@@ -0,0 +1,221 @@
import Foundation
import NetworkExtension
extension PacketTunnelProvider {
func startWireguard(activationAttemptId: String?,
errorNotifier: ErrorNotifier,
completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let wgConfigData: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
wg_log(.error, message: "Can't start, config missing")
completionHandler(nil)
return
}
do {
let wgConfig = try JSONDecoder().decode(WGConfig.self, from: wgConfigData)
let wgConfigStr = wgConfig.str
wg_log(.info, title: "config: ", message: wgConfig.redux)
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
if tunnelConfiguration.peers.first!.allowedIPs
.map({ $0.stringRepresentation })
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {
if wgConfig.splitTunnelType == 1 {
for index in tunnelConfiguration.peers.indices {
tunnelConfiguration.peers[index].allowedIPs.removeAll()
var allowedIPs = [IPAddressRange]()
for allowedIPString in wgConfig.splitTunnelSites {
if let allowedIP = IPAddressRange(from: allowedIPString) {
allowedIPs.append(allowedIP)
}
}
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
}
} else if wgConfig.splitTunnelType == 2 {
for index in tunnelConfiguration.peers.indices {
var excludeIPs = [IPAddressRange]()
for excludeIPString in wgConfig.splitTunnelSites {
if let excludeIP = IPAddressRange(from: excludeIPString) {
excludeIPs.append(excludeIP)
}
}
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
}
}
}
wg_log(.info, message: "Starting tunnel from the " +
(activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
// Start the tunnel
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
guard let adapterError else {
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
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:
fatalError()
}
}
} catch {
wg_log(.error, message: "Can't parse WG config: \(error.localizedDescription)")
completionHandler(nil)
return
}
}
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings {
data = settings.data(using: .utf8)!
}
let components = settings!.components(separatedBy: "\n")
var settingsDictionary: [String: String] = [:]
for component in components {
let pair = component.components(separatedBy: "=")
if pair.count == 2 {
settingsDictionary[pair[0]] = pair[1]
}
}
let response: [String: Any] = [
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
}
}
private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
if messageData.count == 1 && messageData[0] == 0 {
wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings {
data = settings.data(using: .utf8)!
}
completionHandler(data)
}
} else if messageData.count >= 1 {
// Updates the tunnel configuration and responds with the active configuration
wg_log(.info, message: "Switching tunnel configuration")
guard let configString = String(data: messageData, encoding: .utf8)
else {
completionHandler(nil)
return
}
do {
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
if let error {
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
completionHandler(nil)
return
}
self.wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings {
data = settings.data(using: .utf8)!
}
completionHandler(data)
}
}
} catch {
completionHandler(nil)
}
} else {
completionHandler(nil)
}
}
// 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 {
// wg_log(.error, message: "Failed to start an empty tunnel")
// completionHandler(error)
// } else {
// wg_log(.info, message: "Started an empty tunnel")
// self.tunnelAdapterDidStart()
// }
// }
// }
//
// let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
//
// self.setTunnelNetworkSettings(settings) { error in
// completionHandler(error)
// }
// }
// private func tunnelAdapterDidStart() {
// dispatchPrecondition(condition: .onQueue(dispatchQueue))
// // ...
// }
func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile()
if let 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
// sufficient quantities of users.
exit(0)
#endif
}
}
}
+36 -338
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"
@@ -34,62 +34,68 @@ struct Constants {
}
class PacketTunnelProvider: NEPacketTunnelProvider {
private lazy var wgAdapter = {
lazy var wgAdapter = {
WireGuardAdapter(with: self) { logLevel, message in
wg_log(logLevel.osLogLevel, message: message)
}
}()
private lazy var ovpnAdapter: OpenVPNAdapter = {
lazy var ovpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}()
/// Internal queue.
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
private var openVPNConfig: Data?
var splitTunnelType: Int!
var splitTunnelSites: [String]!
let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
var protoType: TunnelProtoType = .none
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
let tmpStr = String(data: messageData, encoding: .utf8)!
wg_log(.error, message: tmpStr)
guard let message = String(data: messageData, encoding: .utf8) else {
if let completionHandler {
completionHandler(nil)
}
return
}
neLog(.info, title: "App said: ", message: message)
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
log(.error, message: "Failed to serialize message from app")
neLog(.error, message: "Failed to serialize message from app")
return
}
guard let completionHandler else {
log(.error, message: "Missing message completion handler")
neLog(.error, message: "Missing message completion handler")
return
}
guard let action = message[Constants.kMessageKeyAction] as? String else {
log(.error, message: "Missing action key in app message")
neLog(.error, message: "Missing action key in app message")
completionHandler(nil)
return
}
if action == Constants.kActionStatus {
handleStatusAppMessage(messageData, completionHandler: completionHandler)
}
}
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
dispatchQueue.async {
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
log(.info, message: "PacketTunnelProvider startTunnel")
neLog(.info, message: "Start tunnel")
if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol {
let providerConfiguration = protocolConfiguration.providerConfiguration
if (providerConfiguration?[Constants.ovpnConfigKey] as? Data) != nil {
@@ -100,7 +106,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} else {
self.protoType = .none
}
switch self.protoType {
case .wireguard:
self.startWireguard(activationAttemptId: activationAttemptId,
@@ -116,7 +122,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
}
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
dispatchQueue.async {
switch self.protoType {
@@ -132,7 +138,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
}
}
func handleStatusAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
switch protoType {
case .wireguard:
@@ -146,291 +152,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
break
}
}
// MARK: Private methods
private func startWireguard(activationAttemptId: String?,
errorNotifier: ErrorNotifier,
completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let wgConfigData: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
wg_log(.error, message: "Can't start WireGuard config missing")
completionHandler(nil)
return
}
do {
let wgConfig = try JSONDecoder().decode(WGConfig.self, from: wgConfigData)
let wgConfigStr = wgConfig.str
log(.info, message: "wgConfig: \(wgConfig.redux.replacingOccurrences(of: "\n", with: " "))")
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
if tunnelConfiguration.peers.first!.allowedIPs
.map({ $0.stringRepresentation })
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {
if wgConfig.splitTunnelType == 1 {
for index in tunnelConfiguration.peers.indices {
tunnelConfiguration.peers[index].allowedIPs.removeAll()
var allowedIPs = [IPAddressRange]()
for allowedIPString in wgConfig.splitTunnelSites {
if let allowedIP = IPAddressRange(from: allowedIPString) {
allowedIPs.append(allowedIP)
}
}
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
}
} else if wgConfig.splitTunnelType == 2 {
for index in tunnelConfiguration.peers.indices {
var excludeIPs = [IPAddressRange]()
for excludeIPString in wgConfig.splitTunnelSites {
if let excludeIP = IPAddressRange(from: excludeIPString) {
excludeIPs.append(excludeIP)
}
}
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
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
guard let adapterError else {
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
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:
fatalError()
}
}
} catch {
log(.error, message: "Can't parse WG config: \(error.localizedDescription)")
completionHandler(nil)
return
}
}
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
wg_log(.error, message: "Can't start startOpenVPN()")
return
}
do {
log(.info, message: "providerConfiguration: \(String(decoding: openVPNConfigData, as: UTF8.self).replacingOccurrences(of: "\n", with: " "))")
let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData)
log(.info, message: "openVPNConfig: \(openVPNConfig.str.replacingOccurrences(of: "\n", with: " "))")
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
} catch {
log(.error, message: "Can't parse OpenVPN config: \(error.localizedDescription)")
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
log(.error, message: "Can't parse OpenVPN config: \(underlyingError.localizedDescription)")
}
return
}
}
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile()
if let 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
// sufficient quantities of users.
exit(0)
#endif
}
}
private func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
ovpnAdapter.disconnect()
}
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings {
data = settings.data(using: .utf8)!
}
let components = settings!.components(separatedBy: "\n")
var settingsDictionary: [String: String] = [:]
for component in components {
let pair = component.components(separatedBy: "=")
if pair.count == 2 {
settingsDictionary[pair[0]] = pair[1]
}
}
let response: [String: Any] = [
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
}
}
private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
if messageData.count == 1 && messageData[0] == 0 {
wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings {
data = settings.data(using: .utf8)!
}
completionHandler(data)
}
} else if messageData.count >= 1 {
// Updates the tunnel configuration and responds with the active configuration
wg_log(.info, message: "Switching tunnel configuration")
guard let configString = String(data: messageData, encoding: .utf8)
else {
completionHandler(nil)
return
}
do {
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
if let error {
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
completionHandler(nil)
return
}
self.wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings {
data = settings.data(using: .utf8)!
}
completionHandler(data)
}
}
} catch {
completionHandler(nil)
}
} else {
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: []))
}
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)")
}
// 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]?,
@@ -450,48 +183,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
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 {
log(.error, message: "Failed to start an empty tunnel")
completionHandler(error)
} else {
log(.info, message: "Started an empty tunnel")
self.tunnelAdapterDidStart()
}
}
}
let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
self.setTunnelNetworkSettings(settings) { error in
completionHandler(error)
}
}
private func tunnelAdapterDidStart() {
dispatchPrecondition(condition: .onQueue(dispatchQueue))
// ...
}
}
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
extension WireGuardLogLevel {
var osLogLevel: OSLogType {
switch self {
-11
View File
@@ -89,14 +89,3 @@ struct WGConfig: Decodable {
"""
}
}
struct OpenVPNConfig: Decodable {
let config: String
let mtu: String
let splitTunnelType: Int
let splitTunnelSites: [String]
var str: String {
"splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) mtu: \(mtu) config: \(config)"
}
}
+1 -1
View File
@@ -217,7 +217,7 @@ ErrorCode OpenVpnProtocol::start()
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_openVpnProcess->waitForSource(1000);
m_openVpnProcess->waitForSource(5000);
if (!m_openVpnProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
+13
View File
@@ -226,9 +226,22 @@ void Settings::setSaveLogs(bool enabled)
}
}
#endif
if (enabled) {
setLogEnableDate(QDateTime::currentDateTime());
}
emit saveLogsChanged(enabled);
}
QDateTime Settings::getLogEnableDate()
{
return value("Conf/logEnableDate").toDateTime();
}
void Settings::setLogEnableDate(QDateTime date)
{
setValue("Conf/logEnableDate", date);
}
QString Settings::routeModeString(RouteMode mode) const
{
switch (mode) {
+3
View File
@@ -100,6 +100,9 @@ public:
}
void setSaveLogs(bool enabled);
QDateTime getLogEnableDate();
void setLogEnableDate(QDateTime date);
enum RouteMode {
VpnAllSites,
VpnOnlyForwardSites,
+3 -3
View File
@@ -19,7 +19,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="65"/>
<source>Connection...</source>
<source>Connecting...</source>
<translation>اتصال...</translation>
</message>
<message>
@@ -29,12 +29,12 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="79"/>
<source>Reconnection...</source>
<source>Reconnecting...</source>
<translation>إعادة الاتصال...</translation>
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="89"/>
<source>Disconnection...</source>
<source>Disconnecting...</source>
<translation>إنهاء الاتصال...</translation>
</message>
<message>
+3 -3
View File
@@ -42,7 +42,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="65"/>
<source>Connection...</source>
<source>Connecting...</source>
<translation>در حال ارتباط...</translation>
</message>
<message>
@@ -63,7 +63,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="79"/>
<source>Reconnection...</source>
<source>Reconnecting...</source>
<translation>اتصال دوباره...</translation>
</message>
<message>
@@ -76,7 +76,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="89"/>
<source>Disconnection...</source>
<source>Disconnecting...</source>
<translation>قطع ارتباط...</translation>
</message>
</context>
+3 -3
View File
@@ -12,7 +12,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="65"/>
<source>Connection...</source>
<source>Connecting...</source>
<translation>က...</translation>
</message>
<message>
@@ -32,7 +32,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="79"/>
<source>Reconnection...</source>
<source>Reconnecting...</source>
<translation>က...</translation>
</message>
<message>
@@ -45,7 +45,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="89"/>
<source>Disconnection...</source>
<source>Disconnecting...</source>
<translation>က...</translation>
</message>
</context>
+3 -3
View File
@@ -35,7 +35,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="65"/>
<source>Connection...</source>
<source>Connecting...</source>
<translation>Подключение...</translation>
</message>
<message>
@@ -55,7 +55,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="79"/>
<source>Reconnection...</source>
<source>Reconnecting...</source>
<translation>Переподключение...</translation>
</message>
<message>
@@ -68,7 +68,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="89"/>
<source>Disconnection...</source>
<source>Disconnecting...</source>
<translation>Отключение...</translation>
</message>
</context>
+3 -3
View File
@@ -34,7 +34,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="65"/>
<source>Connection...</source>
<source>Connecting...</source>
<translation></translation>
</message>
<message>
@@ -44,12 +44,12 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="79"/>
<source>Reconnection...</source>
<source>Reconnecting...</source>
<translation></translation>
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="89"/>
<source>Disconnection...</source>
<source>Disconnecting...</source>
<translation></translation>
</message>
<message>
@@ -62,7 +62,7 @@ void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
m_state = state;
m_isConnected = false;
m_connectionStateText = tr("Connection...");
m_connectionStateText = tr("Connecting...");
switch (state) {
case Vpn::ConnectionState::Connected: {
m_isConnectionInProgress = false;
@@ -76,7 +76,7 @@ void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
}
case Vpn::ConnectionState::Reconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Reconnection...");
m_connectionStateText = tr("Reconnecting...");
break;
}
case Vpn::ConnectionState::Disconnected: {
@@ -86,7 +86,7 @@ void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
}
case Vpn::ConnectionState::Disconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Disconnection...");
m_connectionStateText = tr("Disconnecting...");
break;
}
case Vpn::ConnectionState::Preparing: {
+5 -4
View File
@@ -79,7 +79,7 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
return extractConfigFromData(data);
}
emit importErrorOccurred(tr("Unable to open file"));
emit importErrorOccurred(tr("Unable to open file"), false);
return false;
}
@@ -117,12 +117,12 @@ bool ImportController::extractConfigFromData(QString data)
if (!m_serversModel->getServersCount()) {
emit restoreAppConfig(config.toUtf8());
} else {
emit importErrorOccurred(tr("Invalid configuration file"));
emit importErrorOccurred(tr("Invalid configuration file"), false);
}
break;
}
case ConfigTypes::Invalid: {
emit importErrorOccurred(tr("Invalid configuration file"));
emit importErrorOccurred(tr("Invalid configuration file"), false);
break;
}
}
@@ -257,6 +257,7 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
} else {
qDebug() << "Key parameter 'Endpoint' is missing";
emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError), false);
return QJsonObject();
}
if (hostNameAndPortMatch.hasCaptured(2)) {
@@ -282,7 +283,7 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
lastConfig[config_key::server_pub_key] = configMap.value("PublicKey");
} else {
qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)";
emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError));
emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError), false);
return QJsonObject();
}
-1
View File
@@ -39,7 +39,6 @@ public slots:
signals:
void importFinished();
void importErrorOccurred(const QString &errorMessage, bool goToPageHome);
void importErrorOccurred(const QString &errorMessage);
void qrDecodingFinished();
+15 -1
View File
@@ -27,6 +27,7 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
m_settings(settings)
{
m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH);
checkIfNeedDisableLogs();
}
void SettingsController::toggleAmneziaDns(bool enable)
@@ -71,8 +72,11 @@ void SettingsController::toggleLogging(bool enable)
{
m_settings->setSaveLogs(enable);
#ifdef Q_OS_IOS
AmneziaVPN::toggleLogging(enable);
AmneziaVPN::toggleLogging(enable);
#endif
if (enable == true) {
checkIfNeedDisableLogs();
}
emit loggingStateChanged();
}
@@ -205,3 +209,13 @@ bool SettingsController::isCameraPresent()
return false;
#endif
}
void SettingsController::checkIfNeedDisableLogs()
{
m_loggingDisableDate = m_settings->getLogEnableDate().addDays(14);
if (m_loggingDisableDate <= QDateTime::currentDateTime()) {
toggleLogging(false);
clearLogs();
emit loggingDisableByWatcher();
}
}
@@ -77,6 +77,8 @@ signals:
void amneziaDnsToggled(bool enable);
void loggingDisableByWatcher();
private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
@@ -85,6 +87,10 @@ private:
std::shared_ptr<Settings> m_settings;
QString m_appVersion;
QDateTime m_loggingDisableDate;
void checkIfNeedDisableLogs();
};
#endif // SETTINGSCONTROLLER_H
+9
View File
@@ -81,6 +81,15 @@ int LanguageModel::getCurrentLanguageIndex()
}
}
int LanguageModel::getLineHeightAppend()
{
int langIndex = getCurrentLanguageIndex();
switch (langIndex) {
case 5: return 10; break; // Burmese
default: return 0; break;
}
}
QString LanguageModel::getCurrentLanguageName()
{
return m_availableLanguages[getCurrentLanguageIndex()].name;
+2
View File
@@ -49,10 +49,12 @@ public:
Q_PROPERTY(QString currentLanguageName READ getCurrentLanguageName NOTIFY translationsUpdated)
Q_PROPERTY(int currentLanguageIndex READ getCurrentLanguageIndex NOTIFY translationsUpdated)
Q_PROPERTY(int lineHeightAppend READ getLineHeightAppend NOTIFY translationsUpdated)
public slots:
void changeLanguage(const LanguageSettings::AvailableLanguageEnum language);
int getCurrentLanguageIndex();
int getLineHeightAppend();
QString getCurrentLanguageName();
signals:
@@ -1,7 +1,7 @@
import QtQuick
Text {
lineHeight: 16
lineHeight: 16 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: "#0E0E11"
@@ -1,7 +1,7 @@
import QtQuick
Text {
lineHeight: 38
lineHeight: 38 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: "#D7D8DB"
@@ -1,7 +1,7 @@
import QtQuick
Text {
lineHeight: 30
lineHeight: 30 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: "#D7D8DB"
@@ -1,7 +1,7 @@
import QtQuick
Text {
lineHeight: 16
lineHeight: 16 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: "#878B91"
@@ -1,7 +1,7 @@
import QtQuick
Text {
lineHeight: 21.6
lineHeight: 21.6 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: "#D7D8DB"
@@ -1,7 +1,7 @@
import QtQuick
Text {
lineHeight: 24
lineHeight: 24 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: "#D7D8DB"
@@ -1,7 +1,7 @@
import QtQuick
Text {
lineHeight: 20
lineHeight: 20 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: "#D7D8DB"
+55 -28
View File
@@ -33,44 +33,71 @@ PageType {
anchors.fill: parent
anchors.bottomMargin: drawer.collapsedHeight
ConnectButton {
id: connectButton
anchors.centerIn: parent
}
BasicButtonType {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
ColumnLayout {
anchors.fill: parent
anchors.topMargin: 34
anchors.bottomMargin: 34
leftPadding: 16
rightPadding: 16
implicitHeight: 36
BasicButtonType {
property bool isLoggingEnabled: SettingsController.isLoggingEnabled
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#878B91"
leftImageColor: "transparent"
borderWidth: 0
Layout.alignment: Qt.AlignHCenter
property bool isSplitTunnelingEnabled: SitesModel.isTunnelingEnabled ||
(ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi"))
implicitHeight: 36
text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled")
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#878B91"
borderWidth: 0
imageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : ""
rightImageSource: "qrc:/images/controls/chevron-down.svg"
visible: isLoggingEnabled ? true : false
text: qsTr("Logging enabled")
onClicked: {
homeSplitTunnelingDrawer.open()
onClicked: {
PageController.goToPage(PageEnum.PageSettingsLogging)
}
}
HomeSplitTunnelingDrawer {
id: homeSplitTunnelingDrawer
ConnectButton {
id: connectButton
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
}
parent: root
BasicButtonType {
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.bottomMargin: 34
leftPadding: 16
rightPadding: 16
implicitHeight: 36
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#878B91"
leftImageColor: "transparent"
borderWidth: 0
property bool isSplitTunnelingEnabled: SitesModel.isTunnelingEnabled ||
(ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi"))
text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled")
imageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : ""
rightImageSource: "qrc:/images/controls/chevron-down.svg"
onClicked: {
homeSplitTunnelingDrawer.open()
}
HomeSplitTunnelingDrawer {
id: homeSplitTunnelingDrawer
parent: root
}
}
}
}
@@ -15,6 +15,18 @@ import "../Controls2/TextTypes"
PageType {
id: root
Connections {
target: SettingsController
function onLoggingStateChanged() {
if (SettingsController.isLoggingEnabled) {
var message = qsTr("Logging is enabled. Note that logs will be automatically \
disabled after 14 days, and all log files will be deleted.")
PageController.showNotificationMessage(message)
}
}
}
BackButtonType {
id: backButton
@@ -90,7 +90,7 @@ PageType {
PageController.showBusyIndicator(false)
}
function onImportErrorOccurred(errorMessage) {
function onImportErrorOccurred(errorMessage, goToPageHome) {
PageController.showErrorMessage(errorMessage)
}
}
@@ -24,7 +24,6 @@ PageType {
} else {
PageController.closePage()
}
PageController.showErrorMessage(errorMessage)
}
function onImportFinished() {
+9 -1
View File
@@ -124,11 +124,19 @@ PageType {
Connections {
target: ImportController
function onImportErrorOccurred(errorMessage) {
function onImportErrorOccurred(errorMessage, goToPageHome) {
PageController.showErrorMessage(errorMessage)
}
}
Connections {
target: SettingsController
function onLoggingDisableByWatcher() {
PageController.showNotificationMessage(qsTr("Logging was disabled after 14 days, log files were deleted"))
}
}
StackViewType {
id: tabBarStackView
+1
View File
@@ -95,6 +95,7 @@ Window {
Connections {
target: SettingsController
function onChangeSettingsFinished(finishedMessage) {
PageController.showNotificationMessage(finishedMessage)
}