Merge branch 'refactor-to-submodules'

This commit is contained in:
Davide De Rosa 2019-05-27 11:41:26 +02:00
commit 4a53632046
111 changed files with 371 additions and 10192 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "Submodules/API"]
path = Submodules/API
url = https://github.com/passepartoutvpn/api
[submodule "Submodules/Core"]
path = Submodules/Core
url = https://github.com/passepartoutvpn/passepartout-core-apple

1
Libraries/API Symbolic link
View File

@ -0,0 +1 @@
../Submodules/API/

1
Libraries/Core Symbolic link
View File

@ -0,0 +1 @@
../Submodules/Core/

View File

@ -1 +0,0 @@
{"appGroup":"group.com.algoritmico.Passepartout","activeProfileId":"host.edu","tunnelConfiguration":{"endpointProtocols":["UDP:1194"],"compressionFraming":0,"digest":"SHA1","ca":"","lastErrorKey":"LastVPNError","debugLogFormat":"$DHH:mm:ss$d - $M","usesPIAPatches":false,"cipher":"AES-128-CBC","prefersResolvedAddresses":false,"shouldDebug":true,"mtu":1250,"debugLogKey":"LastVPNLog"},"preferences":{"trustPolicy":"ignore","trustsMobileNetwork":false,"disconnectsOnSleep":false,"trustedWifis":{},"resolvesHostname":true},"profiles":[{"provider":{"username":"p0000000","id":"provider.PIA","poolId":"ca-vancouver","name":"PIA","presetId":"recommended"}},{"host":{"username":"","title":"edu","hostname":"1.2.4.5","parameters":{"endpointProtocols":["UDP:1194","TCP:1194","TCP:443"],"compressionFraming":1,"digest":"SHA256","ca":"bogus+ca","clientCertificate":"bogus+client","usesPIAPatches":false,"tlsWrap":{"key":{"dir":1,"data":"bogus+static+key"},"strategy":"auth"},"cipher":"AES-256-CBC","prefersResolvedAddresses":false,"clientKey":"bogus+key","mtu":1500,"shouldDebug":false}}},{"host":{"username":"","title":"vps-udp-tc","hostname":"8.8.4.4","parameters":{"shouldDebug":false,"endpointProtocols":["UDP:1198"],"compressionFraming":1,"digest":"SHA512","ca":"bogus+ca","renegotiatesAfterSeconds":0,"usesPIAPatches":false,"tlsWrap":{"key":{"dir":1,"data":"bogus+static+key"},"strategy":"crypt"},"cipher":"AES-192-CBC","prefersResolvedAddresses":false,"clientKey":"bogus+key","mtu":1500,"keepAliveSeconds":25}}}]}

View File

@ -1,64 +0,0 @@
//
// ConnectionServiceTests.swift
// Passepartout-CoreTests
//
// Created by Davide De Rosa on 10/25/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import XCTest
import TunnelKit
@testable import Passepartout_Core
class ConnectionServiceTests: XCTestCase {
let url = Bundle(for: ConnectionServiceTests.self).url(forResource: "ConnectionService", withExtension: "json")!
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testParse() {
let jsonData = try! Data(contentsOf: url)
XCTAssertNoThrow(try JSONSerialization.jsonObject(with: jsonData, options: []))
}
func testPathExtension() {
XCTAssertTrue(privateTestPathExtension("file:///foo/bar/johndoe.json"))
XCTAssertFalse(privateTestPathExtension("file:///foo/bar/break.json.johndoe.json"))
}
private func privateTestPathExtension(_ string: String) -> Bool {
let url = URL(string: string)!
let filename = url.lastPathComponent
guard let extRange = filename.range(of: ".json") else {
return false
}
guard url.pathExtension == "json" else {
return false
}
let name1 = String(filename[filename.startIndex..<extRange.lowerBound])
let name2 = url.deletingPathExtension().lastPathComponent
return name1 == name2
}
}

View File

@ -1,84 +0,0 @@
//
// InfrastructureTests.swift
// Passepartout-CoreTests
//
// Created by Davide De Rosa on 6/11/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import XCTest
@testable import Passepartout_Core
import TunnelKit
class InfrastructureTests: XCTestCase {
private let infra = InfrastructureFactory.shared.get(.pia)
override func setUp() {
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testParsing() {
print(infra.categories)
XCTAssertEqual(infra.categories.count, 1)
}
func testIdentifier() {
let id = "us-east"
guard let pool = infra.pool(for: id) else {
XCTAssert(false)
return
}
print(pool)
XCTAssertEqual(pool.id, id)
}
func testStableSort() {
let original: [EndpointProtocol] = [
EndpointProtocol(.udp, 1194),
EndpointProtocol(.udp, 8080),
EndpointProtocol(.udp, 9201),
EndpointProtocol(.udp, 53),
EndpointProtocol(.udp, 1197),
EndpointProtocol(.udp, 198),
EndpointProtocol(.tcp, 443),
EndpointProtocol(.tcp, 110),
EndpointProtocol(.tcp, 80),
EndpointProtocol(.tcp, 500),
EndpointProtocol(.tcp, 501),
EndpointProtocol(.tcp, 502)
]
var preferredType: SocketType
preferredType = .udp
let sorted1 = original.stableSorted {
return ($0.socketType == preferredType) && ($1.socketType != preferredType)
}
XCTAssertEqual(sorted1, original)
preferredType = .tcp
let sorted2 = original.stableSorted {
return ($0.socketType == preferredType) && ($1.socketType != preferredType)
}
XCTAssertNotEqual(sorted2, original)
}
}

View File

@ -1,69 +0,0 @@
//
// UtilsTests.swift
// Passepartout-CoreTests
//
// Created by Davide De Rosa on 3/30/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import XCTest
@testable import Passepartout_Core
class UtilsTests: XCTestCase {
override func setUp() {
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testDataUnitDescription() {
XCTAssertEqual(0.dataUnitDescription, "0B")
XCTAssertEqual(1.dataUnitDescription, "1B")
XCTAssertEqual(1024.dataUnitDescription, "1kB")
XCTAssertEqual(1025.dataUnitDescription, "1kB")
XCTAssertEqual(548575.dataUnitDescription, "0.52MB")
XCTAssertEqual(1048575.dataUnitDescription, "1.00MB")
XCTAssertEqual(1048576.dataUnitDescription, "1.00MB")
XCTAssertEqual(1048577.dataUnitDescription, "1.00MB")
XCTAssertEqual(600000000.dataUnitDescription, "0.56GB")
XCTAssertEqual(1073741823.dataUnitDescription, "1.00GB")
XCTAssertEqual(1073741824.dataUnitDescription, "1.00GB")
XCTAssertEqual(1073741825.dataUnitDescription, "1.00GB")
}
func testLanguageLocalization() {
let languages = ["en", "it", "de", "pt-BR", "ru"]
let english = Locale(identifier: "en")
let italian = Locale(identifier: "it")
let languagesEN = privateSortedLanguages(languages, with: english)
let languagesIT = privateSortedLanguages(languages, with: italian)
XCTAssertEqual(languagesEN, ["en", "de", "it", "pt-BR", "ru"])
XCTAssertEqual(languagesIT, ["en", "it", "pt-BR", "ru", "de"])
}
private func privateSortedLanguages(_ languages: [String], with locale: Locale) -> [String] {
return languages.sorted {
return locale.localizedString(forLanguageCode: $0)! < locale.localizedString(forLanguageCode: $1)!
}
}
}

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.6.1</string>
<string>1.7.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>

View File

@ -25,7 +25,7 @@
import UIKit
import TunnelKit
import Passepartout_Core
import PassepartoutCore
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {

View File

@ -27,7 +27,7 @@ import Foundation
import UIKit
import TunnelKit
import SwiftyBeaver
import Passepartout_Core
import PassepartoutCore
private let log = SwiftyBeaver.self

View File

@ -26,7 +26,7 @@
import Foundation
import MBProgressHUD
import SwiftyBeaver
import Passepartout_Core
import PassepartoutCore
private let log = SwiftyBeaver.self

View File

@ -26,7 +26,7 @@
import Foundation
import TunnelKit
import MessageUI
import Passepartout_Core
import PassepartoutCore
class IssueReporter: NSObject {
struct Attachments {

View File

@ -25,7 +25,7 @@
import UIKit
import TunnelKit
import Passepartout_Core
import PassepartoutCore
extension UITableViewCell {
func applyChecked(_ checked: Bool, _ theme: Theme) {

View File

@ -26,7 +26,7 @@
import UIKit
import MessageUI
import StoreKit
import Passepartout_Core
import PassepartoutCore
extension UIColor {
convenience init(rgb: UInt32, alpha: CGFloat) {

View File

@ -34,7 +34,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.6.1</string>
<string>1.7.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>ITSAppUsesNonExemptEncryption</key>

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
class AboutViewController: UITableViewController, TableModelHost {
@ -195,10 +195,10 @@ extension AboutViewController {
openCredits()
case .readme:
visit(AppConstants.URLs.readme)
visit(AppConstants.URLs.iOS.readme)
case .changelog:
visit(AppConstants.URLs.changelog)
visit(AppConstants.URLs.iOS.changelog)
case .website:
visit(AppConstants.URLs.website)

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
class CreditsViewController: UITableViewController, TableModelHost {
private let licenses = AppConstants.License.all

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
class LabelViewController: UIViewController {
@IBOutlet private weak var scrollView: UIScrollView?

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
class VersionViewController: UIViewController {
@IBOutlet private weak var scrollView: UIScrollView?

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
protocol AccountViewControllerDelegate: class {
func accountController(_: AccountViewController, didEnterCredentials credentials: Credentials)

View File

@ -26,7 +26,7 @@
import UIKit
import TunnelKit
import SwiftyBeaver
import Passepartout_Core
import PassepartoutCore
private let log = SwiftyBeaver.self

View File

@ -25,7 +25,7 @@
import UIKit
import SwiftyBeaver
import Passepartout_Core
import PassepartoutCore
private let log = SwiftyBeaver.self

View File

@ -25,7 +25,7 @@
import UIKit
import TunnelKit
import Passepartout_Core
import PassepartoutCore
protocol EndpointViewControllerDelegate: class {
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: EndpointProtocol?)

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
import TunnelKit
import SwiftyBeaver

View File

@ -25,7 +25,7 @@
import UIKit
import StoreKit
import Passepartout_Core
import PassepartoutCore
class DonationViewController: UITableViewController, TableModelHost {
private var donationList: [InApp.Donation] = []

View File

@ -26,7 +26,7 @@
import UIKit
import TunnelKit
import SwiftyBeaver
import Passepartout_Core
import PassepartoutCore
private let log = SwiftyBeaver.self

View File

@ -26,7 +26,7 @@
import UIKit
import StoreKit
import MessageUI
import Passepartout_Core
import PassepartoutCore
// XXX: convoluted due to the separation of provider/host profiles

View File

@ -26,7 +26,7 @@
import UIKit
import TunnelKit
import SwiftyBeaver
import Passepartout_Core
import PassepartoutCore
private let log = SwiftyBeaver.self

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
class WizardProviderViewController: UITableViewController {
var availableNames: [Infrastructure.Name] = []

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
protocol ProviderPoolViewControllerDelegate: class {
func providerPoolController(_: ProviderPoolViewController, didSelectPool pool: Pool)

View File

@ -24,7 +24,7 @@
//
import UIKit
import Passepartout_Core
import PassepartoutCore
protocol ProviderPresetViewControllerDelegate: class {
func providerPresetController(_: ProviderPresetViewController, didSelectPreset preset: InfrastructurePreset)

View File

@ -28,7 +28,7 @@ import NetworkExtension
import CoreTelephony
import MBProgressHUD
import TunnelKit
import Passepartout_Core
import PassepartoutCore
class ServiceViewController: UIViewController, TableModelHost {
@IBOutlet private weak var tableView: UITableView!

View File

@ -25,7 +25,7 @@
import UIKit
import Intents
import Passepartout_Core
import PassepartoutCore
@available(iOS 12, *)
class ShortcutsAddViewController: UITableViewController, TableModelHost {

View File

@ -26,7 +26,7 @@
import UIKit
import Intents
import IntentsUI
import Passepartout_Core
import PassepartoutCore
@available(iOS 12, *)
class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewControllerDelegate, TableModelHost {

View File

@ -26,7 +26,7 @@
import UIKit
import Intents
import IntentsUI
import Passepartout_Core
import PassepartoutCore
@available(iOS 12, *)
protocol ShortcutsIntentDelegate: class {

View File

@ -20,10 +20,10 @@
0E242740225951B00064A1A3 /* InApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E24273F225951B00064A1A3 /* InApp.swift */; };
0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E242741225956AC0064A1A3 /* DonationViewController.swift */; };
0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2B493F20FCFF990094784C /* Theme+Titles.swift */; };
0E3152A4223F9EF500F61841 /* Passepartout_Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* Passepartout_Core.framework */; };
0E3152AD223F9EF500F61841 /* Passepartout_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E31529D223F9EF500F61841 /* Passepartout_Core.h */; settings = {ATTRIBUTES = (Public, ); }; };
0E3152B0223F9EF500F61841 /* Passepartout_Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* Passepartout_Core.framework */; };
0E3152B1223F9EF500F61841 /* Passepartout_Core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* Passepartout_Core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0E3152A4223F9EF500F61841 /* PassepartoutCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* PassepartoutCore.framework */; };
0E3152AD223F9EF500F61841 /* PassepartoutCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E31529D223F9EF500F61841 /* PassepartoutCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
0E3152B0223F9EF500F61841 /* PassepartoutCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* PassepartoutCore.framework */; };
0E3152B1223F9EF500F61841 /* PassepartoutCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* PassepartoutCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0E3152B9223F9F3B00F61841 /* ConnectionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */; };
0E3152BA223F9F3D00F61841 /* InfrastructureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2620CF257C0027975F /* InfrastructureTests.swift */; };
0E3152BB223FA03D00F61841 /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCF2214DA9310035E9DE /* AppConstants.swift */; };
@ -60,13 +60,15 @@
0E3152DA223FA05800F61841 /* PlaceholderConnectionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */; };
0E3152DB223FA05800F61841 /* ProfileKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D14021919F5600BB5FB2 /* ProfileKey.swift */; };
0E3152DC223FA05800F61841 /* ProviderConnectionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */; };
0E3152DD223FA06100F61841 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E05C5DF20D198B9006EE732 /* Localizable.strings */; };
0E3152DE223FA06400F61841 /* Web in Resources */ = {isa = PBXBuildFile; fileRef = 0E0EABC721DF853C0069DAE7 /* Web */; };
0E3152DF223FA1DD00F61841 /* ConnectionService.json in Resources */ = {isa = PBXBuildFile; fileRef = 0EBBE8F121822B4D00106008 /* ConnectionService.json */; };
0E3586FE225BD34800509A4D /* ActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */; };
0E36D24D2240234B006AF062 /* ShortcutsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */; };
0E36D25822403469006AF062 /* Shortcuts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E36D25A22403469006AF062 /* Shortcuts.storyboard */; };
0E36D25C224034AD006AF062 /* ShortcutsConnectToViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E36D25B224034AD006AF062 /* ShortcutsConnectToViewController.swift */; };
0E3CAFB7229AAE770008E5C8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CAF98229AAE760008E5C8 /* Localizable.strings */; };
0E3CAFB8229AAE770008E5C8 /* Intents.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CAF9A229AAE760008E5C8 /* Intents.strings */; };
0E3CAFC0229AAE770008E5C8 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CAFAD229AAE760008E5C8 /* Intents.intentdefinition */; };
0E3CAFC4229AAF8E0008E5C8 /* API in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CAFC3229AAF8E0008E5C8 /* API */; };
0E3DA371215CB5BF00B40FC9 /* VersionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3DA370215CB5BF00B40FC9 /* VersionViewController.swift */; };
0E4C9CBB20DCF0D600A0C59C /* DestructiveTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4C9CBA20DCF0D600A0C59C /* DestructiveTableViewCell.swift */; };
0E4FD7F120D58618002221FF /* Macros.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7F020D58618002221FF /* Macros.swift */; };
@ -76,8 +78,6 @@
0E57F64120C83FC5008323CF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E57F63F20C83FC5008323CF /* Main.storyboard */; };
0E57F64320C83FC7008323CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E57F64220C83FC7008323CF /* Assets.xcassets */; };
0E57F64620C83FC7008323CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E57F64420C83FC7008323CF /* LaunchScreen.storyboard */; };
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; };
0E58BF65224152F9006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; };
0E66A270225FE25800F9C779 /* PoolCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolCategory.swift */; };
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */; };
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */; };
@ -115,10 +115,10 @@
0EFBFAC121AC464800887A8C /* CreditsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFBFAC021AC464800887A8C /* CreditsViewController.swift */; };
0EFD943E215BE10800529B64 /* IssueReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFD943D215BE10800529B64 /* IssueReporter.swift */; };
0EFD9440215BED8E00529B64 /* LabelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFD943F215BED8E00529B64 /* LabelViewController.swift */; };
2774FA39ED110D958C822250 /* Pods_Passepartout_iOS_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A3A4ADF4EAADB11C595D7937 /* Pods_Passepartout_iOS_Tunnel.framework */; };
64BC3972D5FD0698BA15D741 /* Pods_Passepartout_CoreTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 330CE87BE42AD629984E05C3 /* Pods_Passepartout_CoreTests.framework */; };
65923F9A7B1F49732A1B386B /* Pods_Passepartout_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EAE1FBCD7178916560C394 /* Pods_Passepartout_iOS.framework */; };
974F713030E083F69F5E1049 /* Pods_Passepartout_Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABC62156F813CA8AE19A046B /* Pods_Passepartout_Core.framework */; };
360C6E03D6FEF3C040079A77 /* Pods_PassepartoutCoreTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 992C366F272FAA643335F68C /* Pods_PassepartoutCoreTests_iOS.framework */; };
577B90392A4798F63461B0B5 /* Pods_Passepartout_iOS_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA4CE55A94366C45DDB1E624 /* Pods_Passepartout_iOS_Tunnel.framework */; };
87121F7216E808EE79925040 /* Pods_Passepartout_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D32B24AA7767AB2E7FB6E0F /* Pods_Passepartout_iOS.framework */; };
91AF81613E19B47C06EBD447 /* Pods_PassepartoutCore_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 276375A4CF0313B033CD8B5A /* Pods_PassepartoutCore_iOS.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -152,7 +152,7 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
0E3152B1223F9EF500F61841 /* Passepartout_Core.framework in Embed Frameworks */,
0E3152B1223F9EF500F61841 /* PassepartoutCore.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -171,43 +171,52 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0006BCD0B73ABD3B2B701604 /* Pods-Passepartout-Core.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-Core.release.xcconfig"; path = "Target Support Files/Pods-Passepartout-Core/Pods-Passepartout-Core.release.xcconfig"; sourceTree = "<group>"; };
09CB728874F1553EF27BAAB9 /* Pods-Passepartout-iOS-Tunnel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS-Tunnel.debug.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS-Tunnel/Pods-Passepartout-iOS-Tunnel.debug.xcconfig"; sourceTree = "<group>"; };
0E05C5CE20D139AF006EE732 /* FieldTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldTableViewCell.swift; sourceTree = "<group>"; };
0E05C5DE20D198B9006EE732 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
0E05C5E320D1993C006EE732 /* SwiftGen+Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Strings.swift"; sourceTree = "<group>"; };
0E05C61C20D27C82006EE732 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
0E0B6CE3226F3CDF00C1B244 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
0E0B6CE4226F45B000C1B244 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Intents.strings; sourceTree = "<group>"; };
0E0B6CE5226F45B100C1B244 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
0E0EABC721DF853C0069DAE7 /* Web */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Web; sourceTree = "<group>"; };
0E1066C820E0F84A004F98B7 /* Cells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cells.swift; sourceTree = "<group>"; };
0E108485226F3CC100BA41E9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Intents.strings; sourceTree = "<group>"; };
0E158AD920E11B0B00C85A82 /* EndpointViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointViewController.swift; sourceTree = "<group>"; };
0E1D72B1213BFFCF00BA1586 /* ProviderPresetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderPresetViewController.swift; sourceTree = "<group>"; };
0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationViewController.swift; sourceTree = "<group>"; };
0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
0E242735225944060064A1A3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = "<group>"; };
0E24273B225950450064A1A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/About.storyboard; sourceTree = "<group>"; };
0E24273F225951B00064A1A3 /* InApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InApp.swift; sourceTree = "<group>"; };
0E242741225956AC0064A1A3 /* DonationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewController.swift; sourceTree = "<group>"; };
0E2B493F20FCFF990094784C /* Theme+Titles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Titles.swift"; sourceTree = "<group>"; };
0E2B494120FD16540094784C /* TransientStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransientStore.swift; sourceTree = "<group>"; };
0E2D11B9217DBEDE0096822C /* ConnectionService+Configurations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConnectionService+Configurations.swift"; sourceTree = "<group>"; };
0E31529B223F9EF400F61841 /* Passepartout_Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Passepartout_Core.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0E31529D223F9EF500F61841 /* Passepartout_Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Passepartout_Core.h; sourceTree = "<group>"; };
0E31529B223F9EF400F61841 /* PassepartoutCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PassepartoutCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0E31529D223F9EF500F61841 /* PassepartoutCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PassepartoutCore.h; sourceTree = "<group>"; };
0E31529E223F9EF500F61841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0E3152A3223F9EF500F61841 /* Passepartout-CoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Passepartout-CoreTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
0E3152A3223F9EF500F61841 /* PassepartoutCoreTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PassepartoutCoreTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
0E3152AC223F9EF500F61841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTableViewCell.swift; sourceTree = "<group>"; };
0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsAddViewController.swift; sourceTree = "<group>"; };
0E36D25B224034AD006AF062 /* ShortcutsConnectToViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConnectToViewController.swift; sourceTree = "<group>"; };
0E39BCEF214B9EF10035E9DE /* WebServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebServices.swift; sourceTree = "<group>"; };
0E39BCF2214DA9310035E9DE /* AppConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConstants.swift; sourceTree = "<group>"; };
0E3CAFC5229B033E0008E5C8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAFC7229B28310008E5C8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAF99229AAE760008E5C8 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAF9B229AAE760008E5C8 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAF9C229AAE760008E5C8 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAF9D229AAE760008E5C8 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAF9E229AAE760008E5C8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAF9F229AAE760008E5C8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAFA0229AAE760008E5C8 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAFA1229AAE760008E5C8 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAFAB229AAE760008E5C8 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAFAC229AAE760008E5C8 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAFAE229AAE760008E5C8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; };
0E3CAFAF229AAE760008E5C8 /* pt-br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-br"; path = "pt-br.lproj/Localizable.strings"; sourceTree = "<group>"; };
0E3CAFB0229AAE760008E5C8 /* pt-br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-br"; path = "pt-br.lproj/Intents.strings"; sourceTree = "<group>"; };
0E3CAFB1229AAE760008E5C8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAFB2229AAE760008E5C8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAFB3229AAE760008E5C8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAFB4229AAE760008E5C8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAFB5229AAE760008E5C8 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
0E3CAFB6229AAE760008E5C8 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Intents.strings; sourceTree = "<group>"; };
0E3CAFC3229AAF8E0008E5C8 /* API */ = {isa = PBXFileReference; lastKnownFileType = folder; path = API; sourceTree = "<group>"; };
0E3DA370215CB5BF00B40FC9 /* VersionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionViewController.swift; sourceTree = "<group>"; };
0E411BA32272183700E0852C /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Intents.strings"; sourceTree = "<group>"; };
0E411BA42272184A00E0852C /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
0E4C9CB820DB9BC600A0C59C /* TrustedNetworks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrustedNetworks.swift; sourceTree = "<group>"; };
0E4C9CBA20DCF0D600A0C59C /* DestructiveTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveTableViewCell.swift; sourceTree = "<group>"; };
0E4FD7DD20D3E49A002221FF /* StandardVPNProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardVPNProvider.swift; sourceTree = "<group>"; };
@ -215,9 +224,6 @@
0E4FD7ED20D539A0002221FF /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
0E4FD7F020D58618002221FF /* Macros.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Macros.swift; sourceTree = "<group>"; };
0E4FD80420D683A2002221FF /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/NetworkExtension.framework; sourceTree = DEVELOPER_DIR; };
0E533B0C2257BAB900EF94FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; };
0E533B102257C0F200EF94FC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Intents.strings; sourceTree = "<group>"; };
0E533B142257C1DC00EF94FC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
0E533B152258E03B00EF94FC /* PoolGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoolGroup.swift; sourceTree = "<group>"; };
0E57F63820C83FC5008323CF /* Passepartout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Passepartout.app; sourceTree = BUILT_PRODUCTS_DIR; };
0E57F63B20C83FC5008323CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -240,8 +246,6 @@
0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfrastructurePreset.swift; sourceTree = "<group>"; };
0E9CD7862257462800D033B4 /* Providers.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Providers.xcassets; sourceTree = "<group>"; };
0E9CD788225746B300D033B4 /* Flags.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Flags.xcassets; sourceTree = "<group>"; };
0E9DE06B22992DE000DEC291 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; };
0E9DE06C22992DE600DEC291 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
0EA068F3218475F800C320AD /* ConfigurationParserResult+Alerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationParserResult+Alerts.swift"; sourceTree = "<group>"; };
0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Search.swift"; sourceTree = "<group>"; };
0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportedHostsViewController.swift; sourceTree = "<group>"; };
@ -280,7 +284,7 @@
0ED38AF1214177920004D387 /* VPNProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNProvider.swift; sourceTree = "<group>"; };
0ED824C920D12B8700F2FE9E /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = "<group>"; };
0ED824CD20D12DBE00F2FE9E /* SettingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTableViewCell.swift; sourceTree = "<group>"; };
0ED993B0223FF8C700B0F9C9 /* IntentDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IntentDispatcher.swift; path = Passepartout/Sources/Intents/IntentDispatcher.swift; sourceTree = SOURCE_ROOT; };
0ED993B0223FF8C700B0F9C9 /* IntentDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IntentDispatcher.swift; path = Submodules/Core/Passepartout/Sources/Intents/IntentDispatcher.swift; sourceTree = SOURCE_ROOT; };
0EDE8DBF20C86910004C739C /* Passepartout-Tunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Passepartout-Tunnel.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
0EDE8DC320C86910004C739C /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
0EDE8DC520C86910004C739C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -289,14 +293,8 @@
0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Scenes.swift"; sourceTree = "<group>"; };
0EDE8DE620C93945004C739C /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
0EDE8DED20C93E4C004C739C /* GroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupConstants.swift; sourceTree = "<group>"; };
0EE2FA662291E4AA00F56F49 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Intents.strings; sourceTree = "<group>"; };
0EE2FA672291E50300F56F49 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
0EE4EB29229A6AF8000E4BA1 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Intents.strings; sourceTree = "<group>"; };
0EE4EB2A229A6AF8000E4BA1 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
0EEB53B1225D525B00746300 /* Downloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = "<group>"; };
0EF4EF7F229431CB0030E6EB /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Intents.strings; sourceTree = "<group>"; };
0EF4EF80229431D20030E6EB /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = "<group>"; };
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = "<group>"; };
0EF5CF242141CE58004FF1BD /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; };
0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNetworkSettings.swift; sourceTree = "<group>"; };
@ -304,17 +302,17 @@
0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = "<group>"; };
0EFD943D215BE10800529B64 /* IssueReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueReporter.swift; sourceTree = "<group>"; };
0EFD943F215BED8E00529B64 /* LabelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelViewController.swift; sourceTree = "<group>"; };
1AA0A7801E34D3A4286280AB /* Pods-Passepartout-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS.release.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS/Pods-Passepartout-iOS.release.xcconfig"; sourceTree = "<group>"; };
306E9009D8A771C5756ACC83 /* Pods-Passepartout-CoreTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-CoreTests.release.xcconfig"; path = "Target Support Files/Pods-Passepartout-CoreTests/Pods-Passepartout-CoreTests.release.xcconfig"; sourceTree = "<group>"; };
330CE87BE42AD629984E05C3 /* Pods_Passepartout_CoreTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Passepartout_CoreTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
84E4F391724F65947460C4C6 /* Pods-Passepartout-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS/Pods-Passepartout-iOS.debug.xcconfig"; sourceTree = "<group>"; };
A1EAE1FBCD7178916560C394 /* Pods_Passepartout_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Passepartout_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A3A4ADF4EAADB11C595D7937 /* Pods_Passepartout_iOS_Tunnel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Passepartout_iOS_Tunnel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
ABC62156F813CA8AE19A046B /* Pods_Passepartout_Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Passepartout_Core.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C8A8897C87D8BCB817593651 /* Pods-Passepartout-iOS-Tunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS-Tunnel.release.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS-Tunnel/Pods-Passepartout-iOS-Tunnel.release.xcconfig"; sourceTree = "<group>"; };
CA8AA77D192D2ABAABC8A4DA /* Pods-Passepartout-iOS-Tunnel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS-Tunnel.debug.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS-Tunnel/Pods-Passepartout-iOS-Tunnel.debug.xcconfig"; sourceTree = "<group>"; };
D35137256E20958EA0A025A0 /* Pods-Passepartout-CoreTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-CoreTests.debug.xcconfig"; path = "Target Support Files/Pods-Passepartout-CoreTests/Pods-Passepartout-CoreTests.debug.xcconfig"; sourceTree = "<group>"; };
D7CC6F055B1ECE468E956C00 /* Pods-Passepartout-Core.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-Core.debug.xcconfig"; path = "Target Support Files/Pods-Passepartout-Core/Pods-Passepartout-Core.debug.xcconfig"; sourceTree = "<group>"; };
0FD7B360EE444EF1CDBFDF1C /* Pods-PassepartoutCoreTests-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassepartoutCoreTests-iOS.debug.xcconfig"; path = "Target Support Files/Pods-PassepartoutCoreTests-iOS/Pods-PassepartoutCoreTests-iOS.debug.xcconfig"; sourceTree = "<group>"; };
234D3C887F46AD64480495BB /* Pods-PassepartoutCoreTests-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassepartoutCoreTests-iOS.release.xcconfig"; path = "Target Support Files/Pods-PassepartoutCoreTests-iOS/Pods-PassepartoutCoreTests-iOS.release.xcconfig"; sourceTree = "<group>"; };
276375A4CF0313B033CD8B5A /* Pods_PassepartoutCore_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PassepartoutCore_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4BD12523ABCBF7ED941FC270 /* Pods-Passepartout-iOS-Tunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS-Tunnel.release.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS-Tunnel/Pods-Passepartout-iOS-Tunnel.release.xcconfig"; sourceTree = "<group>"; };
53B773C4BAE7EDB38C7AFCD1 /* Pods-Passepartout-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS.release.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS/Pods-Passepartout-iOS.release.xcconfig"; sourceTree = "<group>"; };
6D32B24AA7767AB2E7FB6E0F /* Pods_Passepartout_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Passepartout_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
992C366F272FAA643335F68C /* Pods_PassepartoutCoreTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PassepartoutCoreTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A1C4E67627603307808F2FFC /* Pods-PassepartoutCore-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassepartoutCore-iOS.debug.xcconfig"; path = "Target Support Files/Pods-PassepartoutCore-iOS/Pods-PassepartoutCore-iOS.debug.xcconfig"; sourceTree = "<group>"; };
F1BECEC1D93FEBDB17B714D8 /* Pods-PassepartoutCore-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassepartoutCore-iOS.release.xcconfig"; path = "Target Support Files/Pods-PassepartoutCore-iOS/Pods-PassepartoutCore-iOS.release.xcconfig"; sourceTree = "<group>"; };
F4959B5F3CB1BBC2C46EA639 /* Pods-Passepartout-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS/Pods-Passepartout-iOS.debug.xcconfig"; sourceTree = "<group>"; };
FA4CE55A94366C45DDB1E624 /* Pods_Passepartout_iOS_Tunnel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Passepartout_iOS_Tunnel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -322,7 +320,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
974F713030E083F69F5E1049 /* Pods_Passepartout_Core.framework in Frameworks */,
91AF81613E19B47C06EBD447 /* Pods_PassepartoutCore_iOS.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -330,8 +328,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0E3152A4223F9EF500F61841 /* Passepartout_Core.framework in Frameworks */,
64BC3972D5FD0698BA15D741 /* Pods_Passepartout_CoreTests.framework in Frameworks */,
0E3152A4223F9EF500F61841 /* PassepartoutCore.framework in Frameworks */,
360C6E03D6FEF3C040079A77 /* Pods_PassepartoutCoreTests_iOS.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -339,9 +337,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0E3152B0223F9EF500F61841 /* Passepartout_Core.framework in Frameworks */,
0E3152B0223F9EF500F61841 /* PassepartoutCore.framework in Frameworks */,
0ED31C3D20CF396E0027975F /* NetworkExtension.framework in Frameworks */,
65923F9A7B1F49732A1B386B /* Pods_Passepartout_iOS.framework in Frameworks */,
87121F7216E808EE79925040 /* Pods_Passepartout_iOS.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -350,7 +348,7 @@
buildActionMask = 2147483647;
files = (
0ED31C3A20CF39510027975F /* NetworkExtension.framework in Frameworks */,
2774FA39ED110D958C822250 /* Pods_Passepartout_iOS_Tunnel.framework in Frameworks */,
577B90392A4798F63461B0B5 /* Pods_Passepartout_iOS_Tunnel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -391,25 +389,34 @@
path = Shortcuts;
sourceTree = "<group>";
};
0E31529C223F9EF500F61841 /* Passepartout-Core */ = {
0E31529C223F9EF500F61841 /* PassepartoutCore-iOS */ = {
isa = PBXGroup;
children = (
0E31529D223F9EF500F61841 /* Passepartout_Core.h */,
0E31529D223F9EF500F61841 /* PassepartoutCore.h */,
0E31529E223F9EF500F61841 /* Info.plist */,
);
path = "Passepartout-Core";
path = "PassepartoutCore-iOS";
sourceTree = "<group>";
};
0E3152A9223F9EF500F61841 /* Passepartout-CoreTests */ = {
0E3152A9223F9EF500F61841 /* PassepartoutTests */ = {
isa = PBXGroup;
children = (
0EBBE8F121822B4D00106008 /* ConnectionService.json */,
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */,
0ED31C2620CF257C0027975F /* InfrastructureTests.swift */,
0ECEB10B224FEF9B00E9E551 /* UtilsTests.swift */,
0E3152AC223F9EF500F61841 /* Info.plist */,
);
path = "Passepartout-CoreTests";
path = PassepartoutTests;
sourceTree = "<group>";
};
0E3CAF97229AAE760008E5C8 /* Resources */ = {
isa = PBXGroup;
children = (
0E3CAF9A229AAE760008E5C8 /* Intents.strings */,
0E3CAFAD229AAE760008E5C8 /* Intents.intentdefinition */,
0E3CAF98229AAE760008E5C8 /* Localizable.strings */,
);
path = Resources;
sourceTree = "<group>";
};
0E4FD7D920D3E43F002221FF /* VPN */ = {
@ -437,11 +444,11 @@
0E57F62F20C83FC5008323CF = {
isa = PBXGroup;
children = (
0EDE8DEF20C93EBB004C739C /* Passepartout */,
0E31529C223F9EF500F61841 /* Passepartout-Core */,
0E3152A9223F9EF500F61841 /* Passepartout-CoreTests */,
0E31529C223F9EF500F61841 /* PassepartoutCore-iOS */,
0EE4EB2B229A6FED000E4BA1 /* PassepartoutCoreTests-iOS */,
0E57F63A20C83FC5008323CF /* Passepartout-iOS */,
0EDE8DC020C86910004C739C /* Passepartout-iOS-Tunnel */,
0EE4EB2C229A71BB000E4BA1 /* Libraries */,
0E57F63920C83FC5008323CF /* Products */,
374B9F085E1148C37CF9117A /* Frameworks */,
ECB6C4CA315B2CB2AFE7ACBB /* Pods */,
@ -453,8 +460,8 @@
children = (
0E57F63820C83FC5008323CF /* Passepartout.app */,
0EDE8DBF20C86910004C739C /* Passepartout-Tunnel.appex */,
0E31529B223F9EF400F61841 /* Passepartout_Core.framework */,
0E3152A3223F9EF500F61841 /* Passepartout-CoreTests.xctest */,
0E31529B223F9EF400F61841 /* PassepartoutCore.framework */,
0E3152A3223F9EF500F61841 /* PassepartoutCoreTests-iOS.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -549,16 +556,6 @@
path = Services;
sourceTree = "<group>";
};
0ED31C1C20CF17CC0027975F /* Resources */ = {
isa = PBXGroup;
children = (
0E0EABC721DF853C0069DAE7 /* Web */,
0E58BD9122404EF1006FB157 /* Intents.intentdefinition */,
0E05C5DF20D198B9006EE732 /* Localizable.strings */,
);
path = Resources;
sourceTree = "<group>";
};
0EDE8DC020C86910004C739C /* Passepartout-iOS-Tunnel */ = {
isa = PBXGroup;
children = (
@ -593,7 +590,7 @@
0EDE8DEF20C93EBB004C739C /* Passepartout */ = {
isa = PBXGroup;
children = (
0ED31C1C20CF17CC0027975F /* Resources */,
0E3CAF97229AAE760008E5C8 /* Resources */,
0EDE8DF020C93EBB004C739C /* Sources */,
);
path = Passepartout;
@ -635,16 +632,42 @@
path = Scenes;
sourceTree = "<group>";
};
0EE4EB2B229A6FED000E4BA1 /* PassepartoutCoreTests-iOS */ = {
isa = PBXGroup;
children = (
0E3152AC223F9EF500F61841 /* Info.plist */,
);
path = "PassepartoutCoreTests-iOS";
sourceTree = "<group>";
};
0EE4EB2C229A71BB000E4BA1 /* Libraries */ = {
isa = PBXGroup;
children = (
0E3CAFC3229AAF8E0008E5C8 /* API */,
0EE4EB2E229A71DA000E4BA1 /* Core */,
);
path = Libraries;
sourceTree = "<group>";
};
0EE4EB2E229A71DA000E4BA1 /* Core */ = {
isa = PBXGroup;
children = (
0EDE8DEF20C93EBB004C739C /* Passepartout */,
0E3152A9223F9EF500F61841 /* PassepartoutTests */,
);
path = Core;
sourceTree = "<group>";
};
374B9F085E1148C37CF9117A /* Frameworks */ = {
isa = PBXGroup;
children = (
0E4FD80420D683A2002221FF /* NetworkExtension.framework */,
0ED31C3920CF39510027975F /* NetworkExtension.framework */,
0EDE8DD220C86978004C739C /* NotificationCenter.framework */,
ABC62156F813CA8AE19A046B /* Pods_Passepartout_Core.framework */,
330CE87BE42AD629984E05C3 /* Pods_Passepartout_CoreTests.framework */,
A1EAE1FBCD7178916560C394 /* Pods_Passepartout_iOS.framework */,
A3A4ADF4EAADB11C595D7937 /* Pods_Passepartout_iOS_Tunnel.framework */,
6D32B24AA7767AB2E7FB6E0F /* Pods_Passepartout_iOS.framework */,
FA4CE55A94366C45DDB1E624 /* Pods_Passepartout_iOS_Tunnel.framework */,
276375A4CF0313B033CD8B5A /* Pods_PassepartoutCore_iOS.framework */,
992C366F272FAA643335F68C /* Pods_PassepartoutCoreTests_iOS.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -652,14 +675,14 @@
ECB6C4CA315B2CB2AFE7ACBB /* Pods */ = {
isa = PBXGroup;
children = (
D7CC6F055B1ECE468E956C00 /* Pods-Passepartout-Core.debug.xcconfig */,
0006BCD0B73ABD3B2B701604 /* Pods-Passepartout-Core.release.xcconfig */,
D35137256E20958EA0A025A0 /* Pods-Passepartout-CoreTests.debug.xcconfig */,
306E9009D8A771C5756ACC83 /* Pods-Passepartout-CoreTests.release.xcconfig */,
84E4F391724F65947460C4C6 /* Pods-Passepartout-iOS.debug.xcconfig */,
1AA0A7801E34D3A4286280AB /* Pods-Passepartout-iOS.release.xcconfig */,
CA8AA77D192D2ABAABC8A4DA /* Pods-Passepartout-iOS-Tunnel.debug.xcconfig */,
C8A8897C87D8BCB817593651 /* Pods-Passepartout-iOS-Tunnel.release.xcconfig */,
F4959B5F3CB1BBC2C46EA639 /* Pods-Passepartout-iOS.debug.xcconfig */,
53B773C4BAE7EDB38C7AFCD1 /* Pods-Passepartout-iOS.release.xcconfig */,
09CB728874F1553EF27BAAB9 /* Pods-Passepartout-iOS-Tunnel.debug.xcconfig */,
4BD12523ABCBF7ED941FC270 /* Pods-Passepartout-iOS-Tunnel.release.xcconfig */,
A1C4E67627603307808F2FFC /* Pods-PassepartoutCore-iOS.debug.xcconfig */,
F1BECEC1D93FEBDB17B714D8 /* Pods-PassepartoutCore-iOS.release.xcconfig */,
0FD7B360EE444EF1CDBFDF1C /* Pods-PassepartoutCoreTests-iOS.debug.xcconfig */,
234D3C887F46AD64480495BB /* Pods-PassepartoutCoreTests-iOS.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -671,18 +694,18 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
0E3152AD223F9EF500F61841 /* Passepartout_Core.h in Headers */,
0E3152AD223F9EF500F61841 /* PassepartoutCore.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
0E31529A223F9EF400F61841 /* Passepartout-Core */ = {
0E31529A223F9EF400F61841 /* PassepartoutCore-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0E3152B6223F9EF500F61841 /* Build configuration list for PBXNativeTarget "Passepartout-Core" */;
buildConfigurationList = 0E3152B6223F9EF500F61841 /* Build configuration list for PBXNativeTarget "PassepartoutCore-iOS" */;
buildPhases = (
62FB13BEA00ABC00B4A29BCD /* [CP] Check Pods Manifest.lock */,
0A0A35FE311454E07CF67743 /* [CP] Check Pods Manifest.lock */,
0E315296223F9EF400F61841 /* Headers */,
0E315297223F9EF400F61841 /* Sources */,
0E315298223F9EF400F61841 /* Frameworks */,
@ -692,42 +715,42 @@
);
dependencies = (
);
name = "Passepartout-Core";
name = "PassepartoutCore-iOS";
productName = "Passepartout-Core";
productReference = 0E31529B223F9EF400F61841 /* Passepartout_Core.framework */;
productReference = 0E31529B223F9EF400F61841 /* PassepartoutCore.framework */;
productType = "com.apple.product-type.framework";
};
0E3152A2223F9EF500F61841 /* Passepartout-CoreTests */ = {
0E3152A2223F9EF500F61841 /* PassepartoutCoreTests-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0E3152B8223F9EF500F61841 /* Build configuration list for PBXNativeTarget "Passepartout-CoreTests" */;
buildConfigurationList = 0E3152B8223F9EF500F61841 /* Build configuration list for PBXNativeTarget "PassepartoutCoreTests-iOS" */;
buildPhases = (
659C2986645B0E1133FB185B /* [CP] Check Pods Manifest.lock */,
C049DFE2FEC5C79568EE5396 /* [CP] Check Pods Manifest.lock */,
0E31529F223F9EF500F61841 /* Sources */,
0E3152A0223F9EF500F61841 /* Frameworks */,
0E3152A1223F9EF500F61841 /* Resources */,
C3A988ABD9CCD3E4C5E86591 /* [CP] Embed Pods Frameworks */,
2825A35DA56F68B80DAF7499 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
0E3152A6223F9EF500F61841 /* PBXTargetDependency */,
);
name = "Passepartout-CoreTests";
name = "PassepartoutCoreTests-iOS";
productName = "Passepartout-CoreTests";
productReference = 0E3152A3223F9EF500F61841 /* Passepartout-CoreTests.xctest */;
productReference = 0E3152A3223F9EF500F61841 /* PassepartoutCoreTests-iOS.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
0E57F63720C83FC5008323CF /* Passepartout-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0E57F65520C83FC7008323CF /* Build configuration list for PBXNativeTarget "Passepartout-iOS" */;
buildPhases = (
D6AF52B99BBE4A3C74D968EC /* [CP] Check Pods Manifest.lock */,
8598326924C7B7B17B82A427 /* [CP] Check Pods Manifest.lock */,
0E57F63420C83FC5008323CF /* Sources */,
0E57F63520C83FC5008323CF /* Frameworks */,
0E57F63620C83FC5008323CF /* Resources */,
0EDE8DCC20C86910004C739C /* Embed App Extensions */,
0E3152B7223F9EF500F61841 /* Embed Frameworks */,
414F21AD820CC70E9B15ACA9 /* [CP] Embed Pods Frameworks */,
71085646689FD70B4B7AD683 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -744,7 +767,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 0EDE8DC920C86910004C739C /* Build configuration list for PBXNativeTarget "Passepartout-iOS-Tunnel" */;
buildPhases = (
7E3CCE2D5D070D79917AEC2C /* [CP] Check Pods Manifest.lock */,
4316D2F30EA7143E6BB85EDE /* [CP] Check Pods Manifest.lock */,
0EDE8DBB20C86910004C739C /* Sources */,
0EDE8DBC20C86910004C739C /* Frameworks */,
0EDE8DBD20C86910004C739C /* Resources */,
@ -824,14 +847,15 @@
sv,
fr,
es,
"pt-br",
);
mainGroup = 0E57F62F20C83FC5008323CF;
productRefGroup = 0E57F63920C83FC5008323CF /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
0E31529A223F9EF400F61841 /* Passepartout-Core */,
0E3152A2223F9EF500F61841 /* Passepartout-CoreTests */,
0E31529A223F9EF400F61841 /* PassepartoutCore-iOS */,
0E3152A2223F9EF500F61841 /* PassepartoutCoreTests-iOS */,
0E57F63720C83FC5008323CF /* Passepartout-iOS */,
0EDE8DBE20C86910004C739C /* Passepartout-iOS-Tunnel */,
);
@ -843,8 +867,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0E3152DD223FA06100F61841 /* Localizable.strings in Resources */,
0E3152DE223FA06400F61841 /* Web in Resources */,
0E3CAFC4229AAF8E0008E5C8 /* API in Resources */,
0E3CAFB8229AAE770008E5C8 /* Intents.strings in Resources */,
0E3CAFB7229AAE770008E5C8 /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -881,7 +906,79 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
414F21AD820CC70E9B15ACA9 /* [CP] Embed Pods Frameworks */ = {
0A0A35FE311454E07CF67743 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-PassepartoutCore-iOS-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
2825A35DA56F68B80DAF7499 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-PassepartoutCoreTests-iOS/Pods-PassepartoutCoreTests-iOS-frameworks.sh",
"${PODS_ROOT}/OpenSSL-Apple/frameworks/iPhone/openssl.framework",
"${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework",
"${BUILT_PRODUCTS_DIR}/TunnelKit/TunnelKit.framework",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
);
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TunnelKit.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PassepartoutCoreTests-iOS/Pods-PassepartoutCoreTests-iOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
4316D2F30EA7143E6BB85EDE /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Passepartout-iOS-Tunnel-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
71085646689FD70B4B7AD683 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -911,101 +1008,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Passepartout-iOS/Pods-Passepartout-iOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
62FB13BEA00ABC00B4A29BCD /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Passepartout-Core-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
659C2986645B0E1133FB185B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Passepartout-CoreTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
7E3CCE2D5D070D79917AEC2C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Passepartout-iOS-Tunnel-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C3A988ABD9CCD3E4C5E86591 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Passepartout-CoreTests/Pods-Passepartout-CoreTests-frameworks.sh",
"${PODS_ROOT}/OpenSSL-Apple/frameworks/iPhone/openssl.framework",
"${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework",
"${BUILT_PRODUCTS_DIR}/TunnelKit/TunnelKit.framework",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
);
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TunnelKit.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Passepartout-CoreTests/Pods-Passepartout-CoreTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D6AF52B99BBE4A3C74D968EC /* [CP] Check Pods Manifest.lock */ = {
8598326924C7B7B17B82A427 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1027,6 +1030,28 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C049DFE2FEC5C79568EE5396 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-PassepartoutCoreTests-iOS-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -1067,12 +1092,12 @@
0E3152CD223FA05400F61841 /* ConnectionProfile.swift in Sources */,
0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */,
0E3152C9223FA04D00F61841 /* InfrastructureFactory.swift in Sources */,
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */,
0E3152D3223FA05400F61841 /* EndpointDataSource.swift in Sources */,
0E3152D4223FA05400F61841 /* Preferences.swift in Sources */,
0EFB901822764689006405E4 /* ProfileNetworkSettings.swift in Sources */,
0E3152C0223FA03D00F61841 /* Utils.swift in Sources */,
0E3152CB223FA04D00F61841 /* Pool.swift in Sources */,
0E3CAFC0229AAE770008E5C8 /* Intents.intentdefinition in Sources */,
0E3152C7223FA04800F61841 /* VPNStatus.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1092,7 +1117,6 @@
buildActionMask = 2147483647;
files = (
0ECEE44E20E1122200A6BB43 /* TableModel.swift in Sources */,
0E58BF65224152F9006FB157 /* Intents.intentdefinition in Sources */,
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */,
0EE3BBB2215ED3A900F30952 /* AboutViewController.swift in Sources */,
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */,
@ -1153,12 +1177,12 @@
/* Begin PBXTargetDependency section */
0E3152A6223F9EF500F61841 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0E31529A223F9EF400F61841 /* Passepartout-Core */;
target = 0E31529A223F9EF400F61841 /* PassepartoutCore-iOS */;
targetProxy = 0E3152A5223F9EF500F61841 /* PBXContainerItemProxy */;
};
0E3152AF223F9EF500F61841 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0E31529A223F9EF400F61841 /* Passepartout-Core */;
target = 0E31529A223F9EF400F61841 /* PassepartoutCore-iOS */;
targetProxy = 0E3152AE223F9EF500F61841 /* PBXContainerItemProxy */;
};
0EDE8DC720C86910004C739C /* PBXTargetDependency */ = {
@ -1169,23 +1193,6 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
0E05C5DF20D198B9006EE732 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
0E05C5DE20D198B9006EE732 /* en */,
0E533B142257C1DC00EF94FC /* it */,
0E0B6CE3226F3CDF00C1B244 /* de */,
0E0B6CE5226F45B100C1B244 /* ru */,
0E411BA42272184A00E0852C /* pt-BR */,
0EE2FA672291E50300F56F49 /* nl */,
0EF4EF80229431D20030E6EB /* el */,
0E9DE06C22992DE600DEC291 /* sv */,
0EE4EB2A229A6AF8000E4BA1 /* fr */,
0E3CAFC7229B28310008E5C8 /* es */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
0E24273C225950450064A1A3 /* About.storyboard */ = {
isa = PBXVariantGroup;
children = (
@ -1202,6 +1209,46 @@
name = Shortcuts.storyboard;
sourceTree = "<group>";
};
0E3CAF98229AAE760008E5C8 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
0E3CAF99229AAE760008E5C8 /* de */,
0E3CAF9C229AAE760008E5C8 /* el */,
0E3CAF9E229AAE760008E5C8 /* en */,
0E3CAFA0229AAE760008E5C8 /* it */,
0E3CAFAB229AAE760008E5C8 /* sv */,
0E3CAFAF229AAE760008E5C8 /* pt-br */,
0E3CAFB1229AAE760008E5C8 /* ru */,
0E3CAFB3229AAE760008E5C8 /* fr */,
0E3CAFB5229AAE760008E5C8 /* nl */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
0E3CAF9A229AAE760008E5C8 /* Intents.strings */ = {
isa = PBXVariantGroup;
children = (
0E3CAF9B229AAE760008E5C8 /* de */,
0E3CAF9D229AAE760008E5C8 /* el */,
0E3CAF9F229AAE760008E5C8 /* en */,
0E3CAFA1229AAE760008E5C8 /* it */,
0E3CAFAC229AAE760008E5C8 /* sv */,
0E3CAFB0229AAE760008E5C8 /* pt-br */,
0E3CAFB2229AAE760008E5C8 /* ru */,
0E3CAFB4229AAE760008E5C8 /* fr */,
0E3CAFB6229AAE760008E5C8 /* nl */,
);
name = Intents.strings;
sourceTree = "<group>";
};
0E3CAFAD229AAE760008E5C8 /* Intents.intentdefinition */ = {
isa = PBXVariantGroup;
children = (
0E3CAFAE229AAE760008E5C8 /* Base */,
);
name = Intents.intentdefinition;
sourceTree = "<group>";
};
0E57F63F20C83FC5008323CF /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
@ -1218,24 +1265,6 @@
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
0E58BD9122404EF1006FB157 /* Intents.intentdefinition */ = {
isa = PBXVariantGroup;
children = (
0E533B0C2257BAB900EF94FC /* Base */,
0E533B102257C0F200EF94FC /* it */,
0E242735225944060064A1A3 /* en */,
0E108485226F3CC100BA41E9 /* de */,
0E0B6CE4226F45B000C1B244 /* ru */,
0E411BA32272183700E0852C /* pt-BR */,
0EE2FA662291E4AA00F56F49 /* nl */,
0EF4EF7F229431CB0030E6EB /* el */,
0E9DE06B22992DE000DEC291 /* sv */,
0EE4EB29229A6AF8000E4BA1 /* fr */,
0E3CAFC5229B033E0008E5C8 /* es */,
);
name = Intents.intentdefinition;
sourceTree = "<group>";
};
0ED38ADC213F44D00004D387 /* Organizer.storyboard */ = {
isa = PBXVariantGroup;
children = (
@ -1249,7 +1278,7 @@
/* Begin XCBuildConfiguration section */
0E3152B2223F9EF500F61841 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D7CC6F055B1ECE468E956C00 /* Pods-Passepartout-Core.debug.xcconfig */;
baseConfigurationReference = A1C4E67627603307808F2FFC /* Pods-PassepartoutCore-iOS.debug.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CODE_SIGN_IDENTITY = "iPhone Developer";
@ -1260,7 +1289,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "Passepartout-Core/Info.plist";
INFOPLIST_FILE = "PassepartoutCore-iOS/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -1269,8 +1298,8 @@
);
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.algoritmico.ios.Passepartout-Core";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PRODUCT_BUNDLE_IDENTIFIER = com.algoritmico.ios.PassepartoutCore;
PRODUCT_NAME = PassepartoutCore;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -1281,7 +1310,7 @@
};
0E3152B3223F9EF500F61841 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0006BCD0B73ABD3B2B701604 /* Pods-Passepartout-Core.release.xcconfig */;
baseConfigurationReference = F1BECEC1D93FEBDB17B714D8 /* Pods-PassepartoutCore-iOS.release.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CODE_SIGN_IDENTITY = "iPhone Developer";
@ -1292,7 +1321,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "Passepartout-Core/Info.plist";
INFOPLIST_FILE = "PassepartoutCore-iOS/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -1300,8 +1329,8 @@
"@loader_path/Frameworks",
);
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.algoritmico.ios.Passepartout-Core";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PRODUCT_BUNDLE_IDENTIFIER = com.algoritmico.ios.PassepartoutCore;
PRODUCT_NAME = PassepartoutCore;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -1312,10 +1341,10 @@
};
0E3152B4223F9EF500F61841 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D35137256E20958EA0A025A0 /* Pods-Passepartout-CoreTests.debug.xcconfig */;
baseConfigurationReference = 0FD7B360EE444EF1CDBFDF1C /* Pods-PassepartoutCoreTests-iOS.debug.xcconfig */;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = "Passepartout-CoreTests/Info.plist";
INFOPLIST_FILE = "PassepartoutCoreTests-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -1324,7 +1353,7 @@
);
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.algoritmico.ios.Passepartout-CoreTests";
PRODUCT_BUNDLE_IDENTIFIER = com.algoritmico.ios.PassepartoutCoreTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -1333,10 +1362,10 @@
};
0E3152B5223F9EF500F61841 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 306E9009D8A771C5756ACC83 /* Pods-Passepartout-CoreTests.release.xcconfig */;
baseConfigurationReference = 234D3C887F46AD64480495BB /* Pods-PassepartoutCoreTests-iOS.release.xcconfig */;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = "Passepartout-CoreTests/Info.plist";
INFOPLIST_FILE = "PassepartoutCoreTests-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -1344,7 +1373,7 @@
"@loader_path/Frameworks",
);
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.algoritmico.ios.Passepartout-CoreTests";
PRODUCT_BUNDLE_IDENTIFIER = com.algoritmico.ios.PassepartoutCoreTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -1481,7 +1510,7 @@
};
0E57F65620C83FC7008323CF /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 84E4F391724F65947460C4C6 /* Pods-Passepartout-iOS.debug.xcconfig */;
baseConfigurationReference = F4959B5F3CB1BBC2C46EA639 /* Pods-Passepartout-iOS.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@ -1505,7 +1534,7 @@
};
0E57F65720C83FC7008323CF /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 1AA0A7801E34D3A4286280AB /* Pods-Passepartout-iOS.release.xcconfig */;
baseConfigurationReference = 53B773C4BAE7EDB38C7AFCD1 /* Pods-Passepartout-iOS.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@ -1528,7 +1557,7 @@
};
0EDE8DCA20C86910004C739C /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = CA8AA77D192D2ABAABC8A4DA /* Pods-Passepartout-iOS-Tunnel.debug.xcconfig */;
baseConfigurationReference = 09CB728874F1553EF27BAAB9 /* Pods-Passepartout-iOS-Tunnel.debug.xcconfig */;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "Passepartout-iOS-Tunnel/Tunnel.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer";
@ -1552,7 +1581,7 @@
};
0EDE8DCB20C86910004C739C /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C8A8897C87D8BCB817593651 /* Pods-Passepartout-iOS-Tunnel.release.xcconfig */;
baseConfigurationReference = 4BD12523ABCBF7ED941FC270 /* Pods-Passepartout-iOS-Tunnel.release.xcconfig */;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "Passepartout-iOS-Tunnel/Tunnel.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer";
@ -1576,7 +1605,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0E3152B6223F9EF500F61841 /* Build configuration list for PBXNativeTarget "Passepartout-Core" */ = {
0E3152B6223F9EF500F61841 /* Build configuration list for PBXNativeTarget "PassepartoutCore-iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0E3152B2223F9EF500F61841 /* Debug */,
@ -1585,7 +1614,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0E3152B8223F9EF500F61841 /* Build configuration list for PBXNativeTarget "Passepartout-CoreTests" */ = {
0E3152B8223F9EF500F61841 /* Build configuration list for PBXNativeTarget "PassepartoutCoreTests-iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0E3152B4223F9EF500F61841 /* Debug */,

View File

@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E31529A223F9EF400F61841"
BuildableName = "Passepartout_Core.framework"
BlueprintName = "Passepartout-Core"
BuildableName = "PassepartoutCore.framework"
BlueprintName = "PassepartoutCore-iOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@ -33,8 +33,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E3152A2223F9EF500F61841"
BuildableName = "Passepartout-CoreTests.xctest"
BlueprintName = "Passepartout-CoreTests"
BuildableName = "PassepartoutCoreTests-iOS.xctest"
BlueprintName = "PassepartoutCoreTests-iOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</TestableReference>
@ -43,8 +43,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E31529A223F9EF400F61841"
BuildableName = "Passepartout_Core.framework"
BlueprintName = "Passepartout-Core"
BuildableName = "PassepartoutCore.framework"
BlueprintName = "PassepartoutCore-iOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</MacroExpansion>
@ -65,8 +65,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E31529A223F9EF400F61841"
BuildableName = "Passepartout_Core.framework"
BlueprintName = "Passepartout-Core"
BuildableName = "PassepartoutCore.framework"
BlueprintName = "PassepartoutCore-iOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</MacroExpansion>
@ -83,8 +83,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E31529A223F9EF400F61841"
BuildableName = "Passepartout_Core.framework"
BlueprintName = "Passepartout-Core"
BuildableName = "PassepartoutCore.framework"
BlueprintName = "PassepartoutCore-iOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</MacroExpansion>

View File

@ -57,8 +57,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E3152A2223F9EF500F61841"
BuildableName = "Passepartout-CoreTests.xctest"
BlueprintName = "Passepartout-CoreTests"
BuildableName = "PassepartoutCoreTests-iOS.xctest"
BlueprintName = "PassepartoutCoreTests-iOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</TestableReference>

View File

@ -1,673 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>INEnums</key>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.0</string>
<key>INIntentDefinitionSystemVersion</key>
<string>18D42</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>10E125</string>
<key>INIntentDefinitionToolsVersion</key>
<string>10.2</string>
<key>INIntents</key>
<array>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Connects to a host profile</string>
<key>INIntentDescriptionID</key>
<string>eXXb2z</string>
<key>INIntentLastParameterTag</key>
<integer>5</integer>
<key>INIntentName</key>
<string>ConnectVPN</string>
<key>INIntentParameterCombinations</key>
<dict>
<key>context,profileId</key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string></string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>uMFvJW</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string>Connect to ${profileId}</string>
<key>INIntentParameterCombinationTitleID</key>
<string>U6o81V</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>context</string>
<key>INIntentParameterSupportsMultipleValues</key>
<false/>
<key>INIntentParameterTag</key>
<integer>5</integer>
<key>INIntentParameterType</key>
<string>String</string>
</dict>
<dict>
<key>INIntentParameterDisplayPriority</key>
<integer>2</integer>
<key>INIntentParameterName</key>
<string>profileId</string>
<key>INIntentParameterSupportsMultipleValues</key>
<false/>
<key>INIntentParameterTag</key>
<integer>4</integer>
<key>INIntentParameterType</key>
<string>String</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>uKU9RD</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>WNbRl5</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Connect to VPN</string>
<key>INIntentTitleID</key>
<string>LA99yM</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Do</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Adds current Wi-Fi to trusted networks</string>
<key>INIntentDescriptionID</key>
<string>BKxs8X</string>
<key>INIntentLastParameterTag</key>
<integer>0</integer>
<key>INIntentName</key>
<string>TrustCurrentNetwork</string>
<key>INIntentParameterCombinations</key>
<dict>
<key></key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string></string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>AxbXUn</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string></string>
<key>INIntentParameterCombinationTitleID</key>
<string>POyDPM</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array/>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>x3pVKZ</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>qEDn4J</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Trust current Wi-Fi</string>
<key>INIntentTitleID</key>
<string>m2E7SI</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Do</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Disables the VPN service</string>
<key>INIntentDescriptionID</key>
<string>eQ1yzr</string>
<key>INIntentLastParameterTag</key>
<integer>0</integer>
<key>INIntentName</key>
<string>DisableVPN</string>
<key>INIntentParameterCombinations</key>
<dict>
<key></key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string></string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>85kxu8</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string></string>
<key>INIntentParameterCombinationTitleID</key>
<string>IeGsEq</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array/>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>fnSNbT</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>oKHXZ3</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Disable VPN</string>
<key>INIntentTitleID</key>
<string>1ZRTCZ</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Do</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Removes current Wi-Fi from trusted networks</string>
<key>INIntentDescriptionID</key>
<string>7eoAss</string>
<key>INIntentLastParameterTag</key>
<integer>0</integer>
<key>INIntentName</key>
<string>UntrustCurrentNetwork</string>
<key>INIntentParameterCombinations</key>
<dict>
<key></key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string></string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>pb3MGt</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string></string>
<key>INIntentParameterCombinationTitleID</key>
<string>0Wu9nb</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array/>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>r0ZjO4</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>rtaAzk</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Untrust current Wi-Fi</string>
<key>INIntentTitleID</key>
<string>rd1T8p</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Do</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Adds cellular to trusted networks</string>
<key>INIntentDescriptionID</key>
<string>9GpJt5</string>
<key>INIntentLastParameterTag</key>
<integer>0</integer>
<key>INIntentName</key>
<string>TrustCellularNetwork</string>
<key>INIntentParameterCombinations</key>
<dict>
<key></key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string></string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>vIPVA5</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string></string>
<key>INIntentParameterCombinationTitleID</key>
<string>NWWgCl</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array/>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>nMRaxS</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>gSqy7Y</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Trust cellular network</string>
<key>INIntentTitleID</key>
<string>H4taev</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Do</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Removes cellular from trusted networks</string>
<key>INIntentDescriptionID</key>
<string>0jRWn5</string>
<key>INIntentLastParameterTag</key>
<integer>0</integer>
<key>INIntentName</key>
<string>UntrustCellularNetwork</string>
<key>INIntentParameterCombinations</key>
<dict>
<key></key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string></string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>qRkLSU</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string></string>
<key>INIntentParameterCombinationTitleID</key>
<string>ggzKA2</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array/>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>YncGoj</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>BW8KLX</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Untrust cellular network</string>
<key>INIntentTitleID</key>
<string>wB1iYX</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Do</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Connects to a specific location of a provider profile</string>
<key>INIntentDescriptionID</key>
<string>KjkCfU</string>
<key>INIntentLastParameterTag</key>
<integer>3</integer>
<key>INIntentName</key>
<string>MoveToLocation</string>
<key>INIntentParameterCombinations</key>
<dict>
<key>providerId,poolName,poolId</key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<false/>
<key>INIntentParameterCombinationSubtitle</key>
<string>With ${providerId} provider</string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>66bZBE</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string>Connect to ${poolName}</string>
<key>INIntentParameterCombinationTitleID</key>
<string>WnTPFg</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>providerId</string>
<key>INIntentParameterSupportsMultipleValues</key>
<false/>
<key>INIntentParameterTag</key>
<integer>2</integer>
<key>INIntentParameterType</key>
<string>String</string>
</dict>
<dict>
<key>INIntentParameterDisplayPriority</key>
<integer>2</integer>
<key>INIntentParameterName</key>
<string>poolId</string>
<key>INIntentParameterSupportsMultipleValues</key>
<false/>
<key>INIntentParameterTag</key>
<integer>3</integer>
<key>INIntentParameterType</key>
<string>String</string>
</dict>
<dict>
<key>INIntentParameterDisplayPriority</key>
<integer>3</integer>
<key>INIntentParameterName</key>
<string>poolName</string>
<key>INIntentParameterSupportsMultipleValues</key>
<false/>
<key>INIntentParameterTag</key>
<integer>1</integer>
<key>INIntentParameterType</key>
<string>String</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>PmWNkC</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>O6nCLQ</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Connect to provider location</string>
<key>INIntentTitleID</key>
<string>qo3Szz</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Go</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>generic</string>
<key>INIntentDescription</key>
<string>Enables the VPN service with the profile currently in use</string>
<key>INIntentDescriptionID</key>
<string>xY97Vu</string>
<key>INIntentLastParameterTag</key>
<integer>0</integer>
<key>INIntentName</key>
<string>EnableVPN</string>
<key>INIntentParameterCombinations</key>
<dict>
<key></key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string>With profile in use</string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>NCoK9B</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string></string>
<key>INIntentParameterCombinationTitleID</key>
<string>yesvFP</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array/>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>FJp18d</string>
<key>INIntentResponseCodeName</key>
<string>failure</string>
<key>INIntentResponseCodeSuccess</key>
<false/>
</dict>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string></string>
<key>INIntentResponseCodeFormatStringID</key>
<string>lV5LVw</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>0</integer>
<key>INIntentResponseParameters</key>
<array/>
</dict>
<key>INIntentRestrictions</key>
<integer>0</integer>
<key>INIntentTitle</key>
<string>Enable VPN</string>
<key>INIntentTitleID</key>
<string>lQ6ziK</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentUserConfirmationRequired</key>
<false/>
<key>INIntentVerb</key>
<string>Do</string>
</dict>
</array>
</dict>
</plist>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Entfernt Mobilfunknetz von vertrauten Netzwerken";
"1ZRTCZ" = "VPN deaktivieren";
"66bZBE" = "Mit Anbieter ${providerId}";
"7eoAss" = "Entferne aktuelles WLAN von vertrauten Netzwerken";
"9GpJt5" = "Fügt Mobilnetz zu vertrauten Netzwerken hinzu";
"BKxs8X" = "Fügt aktuelles WLAN zu vertrauten Netzwerken hinzu";
"H4taev" = "Mobilfunknetz vertrauen";
"KjkCfU" = "Connects to a specific location of a provider profile";
"LA99yM" = "Verbinde mit VPN";
"U6o81V" = "Verbinde mit ${profileId}";
"WnTPFg" = "Verbinde mit ${poolName}";
"eQ1yzr" = "Deaktiviert den VPN-Dienst";
"eXXb2z" = "Verbindet mit einem Hostprofil";
"lQ6ziK" = "Aktiviere VPN";
"m2E7SI" = "Vertraue aktuellem WLAN";
"qo3Szz" = "Verbinde mit Anbieter-Ort";
"rd1T8p" = "Aktuellem WLAN nicht vertrauen";
"wB1iYX" = "Mobilfunknetz nicht vertrauen";
"xY97Vu" = "Aktiviert den VPN-Dienst mit dem derzeitig benutzten Profil";
"NCoK9B" = "Mit dem benutzten Profil";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 4/23/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Abbrechen";
"global.next" = "Weiter";
"global.close" = "Schließen";
"global.host.title_input.message" = "Gültige Zeichen beinhalten Buchstaben und Zahlen sowie Bindestrich \"-\", Unterstrich \"_\" und Punkt \".\".";
"global.host.title_input.placeholder" = "Mein Profil";
"global.email_not_configured" = "Es wurde kein Email-Account konfiguriert.";
"global.cells.enabled" = "Aktiviert";
"global.cells.disabled" = "Deaktiviert";
"global.cells.none" = "Keine";
"global.cells.automatic" = "Automatisch";
"global.cells.manual" = "Manuell";
"reddit.title" = "Reddit";
"reddit.message" = "Wusstest du, daß Passepartout einen Subreddit hat? Abonniere ihn für Updates oder um Features, Probleme, neue Plattformen zu diskutieren - oder was auch immer du möchtest.\n\nDies ist auch ein guter Weg zu zeigen dass dir dieses Projekt etwas bedeutet.";
"reddit.buttons.subscribe" = "Jetzt abbonnieren!";
"reddit.buttons.remind" = "Später erinnern";
"reddit.buttons.never" = "Nicht erneut fragen";
"organizer.sections.providers.header" = "Anbieter";
"organizer.sections.providers.footer" = "Hier findest du einige Anbieter mit voreingestellten Konfigurationsprofilen.";
"organizer.sections.hosts.header" = "Hosts";
"organizer.sections.hosts.footer" = "Importiere Hosts aus .ovpn Konfigurationsdateien.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Erhalte Hilfe von Siri um deine üblichen Interaktionen mit der App zu beschleunigen.";
"organizer.sections.support.header" = "Support";
"organizer.sections.feedback.header" = "Feedback";
"organizer.cells.profile.value.current" = "In Benutzung";
"organizer.cells.add_provider.caption" = "Neuen Anbieter hinzufügen";
"organizer.cells.add_host.caption" = "Neuen Host hinzufügen";
"organizer.cells.siri_shortcuts.caption" = "Kurzbefehle verwalten";
"organizer.cells.join_community.caption" = "Community beitreten";
"organizer.cells.write_review.caption" = "Rezension schreiben";
"organizer.cells.donate.caption" = "Spenden";
"organizer.cells.patreon.caption" = "Unterstütze mich bei Patreon";
"organizer.cells.translate.caption" = "Übersetzung anbieten";
"organizer.cells.about.caption" = "Über %@";
"organizer.cells.uninstall.caption" = "VPN-Konfiguration entfernen";
"organizer.alerts.exhausted_providers.message" = "Du hast Profile für alle verfügbaren Anbieter erstellt.";
"organizer.alerts.add_host.message" = "Öffne eine URL zu einer .ovpn-Konfigurationsdatei aus Safari, Mail oder anderen App um ein Host-Profil einzurichten.\n\nDu kannst auch eine .ovpn-Datei mit iTunes Dateifreigabe importieren.";
"organizer.alerts.cannot_donate.message" = "Auf diesem Gerät ist keine Bezahlmethode konfiguriert.";
"organizer.alerts.delete_vpn_profile.message" = "Möchtest du wirklich die VPN-Konfiguration aus deinen Geräte-Einstellungen löschen? Dies behebt möglicherweise manche kaputten VPN-Zustände und beeinflusst nicht deine Anbieter und Hosts-Profile.";
"wizards.host.cells.title_input.caption" = "Titel";
"wizards.host.sections.existing.header" = "Bestehende Profile";
"wizards.host.alerts.existing.message" = "Ein Host-Profil mit identischem Titel existiert bereits. Ersetzen?";
"parsed_file.alerts.malformed.message" = "Die Konfigurations-Datei enthält eine ungültige Option (%@).";
"parsed_file.alerts.missing.message" = "Die Konfigurations-Datei enthält eine benötigte Option nicht (%@).";
"parsed_file.alerts.unsupported.message" = "Die Konfigurations-Datei enthält eine nicht unterstützte Option (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "Die Konfigurations-Datei ist korrekt, enthält aber möglicherweise eine nicht unterstützte Option (%@).\n\nDie Verbindung kann, abhängig von den Server-Einstellungen, unterbrochen werden.";
"parsed_file.alerts.encryption_passphrase.message" = "Bitte die Verschlüsselungs-Passphrase eingeben.";
"parsed_file.alerts.decryption.message" = "Die Konfiguration enthält einen verschlüsselten Private Key und konnte nicht entschlüsselt werden. Bitte überprüfe ob du die Passphrase eingegeben hast.";
"parsed_file.alerts.parsing.message" = "Fehler beim Verarbeiten der Konfigurationsdatei (%@).";
"parsed_file.alerts.buttons.report" = "Ein Problem melden";
"imported_hosts.title" = "Importierte Hosts";
"service.welcome.message" = "Willkommen bei Passepartout!\n\nBenutze den Organizer um ein neues Profil hinzuzufügen.";
"service.sections.general.header" = "Allgemein";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "Die Verbindung wird immer aufgebaut wenn notwendig.";
"service.sections.status.header" = "Verbindung";
"service.sections.configuration.header" = "Konfiguration";
"service.sections.provider_infrastructure.footer" = "Zuletzt aktualisiert am %@.";
"service.sections.vpn_survives_sleep.footer" = "Deaktivieren um die Batterielaufzeit zu verbessern, allerdings verzögert sich der Verbindungsaufbau beim Aufwachen.";
"service.sections.vpn_resolves_hostname.footer" = "Bevorzugt in den meisten Netzwerken und benötigt in manchen IPv6 Netzwerken. Deaktivieren wo DNS geblockt ist oder um die Aushandlung zu beschleunigen bei langsam antwortenden DNS.";
//"service.sections.vpn_prefers_udp.footer" = "UDP is faster than TCP, but may not work in some networks. Disable in networks where UDP might be blocked.";
"service.sections.trusted.header" = "Vertrauenswürdige Netzwerke";
"service.sections.trusted.footer" = "Wenn ein vertrauenswürdiges Netzwerk verbunden wird, wird normalerweise die VPN-Verbindung beendet und bleibt deaktiviert. Deaktiviere diese Option um dieses Verhalten zu unterbinden.";
"service.sections.diagnostics.header" = "Diagnose";
"service.sections.diagnostics.footer" = "Zensier-Status wird aktiv nach erneutem Verbinden. Netzwerk-Daten sind Hostnamen, IP-Adressen, Routingtabellen, SSID. Zugangsdaten und Private Keys werden nie gelogged.";
//"service.sections.destruction.footer" = "Delete configuration from device settings.";
"service.cells.use_profile.caption" = "Dieses Profil verwenden";
"service.cells.vpn_service.caption" = "Aktiviert";
"service.cells.connection_status.caption" = "Status";
"service.cells.reconnect.caption" = "Erneut verbinden";
"service.cells.account.caption" = "Account";
"service.cells.account.none" = "Keiner konfiguriert";
"service.cells.endpoint.caption" = "Endpoint";
"service.cells.provider.pool.caption" = "Ort";
"service.cells.provider.preset.caption" = "Voreinstellung";
"service.cells.provider.refresh.caption" = "Infrastruktur neu laden";
"service.cells.host.parameters.caption" = "Parameter";
"service.cells.network_settings.caption" = "Netzwerk-Einstellungen";
"service.cells.vpn_survives_sleep.caption" = "Verbindung aktiv halten trotz Schlafmodus";
"service.cells.vpn_resolves_hostname.caption" = "Server Hostname auflösen";
//"service.cells.vpn_prefers_udp.caption" = "Prefer UDP socket";
"service.cells.trusted_mobile.caption" = "Mobilfunknetz";
"service.cells.trusted_add_wifi.caption" = "Aktuelles WLAN hinzufügen";
"service.cells.trusted_policy.caption" = "Vertrauen deaktiviert VPN";
"service.cells.test_connectivity.caption" = "Verbindung testen";
"service.cells.data_count.caption" = "Ausgetauschte Datenmenge";
"service.cells.data_count.none" = "Nicht verfügbar";
"service.cells.debug_log.caption" = "Debug log";
"service.cells.masks_private_data.caption" = "Netzwerkdaten zensieren";
"service.cells.report_issue.caption" = "Verbindungsproblem melden";
"service.alerts.rename.title" = "Profil umbenennen";
"service.alerts.credentials_needed.message" = "Du musst zuerst die Account-Zugangsdaten eingeben.";
"service.alerts.reconnect_vpn.message" = "Möchtest du erneut zum VPN verbinden?";
"service.alerts.trusted.no_network.message" = "Du bist mit keinem WLAN verbunden.";
"service.alerts.trusted.will_disconnect_trusted.message" = "In dem du diesem Netzwerk vertraust, wird das VPN getrennt. Weiter?";
"service.alerts.trusted.will_disconnect_policy.message" = "Durch das Ändern der Vertrauens-Policy könnte das VPN deaktiviert werden. Weiter?";
"service.alerts.test_connectivity.title" = "Konnektivität";
"service.alerts.test_connectivity.messages.success" = "Dein Gerät ist mit dem Internet verbunden!";
"service.alerts.test_connectivity.messages.failure" = "Dein Gerät hat keine Verbindung mit dem Internet, bitte prüfe deine Profil-Parameter.";
"service.alerts.masks_private_data.messages.must_reconnect" = "Um das aktuelle Debug-Log sicher zurückzusetzen und die neuen Zensier-Paramenter anzuwenden, musst du das VPN jetzt erneut verbinden.";
"service.alerts.buttons.reconnect" = "Erneut verbinden";
"service.alerts.download.title" = "Download benötigt";
"service.alerts.download.message" = "%@ benötigt den Download von zusätzlichen Konfigurationsdateien.\n\nBestätige um mit dem Download zu beginnen.";
"service.alerts.download.failed" = "Herunterladen der Konfigurationsdateien fehlgeschlagen. %@";
"service.alerts.download.hud.extracting" = "Extrahiere Dateien, bitte warten...";
"account.sections.credentials.header" = "Zugangsdaten";
"account.sections.guidance.footer.infrastructure.mullvad" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise numerischt.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise deine Email.";
"account.sections.guidance.footer.infrastructure.pia" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise numerischt mit einem \"p\" Präfix.";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Deine Zugangsdaten für %@ findest du unter \"Account > OpenVPN / IKEv2 Username\" auf der Webseite.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise deine Email.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise deine Email.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Deine Zugangsdaten für %@ findest du im OpenVPN Config Generator auf der Webseite.";
"account.sections.registration.footer" = "Beantrage einen Account auf der %@ Webseite.";
"account.cells.username.caption" = "Benutzername";
"account.cells.username.placeholder" = "Benutzername";
"account.cells.password.caption" = "Passwort";
"account.cells.password.placeholder" = "Geheim";
//"account.cells.password_confirm.caption" = "Confirm";
//"account.cells.password_confirm.mismatch" = "Passwords don't match!";
"account.cells.open_guide.caption" = "Siehe deine Zugangsdaten";
"account.cells.signup.caption" = "Registrieren bei %@";
"endpoint.sections.location_addresses.header" = "Adressen";
"endpoint.sections.location_protocols.header" = "Protokolle";
"endpoint.cells.any_address.caption" = "Automatisch";
"endpoint.cells.any_protocol.caption" = "Automatisch";
"provider.preset.cells.tech_details.caption" = "Technische Details";
//"provider.preset.sections.main.footer" = "Tap info button to disclose technical details.";
"configuration.sections.communication.header" = "Kommunikation";
"configuration.sections.reset.footer" = "Wenn du nach einer Änderung der Kommunikations-Parameter dich nicht mehr verbinden kannst, hier tippen um zur originalen Konfiguration zurückzukehren.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Komprimierung";
"configuration.sections.network.header" = "Netzwerk";
"configuration.sections.other.header" = "Andere";
"configuration.cells.cipher.caption" = "Chiffre";
"configuration.cells.digest.caption" = "Authentifizierung";
"configuration.cells.digest.value.embedded" = "Eingebettet";
"configuration.cells.reset_original.caption" = "Konfiguration zurücksetzen";
"configuration.cells.client.caption" = "Client Zertifikat";
"configuration.cells.client.value.enabled" = "Geprüft";
"configuration.cells.client.value.disabled" = "Nicht geprüft";
"configuration.cells.tls_wrapping.caption" = "Wrapping";
"configuration.cells.tls_wrapping.value.auth" = "Authentifizierung";
"configuration.cells.tls_wrapping.value.crypt" = "Verschlüsselung";
"configuration.cells.eku.caption" = "Erweiterte Verifizierung";
"configuration.cells.default_gateway.caption" = "Standart-Gateway";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Domäne";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Framing";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Algorithmus";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Nicht unterstützt";
"configuration.cells.keep_alive.caption" = "Keep-alive";
"configuration.cells.keep_alive.value.seconds" = "%d Sekunden";
"configuration.cells.renegotiation_seconds.caption" = "erneute Aushandlung";
"configuration.cells.renegotiation_seconds.value.after" = "nach %@";
"configuration.cells.random_endpoint.caption" = "Endpunkt zufällig wählen";
"network_settings.cells.choice.client" = ".ovpn-Datei einlesen";
"network_settings.cells.choice.server" = "Vom Server holen";
"network_settings.cells.address.caption" = "Adresse";
"network_settings.cells.port.caption" = "Port";
"network_settings.cells.add_dns_server.caption" = "Adresse hinzufügen";
"network_settings.cells.proxy_bypass.caption" = "Domäne umgehen";
"network_settings.cells.add_proxy_bypass.caption" = "Zu umgehende Domäne hinzufügen";
"debug_log.buttons.previous" = "Zurück";
"debug_log.buttons.next" = "Weiter";
"debug_log.alerts.empty_log.message" = "Das Debug-Log ist leer.";
"vpn.connecting" = "Verbinde";
"vpn.active" = "Aktiv";
"vpn.disconnecting" = "Trenne";
"vpn.inactive" = "Inaktiv";
"vpn.disabled" = "Deaktiviert";
"vpn.errors.timeout" = "Timeout";
"vpn.errors.dns" = "DNS fehlgeschlagen";
"vpn.errors.auth" = "Authentifizierung fehlgeschlagen";
"vpn.errors.tls" = "TLS fehlgeschlagen";
"vpn.errors.encryption" = "Verschlüsselung fehlgeschlagen";
"vpn.errors.compression" = "Komprimierung nicht unterstützt";
"vpn.errors.network" = "Netzwerk geändert";
"vpn.errors.routing" = "Kein Routing";
"vpn.errors.gateway" = "Kein Gateway";
"issue_reporter.title" = "Problem melden";
"issue_reporter.message" = "Das Debug-Log deiner letzten Verbindung ist notwendig um dein Verbindungs-Problem zu untersuchen und ist vollständig anonymisiert.\n\nDie .ovpn-Konfigurations-Datei, sofern vorhanden, wird anonymisiert von jeglichen sensiblen Daten, angehangen.\n\nBitte prüfe im Zweifelsfall die Email-Anhänge.";
"issue_reporter.buttons.accept" = "Ich verstehe";
"translations.title" = "Übersetzungen";
"shortcuts.add.title" = "Füge Kurzbefehl hinzu";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "WLAN";
"shortcuts.add.sections.cellular.header" = "Mobilfunknetz";
"shortcuts.add.cells.connect.caption" = "Verbinde mit";
"shortcuts.add.cells.enable_vpn.caption" = "Aktiviere VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Deaktiviere VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Vertraue aktivem WLAN";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Misstraue aktivem WLAN";
"shortcuts.add.cells.trust_cellular.caption" = "Vertraue Mobilfunknetz";
"shortcuts.add.cells.untrust_cellular.caption" = "Misstraue Mobilfunknetz";
"shortcuts.add.alerts.no_profiles.message" = "Es gibt kein Profil mit dem eine Verbindung hergestellt werden kann.";
"shortcuts.edit.title" = "Kurzbefehle bearbeiten";
"shortcuts.edit.sections.all.header" = "Existierende Kurzbefehle";
"shortcuts.edit.cells.add_shortcut.caption" = "Kurzbefehl hinzufügen";
"about.title" = "Über";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Teilen";
"about.cells.credits.caption" = "Credits";
"about.cells.website.caption" = "Homepage";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Haftungsausschluss";
"about.cells.privacy_policy.caption" = "Datenschutzrichtlinie";
"about.cells.share_twitter.caption" = "Darüber Twittern!";
"about.cells.share_generic.caption" = "Freund einladen";
"donation.title" = "Spenden";
"donation.sections.one_time.header" = "Einmalig";
"donation.sections.one_time.footer" = "Wenn du dich erkenntlich zeigen möchtest für meine Arbeit, gibt es hier ein paar Beträge die du direkt spenden kannst.\n\nDu bezahlst pro Spende nur einmal und kannst mehrmals spenden wenn du möchtest.";
"donation.cells.loading.caption" = "Lade Spenden";
"donation.cells.purchasing.caption" = "Führe Spende durch";
"donation.alerts.purchase.success.title" = "Danke";
"donation.alerts.purchase.success.message" = "Das bedeutet mir viel und ich hoffe wirklich dass du die App weiterhin benutzt und unterstützt.";
"donation.alerts.purchase.failure.message" = "Konnte Spende nicht durchführen. %@";
"share.message" = "Passepartout ist ein Benutzerfreundlicher, Open Source OpenVPN client für iOS und macOS";
"version.title" = "Version";
"version.labels.intro" = "Passepartout und TunnelKit sind geschrieben und gewartet von by Davide De Rosa (keeshux).\n\nQuellcode für Passepartout und TunnelKit ist öffentlich auf GitHub unter GPLv3 verfügbar, du findest die Links auf der Homepage.\n\nPassepartout ist ein inoffizieller client und auf keine Art und Weise mit OpenVPN Inc. verbunden.";
"credits.title" = "Credits";
"credits.sections.licenses.header" = "Lizenzen";
"credits.sections.notices.header" = "Notizen";
"credits.sections.translations.header" = "Übersetzungen";
"label.license.error" = "Konnte vollständigen Lizenz-Inhalt nicht herunterladen.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Αφαιρεί το κινητό δίκτυο από τα αξιόπιστα δίκτυα";
"1ZRTCZ" = "Απενεργοποίηση VPN";
"66bZBE" = "Με ${providerId} πάροχο";
"7eoAss" = "Αφαίρεση συνδεδεμένου Wi-Fi από τα έμπιστα δίκτυα";
"9GpJt5" = "Προσθέτει το κινητό δίκτυο στα αξιόπιστα δίκτυα";
"BKxs8X" = "Προσθήκη τρέχων Wi-Fi στα έμπιστα δίκτυα";
"H4taev" = "Αξιόπιστο Δίκτυο κινητής τηλεφωνίας";
"KjkCfU" = "Συνδέετε σε μια συγκεκριμένη τοποθεσία ενός προφίλ παρόχου";
"LA99yM" = "Σύνδεση VPN";
"U6o81V" = "Σύνδεση στο ${profileId}";
"WnTPFg" = "Σύνδεση σε ${poolName}";
"eQ1yzr" = "Απενεργοποίηση υπηρεσίας VPN";
"eXXb2z" = "Συνδέεται σε ένα προφίλ διακομιστή";
"lQ6ziK" = "Ενεργοποίηση VPN";
"m2E7SI" = "Εμπιστευθείτε το τρέχον Wi-Fi";
"qo3Szz" = "Συνδεθείτε με τη θέση του παρόχου";
"rd1T8p" = "Μην εμπιστευθείτε το τρέχον Wi-Fi";
"wB1iYX" = "Μη αξιόπιστο κινητό δίκτυο";
"xY97Vu" = "Ενεργοποιεί την υπηρεσία VPN με το προφίλ που χρησιμοποιείται αυτήν τη στιγμή";
"NCoK9B" = "Με προφίλ σε χρήση";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 6/13/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Ακύρωση";
"global.next" = "Επόμενο";
"global.close" = "Κλείσιμο";
"global.host.title_input.message" = "Αποδεκτοί χαρακτήρες είναι οι αλφαριθμητικοί συν τη παύλα \"-\", κάτω παύλα \"_\" και τελεία \".\".";
"global.host.title_input.placeholder" = "Το προφίλ μου";
"global.email_not_configured" = "Δεν έχει ρυθμιστεί λογαριασμός ηλεκτρονικού ταχυδρομείου.";
"global.cells.enabled" = "Ενεργοποιήθηκε";
"global.cells.disabled" = "Απενεργοποιήθηκε";
"global.cells.none" = "Κανένα";
"global.cells.automatic" = "Αυτόματο";
"global.cells.manual" = "Χειροκίνητο";
"reddit.title" = "Reddit";
"reddit.message" = "Γνωρίζατε ότι το Passepartout έχει subreddit? Εγγραφείτε για ενημερώσεις ή για να συζητήσετε προβλήματα της εφαρμογές, νέες δυνατότητες και άλλα.\n\nΕίναι επίσης ένας ωραίος τρόπος να δείξετε ότι ενδιαφέρεστε για τη προσπάθεια αυτή.";
"reddit.buttons.subscribe" = "Εγγραφή τώρα!";
"reddit.buttons.remind" = "Υπενθύμιση Αργότερα";
"reddit.buttons.never" = "Μη με ρωτήσεις ξανά";
"organizer.sections.providers.header" = "Πάροχοι";
"organizer.sections.providers.footer" = "Εδώ θα βρείτε ορισμένους παρόχους με προκαθορισμένες ρυθμίσεις προφίλ.";
"organizer.sections.hosts.header" = "Φιλοξενητές";
"organizer.sections.hosts.footer" = "Εισάγετε φιλοξενητές από ένα raw .ovpn αρχείο.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Get help from Siri to speed up your most common interactions with the app.";
"organizer.sections.support.header" = "Υποστήριξη";
"organizer.sections.feedback.header" = "Ανατροφοδότηση";
"organizer.cells.profile.value.current" = "Σε χρήση";
"organizer.cells.add_provider.caption" = "Προσθήκη νέου παρόχου";
"organizer.cells.add_host.caption" = "Προσθήκη νέου διακομιστή";
"organizer.cells.siri_shortcuts.caption" = "Διαχείριση Συντομεύσεων";
"organizer.cells.join_community.caption" = "Συμμετοχή στην κοινότητα";
"organizer.cells.write_review.caption" = "Γράψτε μια κριτική";
"organizer.cells.donate.caption" = "Κάντε μια δωρεά";
"organizer.cells.patreon.caption" = "Υποστηρίξτε με στο Patreon";
"organizer.cells.translate.caption" = "Βοηθήστε στη μετάφραση";
"organizer.cells.about.caption" = "Σχετικά με %@";
"organizer.cells.uninstall.caption" = "Αφαίρεση ρύθμισης VPN";
"organizer.alerts.exhausted_providers.message" = "Έχετε δημιουργήσει προφίλ για οποιονδήποτε διαθέσιμο πάροχο.";
"organizer.alerts.add_host.message" = "Εισάγετε μια διεύθυνση από ένα αρχείο .ovpn στο Safari, το Mail ή άλλη εφαρμογή για να ρυθμίσετε ένα προφίλ διακομιστή.\n\nΜπορείτε επίσης να εισάγετε ένα .ovpn αρχείο από το iTunes File Sharing.";
"organizer.alerts.cannot_donate.message" = "Δεν έχει ρυθμιστεί καμία μέθοδος πληρωμής σε αυτήν τη συσκευή.";
"organizer.alerts.delete_vpn_profile.message" = "Θέλετε πραγματικά να διαγράψετε τη διαμόρφωση VPN από τις ρυθμίσεις της συσκευής σας; Αυτό μπορεί να διορθώσει κάποιες καταστραμμένες καταστάσεις VPN και δεν θα επηρεάσει τα προφίλ του παροχέα και του διακομιστή σας.";
"wizards.host.cells.title_input.caption" = "Τίτλος";
"wizards.host.sections.existing.header" = "Υπάρχον Προφίλ";
"wizards.host.alerts.existing.message" = "Ένα προφίλ διακομιστή με τον ίδιο τίτλο υπάρχει ήδη. Αντικατέστησέ το;";
"parsed_file.alerts.malformed.message" = "Το αρχείο ρυθμίσεων περιέχει μια ακατάλληλη επιλογή (%@).";
"parsed_file.alerts.missing.message" = "Το αρχείο διαμόρφωσης δεν διαθέτει την απαιτούμενη επιλογή (%@).";
"parsed_file.alerts.unsupported.message" = "Το αρχείο διαμόρφωσης περιέχει μια επιλογή που δεν υποστηρίζεται (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "Το αρχείο ρυθμίσεων είναι σωστό, αλλά περιέχει μια δυνητικά μη υποστηριζόμενη επιλογή (%@).\n\nΗ δυνατότητα σύνδεσης μπορεί να διακοπεί ανάλογα με τις ρυθμίσεις του διακομιστή.";
"parsed_file.alerts.encryption_passphrase.message" = "Εισαγάγετε το κωδικό κρυπτογράφησης.";
"parsed_file.alerts.decryption.message" = "Η διαμόρφωση περιέχει κρυπτογραφημένο ιδιωτικό κλειδί και δεν ήταν δυνατό να αποκρυπτογραφηθεί. Δείτε πάλι το κωδικό που καταχωρίσατε.";
"parsed_file.alerts.parsing.message" = "Δεν είναι δυνατή η ανάλυση του παρεχόμενου αρχείου ρύθμισης παραμέτρων (%@).";
"parsed_file.alerts.buttons.report" = "Αναφέρετε ένα πρόβλημα";
"imported_hosts.title" = "Εισαγόμενοι διακομιστές";
"service.welcome.message" = "Καλώς Ήλθατε στο Passepartout!\n\nΧρησιμοποιήστε τον διοργανωτή για να προσθέσετε ένα νέο προφίλ.";
"service.sections.general.header" = "Γενικά";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "Η σύνδεση θα πραγματοποιηθεί όποτε είναι απαραίτητο.";
"service.sections.status.header" = "Σύνδεση";
"service.sections.configuration.header" = "Ρύθμιση";
"service.sections.provider_infrastructure.footer" = "Τελευταία ενημέρωση στις %@.";
"service.sections.vpn_survives_sleep.footer" = "Απενεργοποιήστε για να βελτιώσετε τη χρήση της μπαταρίας, εις βάρος των περιστασιακών επιβραδύνσεων που οφείλονται σε επανασύνδεση αφύπνισης.";
"service.sections.vpn_resolves_hostname.footer" = "Προτιμάται στα περισσότερα δίκτυα και απαιτείται σε ορισμένα δίκτυα IPv6. Απενεργοποιήστε το εκεί που μπλοκάρεται το DNS ή για να επιταχύνετε τη επικοινωνία όταν το DNS είναι αργό για να ανταποκριθεί.";
//"service.sections.vpn_prefers_udp.footer" = "Το UDP είναι πιο γρήγορο από το TCP, αλλά ενδέχεται να μην λειτουργεί σε ορισμένα δίκτυα. Απενεργοποιήστε σε δίκτυα όπου ενδέχεται να αποκλειστεί το UDP.";
"service.sections.trusted.header" = "Αξιόπιστα δίκτυα";
"service.sections.trusted.footer" = "Κατά την είσοδο σε ένα αξιόπιστο δίκτυο, το VPN απενεργοποιείται κανονικά και διατηρείται αποσυνδεδεμένο. Απενεργοποιήστε αυτήν την επιλογή για να μην έχετε μια τέτοια συμπεριφορά.";
"service.sections.diagnostics.header" = "Διαγνωστικά";
"service.sections.diagnostics.footer" = "Η κατάσταση κάλυψης θα είναι αποτελεσματική μετά την επανασύνδεση. Τα δεδομένα δικτύου είναι του διακομιστή, διευθύνσεις IP, δρομολόγηση και SSID. Τα διαπιστευτήρια και τα ιδιωτικά κλειδιά δεν καταγράφονται ανεξάρτητα.";
//"service.sections.destruction.footer" = "Διαγραφή ρυθμίσεων από τις ρυθμίσεις της συσκευής.";
"service.cells.use_profile.caption" = "Χρησιμοποιήστε αυτό το προφίλ";
"service.cells.vpn_service.caption" = "Ενεργοποιήθηκε";
"service.cells.connection_status.caption" = "Κατάσταση";
"service.cells.reconnect.caption" = "Επανασύνδεση";
"service.cells.account.caption" = "Λογαριασμός";
"service.cells.account.none" = "Δεν έχει διαμορφωθεί";
"service.cells.endpoint.caption" = "Τελικό σημείο";
"service.cells.provider.pool.caption" = "Τοποθεσία";
"service.cells.provider.preset.caption" = "Προεπιλογή";
"service.cells.provider.refresh.caption" = "Ανανέωση της υποδομής";
"service.cells.host.parameters.caption" = "Παράμετροι";
"service.cells.network_settings.caption" = "Ρυθμίσεις Δικτύου";
"service.cells.vpn_survives_sleep.caption" = "Κρατήστε ζωντανό στον ύπνο";
"service.cells.vpn_resolves_hostname.caption" = "Επίλυση του ονόματος σέρβερ διακομιστή";
//"service.cells.vpn_prefers_udp.caption" = "Prefer UDP socket";
"service.cells.trusted_mobile.caption" = "Δίκτυο Κινητής";
"service.cells.trusted_add_wifi.caption" = "Προσθέστε το τρέχον Wi-Fi";
"service.cells.trusted_policy.caption" = "Τα αξιόπιστα δίκτυα απενεργοποιούν το VPN";
"service.cells.test_connectivity.caption" = "Δοκιμή συνδεσιμότητας";
"service.cells.data_count.caption" = "Ανταλλαγή δεδομένων";
"service.cells.data_count.none" = "Μη διαθέσιμο";
"service.cells.debug_log.caption" = "Μητρώο εντοπισμού σφαλμάτων";
"service.cells.masks_private_data.caption" = "Μάσκα δεδομένα δικτύου";
"service.cells.report_issue.caption" = "Αναφορά ζητήματος συνδεσιμότητας";
"service.alerts.rename.title" = "Μετονομασία προφίλ";
"service.alerts.credentials_needed.message" = "Πρέπει πρώτα να εισαγάγετε διαπιστευτήρια λογαριασμού.";
"service.alerts.reconnect_vpn.message" = "Θέλετε να συνδεθείτε ξανά με το VPN;";
"service.alerts.trusted.no_network.message" = "Δεν είστε συνδεδεμένοι σε κανένα δίκτυο Wi-Fi.";
"service.alerts.trusted.will_disconnect_trusted.message" = "Με εμπιστοσύνη σε αυτό το δίκτυο, το VPN μπορεί να αποσυνδεθεί. Να συνεχίσω;";
"service.alerts.trusted.will_disconnect_policy.message" = "Αλλάζοντας την πολιτική εμπιστοσύνης, το VPN μπορεί να αποσυνδεθεί. Να συνεχίσω;";
"service.alerts.test_connectivity.title" = "Συνδεσιμότητα";
"service.alerts.test_connectivity.messages.success" = "Η συσκευή σας είναι συνδεδεμένη στο Διαδίκτυο!";
"service.alerts.test_connectivity.messages.failure" = "Η συσκευή σας δεν διαθέτει σύνδεση στο Internet, παρακαλούμε να ελέγξετε τις παραμέτρους του προφίλ σας.";
"service.alerts.masks_private_data.messages.must_reconnect" = "Για να επαναφέρετε με ασφάλεια την τρέχουσα καταγραφή εντοπισμού σφαλμάτων και να εφαρμόσετε τη νέα προτίμηση κάλυψης, πρέπει να συνδεθείτε ξανά με το VPN.";
"service.alerts.buttons.reconnect" = "Επανασύνδεση";
"service.alerts.download.title" = "Απαιτείται λήψη";
"service.alerts.download.message" = "%@ απαιτεί τη λήψη πρόσθετων αρχείων ρυθμίσεων.\n\nΕπιβεβαιώστε για να ξεκινήσετε τη λήψη.";
"service.alerts.download.failed" = "Αποτυχία λήψης αρχείων ρυθμίσεων. %@";
"service.alerts.download.hud.extracting" = "Εξάγοντας τα αρχεία, παρακαλώ να είστε υπομονετικοί...";
"account.sections.credentials.header" = "Διαπιστευτήρια";
"account.sections.guidance.footer.infrastructure.mullvad" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως αριθμητικό.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως το ηλεκτρονικό σας ταχυδρομείο.";
"account.sections.guidance.footer.infrastructure.pia" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως αριθμητικό με πρόθεμα \"p\".";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Βρείτε τα διαπιστευτήριά σας %@ στην ενότητα \"Λογαριασμός> OpenVPN / IKEv2 Username \" της ιστοσελίδας.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως το ηλεκτρονικό σας ταχυδρομείο.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως το ηλεκτρονικό σας ταχυδρομείο.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Βρείτε τα διαπιστευτήριά σας %@ στο OpenVPN Config Generator στον ιστότοπο.";
"account.sections.registration.footer" = "Πηγαίνετε να αποκτήσετε λογαριασμό στον ιστότοπο %@.";
"account.cells.username.caption" = "Όνομα χρήστη";
"account.cells.username.placeholder" = "χρήστης";
"account.cells.password.caption" = "Κωδικός";
"account.cells.password.placeholder" = "κωδικός";
//"account.cells.password_confirm.caption" = "Επιβεβαίωση";
//"account.cells.password_confirm.mismatch" = "Οι κωδικοί πρόσβασης δεν ταιριάζουν!";
"account.cells.open_guide.caption" = "Δείτε τα διαπιστευτήρια σας";
"account.cells.signup.caption" = "Εγγραφείτε με %@";
"endpoint.sections.location_addresses.header" = "Διεθύνσεις";
"endpoint.sections.location_protocols.header" = "Πρωτόκολλα";
"endpoint.cells.any_address.caption" = "Αυτόματο";
"endpoint.cells.any_protocol.caption" = "Αυτόματο";
"provider.preset.cells.tech_details.caption" = "Τεχνικές Λεπτομέρειες";
//"provider.preset.sections.main.footer" = "Αγγίξτε το κουμπί πληροφοριών για να αποκαλύψετε τεχνικές λεπτομέρειες.";
"configuration.sections.communication.header" = "Επικοινωνία";
"configuration.sections.reset.footer" = "Αν καταλήξατε σε κατεστραμένη συνδεσιμότητα μετά την αλλαγή των παραμέτρων επικοινωνίας, πατήστε για να επανέλθετε στην αρχική διαμόρφωση.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Συμπίεση";
"configuration.sections.network.header" = "Δίκτυο";
"configuration.sections.other.header" = "Άλλο";
"configuration.cells.cipher.caption" = "Cipher";
"configuration.cells.digest.caption" = "Αυθεντικοποίηση";
"configuration.cells.digest.value.embedded" = "Ενσωματωμένο";
"configuration.cells.reset_original.caption" = "Επαναφορά ρυθμίσεων";
"configuration.cells.client.caption" = "Πιστοποιητικό πελάτη";
"configuration.cells.client.value.enabled" = "Επαληθεύτηκε";
"configuration.cells.client.value.disabled" = "Δεν επαληθεύτηκε";
"configuration.cells.tls_wrapping.caption" = "Wrapping";
"configuration.cells.tls_wrapping.value.auth" = "Αυθεντικοποίηση";
"configuration.cells.tls_wrapping.value.crypt" = "Κρυπτογράφηση";
"configuration.cells.eku.caption" = "Εκτεταμένη επαλήθευση";
"configuration.cells.default_gateway.caption" = "Προεπιλεγμένη πύλη";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Domain";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Framing";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--συμπίεση";
"configuration.cells.compression_algorithm.caption" = "Αλγόρυθμος";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Δεν υποστηρίζεται";
"configuration.cells.keep_alive.caption" = "Διατηρήστε ζωντανή";
"configuration.cells.keep_alive.value.seconds" = "%d δευτερόλεπτα";
"configuration.cells.renegotiation_seconds.caption" = "Επαναδιαπραγμάτευση";
"configuration.cells.renegotiation_seconds.value.after" = "μετά από %@";
"configuration.cells.random_endpoint.caption" = "Τυχαίο τελικό σημείο";
"network_settings.cells.choice.client" = "Ανάγνωση .ovpn";
"network_settings.cells.choice.server" = "Λήψη από το διακομιστή";
"network_settings.cells.address.caption" = "Διεύθυνση";
"network_settings.cells.port.caption" = "Θύρα";
"network_settings.cells.add_dns_server.caption" = "Προσθήκη Διεύθυνσης";
"network_settings.cells.proxy_bypass.caption" = "Παράκαμψη Τομέα";
"network_settings.cells.add_proxy_bypass.caption" = "Προσθήκη τομέα παράκαμψης";
"debug_log.buttons.previous" = "Προηγούμενο";
"debug_log.buttons.next" = "Επόμενο";
"debug_log.alerts.empty_log.message" = "Το αρχείο εντοπισμού σφαλμάτων είναι κενό.";
"vpn.connecting" = "Προσπάθεια Σύνδεσης";
"vpn.active" = "Ενεργό";
"vpn.disconnecting" = "Αποσυνδέετε";
"vpn.inactive" = "Μη ενεργό";
"vpn.disabled" = "Απενεργοποιημένο";
"vpn.errors.timeout" = "Τέλος χρονικού Ορίου";
"vpn.errors.dns" = "Το DNS απέτυχε";
"vpn.errors.auth" = "Το Auth απέτυχε";
"vpn.errors.tls" = "Το TLS απέτυχε";
"vpn.errors.encryption" = "Η Κρυπτογράφηση απέτυχε";
"vpn.errors.compression" = "Η συμπίεση δεν υποστηρίζεται";
"vpn.errors.network" = "Το δίκτυο άλλαξε";
"vpn.errors.routing" = "Λείπει η δρομολόγηση";
"vpn.errors.gateway" = "Δεν υπάρχει πύλη";
"issue_reporter.title" = "Αναφορά Προβλήματος";
"issue_reporter.message" = "The debug log of your latest connections is crucial to resolve your connectivity issues and is completely anonymous.\n\nThe .ovpn configuration file, if any, is attached stripped of any sensitive data.\n\nPlease double check the e-mail attachments if unsure.";
"issue_reporter.buttons.accept" = "Καταλαβαίνω";
"translations.title" = "Μεταφράσεις";
"shortcuts.add.title" = "Προσθήκη Συντόμευσης";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Δίκτυο Κινητής";
"shortcuts.add.cells.connect.caption" = "Σύνδεση σε";
"shortcuts.add.cells.enable_vpn.caption" = "Ενεργοποίηση VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Απενεργοποίηση VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Εμπιστέψου το τρέχον Wi-Fi";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Μην εμπιστευθείτε το τρέχον Wi-Fi";
"shortcuts.add.cells.trust_cellular.caption" = "Εμπιστοσύνη δικτύου κινητής τηλεφωνίας";
"shortcuts.add.cells.untrust_cellular.caption" = "Μην εμπιστευθείτε το δίκτυο κινητής τηλεφωνίας";
"shortcuts.add.alerts.no_profiles.message" = "Δεν υπάρχει προφίλ για σύνδεση.";
"shortcuts.edit.title" = "Διαχείριση συντομεύσεων";
"shortcuts.edit.sections.all.header" = "Υπάρχουσες συντομεύσεις";
"shortcuts.edit.cells.add_shortcut.caption" = "Προσθήκη Συντόμευσης";
"about.title" = "Περι";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Διαμοιράστε";
"about.cells.credits.caption" = "Συντελεστές";
"about.cells.website.caption" = "Αρχική Σελίδα";
"about.cells.faq.caption" = "Συχνές Ερωτήσεις";
"about.cells.disclaimer.caption" = "Άρνηση Ευθύνης";
"about.cells.privacy_policy.caption" = "Πολιτική Απορρήτου";
"about.cells.share_twitter.caption" = "Tweet γι 'αυτό!";
"about.cells.share_generic.caption" = "Πρόσκληση Φίλου";
"donation.title" = "Δωρεά";
"donation.sections.one_time.header" = "Μια Φορά";
"donation.sections.one_time.footer" = "Αν είστε χαρούμενη με τη δουλειά μου, εδώ είναι λίγα ποσά που μπορείτε να δώσετε αμέσως.\n\nΘα χρεωθείτε μόνο μία φορά και μπορείτε να δώσετε πολλές φορές.";
"donation.cells.loading.caption" = "Φόρτωση δωρεών";
"donation.cells.purchasing.caption" = "Εκτέλεση δωρεάς";
"donation.alerts.purchase.success.title" = "Ευχαριστώ";
"donation.alerts.purchase.success.message" = "Αυτό σημαίνει πολλά για μένα και πραγματικά ελπίζω να συνεχίσετε να χρησιμοποιείτε και να προωθείτε αυτήν την εφαρμογή.";
"donation.alerts.purchase.failure.message" = "Δεν είναι δυνατή η εκτέλεση της δωρεάς. %@";
"share.message" = "Το Passepartout είναι φιλικό προς το χρήστη, ανοιχτού κώδικα OpenVPN πρόγραμμα για iOS και macOS";
"version.title" = "Έκδοση";
"version.labels.intro" = "Το Passepartout και το TunnelKit γράφονται και συντηρούνται από τον Davide De Rosa (keeshux).\n\nΟ πηγαίος κώδικας για το Passepartout και το TunnelKit είναι δημόσια διαθέσιμε στο GitHub υπό το GPLv3, μπορείτε να βρείτε συνδέσμους στην αρχική σελίδα.\n\nΤο Passepartout είναι ένας μη επίσημος πελάτης και δεν είναι συνδεδεμένος με το OpenVPN Inc.";
"credits.title" = "Συντελεστές";
"credits.sections.licenses.header" = "Άδειες";
"credits.sections.notices.header" = "Σημειώσεις";
"credits.sections.translations.header" = "Μεταφράσεις";
"label.license.error" = "Δεν είναι δυνατή η λήψη πλήρους περιεχομένου άδειας χρήσης.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Removes cellular from trusted networks";
"1ZRTCZ" = "Disable VPN";
"66bZBE" = "With ${providerId} provider";
"7eoAss" = "Removes current Wi-Fi from trusted networks";
"9GpJt5" = "Adds cellular to trusted networks";
"BKxs8X" = "Adds current Wi-Fi to trusted networks";
"H4taev" = "Trust cellular network";
"KjkCfU" = "Connects to a specific location of a provider profile";
"LA99yM" = "Connect to VPN";
"U6o81V" = "Connect to ${profileId}";
"WnTPFg" = "Connect to ${poolName}";
"eQ1yzr" = "Disables the VPN service";
"eXXb2z" = "Connects to a host profile";
"lQ6ziK" = "Enable VPN";
"m2E7SI" = "Trust current Wi-Fi";
"qo3Szz" = "Connect to provider location";
"rd1T8p" = "Untrust current Wi-Fi";
"wB1iYX" = "Untrust cellular network";
"xY97Vu" = "Enables the VPN service with the profile currently in use";
"NCoK9B" = "With profile in use";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 6/13/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Cancel";
"global.next" = "Next";
"global.close" = "Close";
"global.host.title_input.message" = "Acceptable characters are alphanumerics plus dash \"-\", underscore \"_\" and dot \".\".";
"global.host.title_input.placeholder" = "My profile";
"global.email_not_configured" = "No e-mail account is configured.";
"global.cells.enabled" = "Enabled";
"global.cells.disabled" = "Disabled";
"global.cells.none" = "None";
"global.cells.automatic" = "Automatic";
"global.cells.manual" = "Manual";
"reddit.title" = "Reddit";
"reddit.message" = "Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project.";
"reddit.buttons.subscribe" = "Subscribe now!";
"reddit.buttons.remind" = "Remind me later";
"reddit.buttons.never" = "Don't ask again";
"organizer.sections.providers.header" = "Providers";
"organizer.sections.providers.footer" = "Here you find a few providers with preset configuration profiles.";
"organizer.sections.hosts.header" = "Hosts";
"organizer.sections.hosts.footer" = "Import hosts from raw .ovpn configuration files.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Get help from Siri to speed up your most common interactions with the app.";
"organizer.sections.support.header" = "Support";
"organizer.sections.feedback.header" = "Feedback";
"organizer.cells.profile.value.current" = "In use";
"organizer.cells.add_provider.caption" = "Add new provider";
"organizer.cells.add_host.caption" = "Add new host";
"organizer.cells.siri_shortcuts.caption" = "Manage shortcuts";
"organizer.cells.join_community.caption" = "Join community";
"organizer.cells.write_review.caption" = "Write a review";
"organizer.cells.donate.caption" = "Make a donation";
"organizer.cells.patreon.caption" = "Support me on Patreon";
"organizer.cells.translate.caption" = "Offer to translate";
"organizer.cells.about.caption" = "About %@";
"organizer.cells.uninstall.caption" = "Remove VPN configuration";
"organizer.alerts.exhausted_providers.message" = "You have created profiles for any available provider.";
"organizer.alerts.add_host.message" = "Open an URL to an .ovpn configuration file from Safari, Mail or another app to set up a host profile.\n\nYou can also import an .ovpn with iTunes File Sharing.";
"organizer.alerts.cannot_donate.message" = "There is no payment method configured on this device.";
"organizer.alerts.delete_vpn_profile.message" = "Do you really want to erase the VPN configuration from your device settings? This may fix some broken VPN states and will not affect your provider and host profiles.";
"wizards.host.cells.title_input.caption" = "Title";
"wizards.host.sections.existing.header" = "Existing profiles";
"wizards.host.alerts.existing.message" = "A host profile with the same title already exists. Replace it?";
"parsed_file.alerts.malformed.message" = "The configuration file contains a malformed option (%@).";
"parsed_file.alerts.missing.message" = "The configuration file lacks a required option (%@).";
"parsed_file.alerts.unsupported.message" = "The configuration file contains an unsupported option (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "The configuration file is correct but contains a potentially unsupported option (%@).\n\nConnectivity may break depending on server settings.";
"parsed_file.alerts.encryption_passphrase.message" = "Please enter the encryption passphrase.";
"parsed_file.alerts.decryption.message" = "The configuration contains an encrypted private key and it could not be decrypted. Double check your entered passphrase.";
"parsed_file.alerts.parsing.message" = "Unable to parse the provided configuration file (%@).";
"parsed_file.alerts.buttons.report" = "Report an issue";
"imported_hosts.title" = "Imported hosts";
"service.welcome.message" = "Welcome to Passepartout!\n\nUse the organizer to add a new profile.";
"service.sections.general.header" = "General";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "The connection will be established whenever necessary.";
"service.sections.status.header" = "Connection";
"service.sections.configuration.header" = "Configuration";
"service.sections.provider_infrastructure.footer" = "Last updated on %@.";
"service.sections.vpn_survives_sleep.footer" = "Disable to improve battery usage, at the expense of occasional slowdowns due to wake-up reconnections.";
"service.sections.vpn_resolves_hostname.footer" = "Preferred in most networks and required in some IPv6 networks. Disable where DNS is blocked, or to speed up negotiation when DNS is slow to respond.";
//"service.sections.vpn_prefers_udp.footer" = "UDP is faster than TCP, but may not work in some networks. Disable in networks where UDP might be blocked.";
"service.sections.trusted.header" = "Trusted networks";
"service.sections.trusted.footer" = "When entering a trusted network, the VPN is normally shut down and kept disconnected. Disable this option to not enforce such behavior.";
"service.sections.diagnostics.header" = "Diagnostics";
"service.sections.diagnostics.footer" = "Masking status will be effective after reconnecting. Network data are hostnames, IP addresses, routing, SSID. Credentials and private keys are not logged regardless.";
//"service.sections.destruction.footer" = "Delete configuration from device settings.";
"service.cells.use_profile.caption" = "Use this profile";
"service.cells.vpn_service.caption" = "Enabled";
"service.cells.connection_status.caption" = "Status";
"service.cells.reconnect.caption" = "Reconnect";
"service.cells.account.caption" = "Account";
"service.cells.account.none" = "None configured";
"service.cells.endpoint.caption" = "Endpoint";
"service.cells.provider.pool.caption" = "Location";
"service.cells.provider.preset.caption" = "Preset";
"service.cells.provider.refresh.caption" = "Refresh infrastructure";
"service.cells.host.parameters.caption" = "Parameters";
"service.cells.network_settings.caption" = "Network settings";
"service.cells.vpn_survives_sleep.caption" = "Keep alive on sleep";
"service.cells.vpn_resolves_hostname.caption" = "Resolve server hostname";
//"service.cells.vpn_prefers_udp.caption" = "Prefer UDP socket";
"service.cells.trusted_mobile.caption" = "Cellular network";
"service.cells.trusted_add_wifi.caption" = "Add current Wi-Fi";
"service.cells.trusted_policy.caption" = "Trust disables VPN";
"service.cells.test_connectivity.caption" = "Test connectivity";
"service.cells.data_count.caption" = "Exchanged data";
"service.cells.data_count.none" = "Unavailable";
"service.cells.debug_log.caption" = "Debug log";
"service.cells.masks_private_data.caption" = "Mask network data";
"service.cells.report_issue.caption" = "Report connectivity issue";
"service.alerts.rename.title" = "Rename profile";
"service.alerts.credentials_needed.message" = "You need to enter account credentials first.";
"service.alerts.reconnect_vpn.message" = "Do you want to reconnect to the VPN?";
"service.alerts.trusted.no_network.message" = "You are not connected to any Wi-Fi network.";
"service.alerts.trusted.will_disconnect_trusted.message" = "By trusting this network, the VPN may be disconnected. Continue?";
"service.alerts.trusted.will_disconnect_policy.message" = "By changing the trust policy, the VPN may be disconnected. Continue?";
"service.alerts.test_connectivity.title" = "Connectivity";
"service.alerts.test_connectivity.messages.success" = "Your device is connected to the Internet!";
"service.alerts.test_connectivity.messages.failure" = "Your device has no Internet connectivity, please review your profile parameters.";
"service.alerts.masks_private_data.messages.must_reconnect" = "In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.";
"service.alerts.buttons.reconnect" = "Reconnect";
"service.alerts.download.title" = "Download required";
"service.alerts.download.message" = "%@ requires the download of additional configuration files.\n\nConfirm to start the download.";
"service.alerts.download.failed" = "Failed to download configuration files. %@";
"service.alerts.download.hud.extracting" = "Extracting files, please be patient...";
"account.sections.credentials.header" = "Credentials";
"account.sections.guidance.footer.infrastructure.mullvad" = "Use your %@ website credentials. Your username is usually numeric.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Use your %@ website credentials. Your username is usually your e-mail.";
"account.sections.guidance.footer.infrastructure.pia" = "Use your %@ website credentials. Your username is usually numeric with a \"p\" prefix.";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Find your %@ credentials in the \"Account > OpenVPN / IKEv2 Username\" section of the website.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Use your %@ website credentials. Your username is usually your e-mail.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Use your %@ website credentials. Your username is usually your e-mail.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Find your %@ credentials in the OpenVPN Config Generator on the website.";
"account.sections.registration.footer" = "Go get an account on the %@ website.";
"account.cells.username.caption" = "Username";
"account.cells.username.placeholder" = "username";
"account.cells.password.caption" = "Password";
"account.cells.password.placeholder" = "secret";
//"account.cells.password_confirm.caption" = "Confirm";
//"account.cells.password_confirm.mismatch" = "Passwords don't match!";
"account.cells.open_guide.caption" = "See your credentials";
"account.cells.signup.caption" = "Register with %@";
"endpoint.sections.location_addresses.header" = "Addresses";
"endpoint.sections.location_protocols.header" = "Protocols";
"endpoint.cells.any_address.caption" = "Automatic";
"endpoint.cells.any_protocol.caption" = "Automatic";
"provider.preset.cells.tech_details.caption" = "Technical details";
//"provider.preset.sections.main.footer" = "Tap info button to disclose technical details.";
"configuration.sections.communication.header" = "Communication";
"configuration.sections.reset.footer" = "If you ended up with broken connectivity after changing the communication parameters, tap to revert to the original configuration.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Compression";
"configuration.sections.network.header" = "Network";
"configuration.sections.other.header" = "Other";
"configuration.cells.cipher.caption" = "Cipher";
"configuration.cells.digest.caption" = "Authentication";
"configuration.cells.digest.value.embedded" = "Embedded";
"configuration.cells.reset_original.caption" = "Reset configuration";
"configuration.cells.client.caption" = "Client certificate";
"configuration.cells.client.value.enabled" = "Verified";
"configuration.cells.client.value.disabled" = "Not verified";
"configuration.cells.tls_wrapping.caption" = "Wrapping";
"configuration.cells.tls_wrapping.value.auth" = "Authentication";
"configuration.cells.tls_wrapping.value.crypt" = "Encryption";
"configuration.cells.eku.caption" = "Extended verification";
"configuration.cells.default_gateway.caption" = "Default gateway";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Domain";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Framing";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Algorithm";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Unsupported";
"configuration.cells.keep_alive.caption" = "Keep-alive";
"configuration.cells.keep_alive.value.seconds" = "%d seconds";
"configuration.cells.renegotiation_seconds.caption" = "Renegotiation";
"configuration.cells.renegotiation_seconds.value.after" = "after %@";
"configuration.cells.random_endpoint.caption" = "Randomize endpoint";
"network_settings.cells.choice.client" = "Read .ovpn";
"network_settings.cells.choice.server" = "Pull from server";
"network_settings.cells.address.caption" = "Address";
"network_settings.cells.port.caption" = "Port";
"network_settings.cells.add_dns_server.caption" = "Add address";
"network_settings.cells.proxy_bypass.caption" = "Bypass domain";
"network_settings.cells.add_proxy_bypass.caption" = "Add bypass domain";
"debug_log.buttons.previous" = "Previous";
"debug_log.buttons.next" = "Next";
"debug_log.alerts.empty_log.message" = "The debug log is empty.";
"vpn.connecting" = "Connecting";
"vpn.active" = "Active";
"vpn.disconnecting" = "Disconnecting";
"vpn.inactive" = "Inactive";
"vpn.disabled" = "Disabled";
"vpn.errors.timeout" = "Timeout";
"vpn.errors.dns" = "DNS failed";
"vpn.errors.auth" = "Auth failed";
"vpn.errors.tls" = "TLS failed";
"vpn.errors.encryption" = "Encryption failed";
"vpn.errors.compression" = "Compression unsupported";
"vpn.errors.network" = "Network changed";
"vpn.errors.routing" = "Missing routing";
"vpn.errors.gateway" = "No gateway";
"issue_reporter.title" = "Report issue";
"issue_reporter.message" = "The debug log of your latest connections is crucial to resolve your connectivity issues and is completely anonymous.\n\nThe .ovpn configuration file, if any, is attached stripped of any sensitive data.\n\nPlease double check the e-mail attachments if unsure.";
"issue_reporter.buttons.accept" = "I understand";
"translations.title" = "Translations";
"shortcuts.add.title" = "Add shortcut";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Cellular";
"shortcuts.add.cells.connect.caption" = "Connect to";
"shortcuts.add.cells.enable_vpn.caption" = "Enable VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Disable VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Trust current Wi-Fi";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Untrust current Wi-Fi";
"shortcuts.add.cells.trust_cellular.caption" = "Trust cellular network";
"shortcuts.add.cells.untrust_cellular.caption" = "Untrust cellular network";
"shortcuts.add.alerts.no_profiles.message" = "There is no profile to connect to.";
"shortcuts.edit.title" = "Manage shortcuts";
"shortcuts.edit.sections.all.header" = "Existing shortcuts";
"shortcuts.edit.cells.add_shortcut.caption" = "Add shortcut";
"about.title" = "About";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Share";
"about.cells.credits.caption" = "Credits";
"about.cells.website.caption" = "Home page";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Disclaimer";
"about.cells.privacy_policy.caption" = "Privacy policy";
"about.cells.share_twitter.caption" = "Tweet about it!";
"about.cells.share_generic.caption" = "Invite a friend";
"donation.title" = "Donate";
"donation.sections.one_time.header" = "One time";
"donation.sections.one_time.footer" = "If you want to display gratitude for my free work, here are a couple amounts you can donate instantly.\n\nYou will only be charged once per donation, and you can donate multiple times.";
"donation.cells.loading.caption" = "Loading donations";
"donation.cells.purchasing.caption" = "Performing donation";
"donation.alerts.purchase.success.title" = "Thank you";
"donation.alerts.purchase.success.message" = "This means a lot to me and I really hope you keep using and promoting this app.";
"donation.alerts.purchase.failure.message" = "Unable to perform the donation. %@";
"share.message" = "Passepartout is an user-friendly, open source OpenVPN client for iOS and macOS";
"version.title" = "Version";
"version.labels.intro" = "Passepartout and TunnelKit are written and maintained by Davide De Rosa (keeshux).\n\nSource code for Passepartout and TunnelKit is publicly available on GitHub under the GPLv3, you can find links in the home page.\n\nPassepartout is a non-official client and is in no way affiliated with OpenVPN Inc.";
"credits.title" = "Credits";
"credits.sections.licenses.header" = "Licenses";
"credits.sections.notices.header" = "Notices";
"credits.sections.translations.header" = "Translations";
"label.license.error" = "Unable to download full license content.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Supprime le réseau cellulaire des réseaux de confiance";
"1ZRTCZ" = "Désactive VPN";
"66bZBE" = "Avec ${providerId} fournisseur";
"7eoAss" = "Supprime le présent réseaux Wi-Fi des réseaux de confiance ";
"9GpJt5" = "Ajoutes le réseau cellulaire aux réseaux de confiance";
"BKxs8X" = "Ajoutes le présent réseau Wi-Fi aux réseaux de confiance";
"H4taev" = "Faire confiance au réseau cellulaire";
"KjkCfU" = "Connecter à une localization spécifique d'un profile de fournisseur";
"LA99yM" = "Se connecter au VPN";
"U6o81V" = "Se connecter à ${profileId}";
"WnTPFg" = "Se connecter à ${poolName}";
"eQ1yzr" = "Désactives le service VPN";
"eXXb2z" = "Connectes à un profile hôte";
"lQ6ziK" = "Activer VPN";
"m2E7SI" = "Faire confiance au présent réseau Wi-Fi";
"qo3Szz" = "Se connecter à la localisation du fournisseur";
"rd1T8p" = "Ne plus faire confiance au présent réseau Wi-Fi";
"wB1iYX" = "Faire confiance au présent réseau cellulaire";
"xY97Vu" = "Activer le service VPN avec le profile présentement utilisé";
"NCoK9B" = "Avec le profile utilisé";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 6/13/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Annuler";
"global.next" = "Suivant";
"global.close" = "Fermer";
"global.host.title_input.message" = "Caractères acceptables sont alphanumériques et tiret \"-\", barre de soulignement \"_\" et point \".\".";
"global.host.title_input.placeholder" = "Mon profile";
"global.email_not_configured" = "Aucun compte courriel n'est configuré.";
"global.cells.enabled" = "Activer";
"global.cells.disabled" = "Désactiver";
"global.cells.none" = "Aucun";
"global.cells.automatic" = "Automatique";
"global.cells.manual" = "Manuel";
"reddit.title" = "Reddit";
"reddit.message" = "Saviez-vous que Passepartout a un subreddit? Souscrivez pour les mises à jour ou discuter des problèmes, caractéristiques, nouvelles plateformes ou quoi que ce soit.\n\nC'est aussi une très bonne façon de démontrer votre enthousiasme envers le projet.";
"reddit.buttons.subscribe" = "Souscrivez maintenant!";
"reddit.buttons.remind" = "Me rappeler plus tard";
"reddit.buttons.never" = "Ne pas me redemander";
"organizer.sections.providers.header" = "Fournisseurs";
"organizer.sections.providers.footer" = "Ici vous pouvez trousers certains fournisseurs avec des profiles déjà configurés.";
"organizer.sections.hosts.header" = "Hôtes";
"organizer.sections.hosts.footer" = "Importer des hôtes d'un fichier de configuration .ovpn.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Obtenez de l'aide de Siri pour accélérer vos intéractions les plus courantes avec l'app.";
"organizer.sections.support.header" = "Support";
"organizer.sections.feedback.header" = "Commentaires";
"organizer.cells.profile.value.current" = "En utilisation";
"organizer.cells.add_provider.caption" = "Ajouter un nouveau fournisseur";
"organizer.cells.add_host.caption" = "Ajouter un nouvel hôte";
"organizer.cells.siri_shortcuts.caption" = "Gérer les raccourcis";
"organizer.cells.join_community.caption" = "Joindre la communauté";
"organizer.cells.write_review.caption" = "Écrire un avis";
"organizer.cells.donate.caption" = "Faire un don";
"organizer.cells.patreon.caption" = "Me supporter sur Patreon";
"organizer.cells.translate.caption" = "Offrir de traduire";
"organizer.cells.about.caption" = "À propos %@";
"organizer.cells.uninstall.caption" = "Supprimer la configuration VPN";
"organizer.alerts.exhausted_providers.message" = "Vous avez créé un profile pour un fournisseur existant.";
"organizer.alerts.add_host.message" = "Ouvrir un URL vers un fichier de configuration .ovpn depuis Safari, Courriels ou un autre app pour installer un profile hôte.\n\nVous pouvez importer une configuration .ovpn avec le transfert de fichiers iTunes.";
"organizer.alerts.cannot_donate.message" = "Il n'y a aucune méthode de paiement configuré sur cet appareil.";
"organizer.alerts.delete_vpn_profile.message" = "Voulez-vous vraiment effacer la configuration VPN de vos paramètres? Ceci peux fixer certains VPN en arrêt et n'affectera pas vos profiles de fournisseurs et hôtes.";
"wizards.host.cells.title_input.caption" = "Titre";
"wizards.host.sections.existing.header" = "Profiles existants";
"wizards.host.alerts.existing.message" = "Un profile hôte avec ce même nom existe déjà. Le remplacer?";
"parsed_file.alerts.malformed.message" = "Le fichier de configuration contient une mauvaise option.(%@).";
"parsed_file.alerts.missing.message" = "Le fichier de configuration ne contient pas une option requise. (%@).";
"parsed_file.alerts.unsupported.message" = "Le fichier de configuration contient une option non supportée (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "Le fichier de configuration est adéquat, mais contient une option potentiellement non supportée. (%@).\n\nLa connection peut être perdue selon les paramètres du serveur.";
"parsed_file.alerts.encryption_passphrase.message" = "Veuillez entrer le mot de passe d'encryption.";
"parsed_file.alerts.decryption.message" = "Le fichier de configuration contient une clé privée encryptée et n'a pas été décryptée. Veuillez revérifier votre mot de passe.";
"parsed_file.alerts.parsing.message" = "Incapable d'analyser le fichier de configuration fournis. (%@).";
"parsed_file.alerts.buttons.report" = "Rapporter un problème";
"imported_hosts.title" = "Hôtes importés";
"service.welcome.message" = "Bienvenue à Passepartout!\n\nUtilisez l'organiseur pour ajouter un nouveau profile.";
"service.sections.general.header" = "Général";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "La connection sera établie lorsque nécessaire.";
"service.sections.status.header" = "Connection";
"service.sections.configuration.header" = "Configuration";
"service.sections.provider_infrastructure.footer" = "Mis à jour : %@.";
"service.sections.vpn_survives_sleep.footer" = "Désactiver pour augmenter l'autonomie de la batterie, au dépends de la rapidité au réveil pour la reconnection.";
"service.sections.vpn_resolves_hostname.footer" = "Préféré dans la plus part des réseaux et requis dans certains réseaux IPv6. Désactiver lorsque le DNS est bloqué ou pour augmenter la rapidité des négociations lorsque le DNS est lent à répondre.";
//"service.sections.vpn_prefers_udp.footer" = "UDP est plus rapide que le TCP, mais peut ne pas fonctionner sur certains réseaux. Désactiver dans les réseaux où UDP peut être bloqué.";
"service.sections.trusted.header" = "Réseaux de confiance";
"service.sections.trusted.footer" = "Lors d'une connection à un réseau de confiance, le VPN est normalement fermé. Désactivez cette option pour ne pas autoriser ce comportement.";
"service.sections.diagnostics.header" = "Diagnostiques";
"service.sections.diagnostics.footer" = "Camouflage du status sera effectif après la reconnection. Les données réseaux sont les noms d'hôtes, adresses IP, routage, SSID. Les identifiants et clés privés ne sont pas enregistrés.";
//"service.sections.destruction.footer" = "Supprimer la configuration des réglages de l'appareil.";
"service.cells.use_profile.caption" = "Utiliser ce profile";
"service.cells.vpn_service.caption" = "Activer";
"service.cells.connection_status.caption" = "Status";
"service.cells.reconnect.caption" = "Reconnecter";
"service.cells.account.caption" = "Compte";
"service.cells.account.none" = "Aucun configuré";
"service.cells.endpoint.caption" = "Extrémité";
"service.cells.provider.pool.caption" = "Locallisation";
"service.cells.provider.preset.caption" = "Préréglage";
"service.cells.provider.refresh.caption" = "Rafraîchir l'infrastructure";
"service.cells.host.parameters.caption" = "Paramètres";
"service.cells.network_settings.caption" = "Paramètres réseaux";
"service.cells.vpn_survives_sleep.caption" = "Garder actif lors de la veille";
"service.cells.vpn_resolves_hostname.caption" = "Résoudre le nom d'hôte du serveur";
//"service.cells.vpn_prefers_udp.caption" = "Préférer connection UDP";
"service.cells.trusted_mobile.caption" = "Réseau cellulaire";
"service.cells.trusted_add_wifi.caption" = "Ajouter le présent Wi-Fi";
"service.cells.trusted_policy.caption" = "La confiance désactive le VPN";
"service.cells.test_connectivity.caption" = "Tester la connection";
"service.cells.data_count.caption" = "Échanger les données";
"service.cells.data_count.none" = "Indisponible";
"service.cells.debug_log.caption" = "Journal de débogage";
"service.cells.masks_private_data.caption" = "Masquer les données de réseau";
"service.cells.report_issue.caption" = "Rapporter un problème de connection";
"service.alerts.rename.title" = "Renommer le profile";
"service.alerts.credentials_needed.message" = "Vous devez entrer les identifiants de compte premièrement.";
"service.alerts.reconnect_vpn.message" = "Voulez-vous reconnecter le VPN?";
"service.alerts.trusted.no_network.message" = "Vous n'êtes pas connectés à aucun réseau Wi-Fi.";
"service.alerts.trusted.will_disconnect_trusted.message" = "En faisant confiance à ce réseau, le VPN pourrait être déconnecté. Continuer?";
"service.alerts.trusted.will_disconnect_policy.message" = "En changeant la stratégie de confiance, le VPN pourrait être déconnecté. Continuer?";
"service.alerts.test_connectivity.title" = "Connections";
"service.alerts.test_connectivity.messages.success" = "Votre appareil est connecté à Internet!";
"service.alerts.test_connectivity.messages.failure" = "Votre appareil n'a aucune connection Invernet, veuillez vérifier vos paramètres de profile.";
"service.alerts.masks_private_data.messages.must_reconnect" = "Pour bien réinitialiser le registre de diagnostique et appliquer les préférences de camouflage, vous devez vous reconnecter au VPN maintenant.";
"service.alerts.buttons.reconnect" = "Reconnecter";
"service.alerts.download.title" = "Téléchargement requis";
"service.alerts.download.message" = "%@ requiert le téléchargement de fichiers de configuration supplémentaires.\n\nConfirmer le début du téléchargement.";
"service.alerts.download.failed" = "Échec de téléchargement des fichiers de configuration. %@";
"service.alerts.download.hud.extracting" = "Extraction des fichiers, veuillez patienter...";
"account.sections.credentials.header" = "Indetifiants";
"account.sections.guidance.footer.infrastructure.mullvad" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement numérique.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement votre courriel.";
"account.sections.guidance.footer.infrastructure.pia" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement numérique avec le préfixe \"p\" ";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Trouvez votre identifiant web %@ dans la section du site web \"Account > OpenVPN / IKEv2 nom d'utilisateur\" ";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement votre courriel.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement votre courriel.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Trouver votre identifiant %@ dans la section web Générateur de configuration OpenVPN.";
"account.sections.registration.footer" = "Allez créer un compte sur le site %@.";
"account.cells.username.caption" = "Nom d'utilisateur";
"account.cells.username.placeholder" = "nom d'utilisateur";
"account.cells.password.caption" = "Mot de passe";
"account.cells.password.placeholder" = "secret";
//"account.cells.password_confirm.caption" = "Confirmer";
//"account.cells.password_confirm.mismatch" = "Les mots de passe ne correspondent pas!";
"account.cells.open_guide.caption" = "Voir vos identifiants";
"account.cells.signup.caption" = "S'inscrire avec %@";
"endpoint.sections.location_addresses.header" = "Adresses";
"endpoint.sections.location_protocols.header" = "Protocols";
"endpoint.cells.any_address.caption" = "Automatique";
"endpoint.cells.any_protocol.caption" = "Automatique";
"provider.preset.cells.tech_details.caption" = "Détails techniques";
//"provider.preset.sections.main.footer" = "Tapotez le bouton info pour voir les détails techniques.";
"configuration.sections.communication.header" = "Communications";
"configuration.sections.reset.footer" = "Si vous obtenez une connection erronnée après le changement des parameters de communication, tapotez pour revenir à la configuration initiale.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Compression";
"configuration.sections.network.header" = "Réseau";
"configuration.sections.other.header" = "Autre";
"configuration.cells.cipher.caption" = "Cryptogramme";
"configuration.cells.digest.caption" = "Authentification";
"configuration.cells.digest.value.embedded" = "Intégré";
"configuration.cells.reset_original.caption" = "Réinitialiser la configuration";
"configuration.cells.client.caption" = "Certificat du client";
"configuration.cells.client.value.enabled" = "Verifié";
"configuration.cells.client.value.disabled" = "Non vérifié";
"configuration.cells.tls_wrapping.caption" = "Wrapping";
"configuration.cells.tls_wrapping.value.auth" = "Authentification";
"configuration.cells.tls_wrapping.value.crypt" = "Cryptage";
"configuration.cells.eku.caption" = "Vérification étendue";
"configuration.cells.default_gateway.caption" = "Passerelle par défaut";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Domaine";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Framing";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Algorithme";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Non supporté";
"configuration.cells.keep_alive.caption" = "Garder actif";
"configuration.cells.keep_alive.value.seconds" = "%d secondes";
"configuration.cells.renegotiation_seconds.caption" = "Renégociation";
"configuration.cells.renegotiation_seconds.value.after" = "aprè %@";
"configuration.cells.random_endpoint.caption" = "Extrémité aléatoire";
"network_settings.cells.choice.client" = "Lire .ovpn";
"network_settings.cells.choice.server" = "Récupérer depuis le serveur";
"network_settings.cells.address.caption" = "Adresse";
"network_settings.cells.port.caption" = "Port";
"network_settings.cells.add_dns_server.caption" = "Ajouter une adresse";
"network_settings.cells.proxy_bypass.caption" = "Outrepasser le domaine";
"network_settings.cells.add_proxy_bypass.caption" = "Ajouter outrepasser le domaine";
"debug_log.buttons.previous" = "Précédent";
"debug_log.buttons.next" = "Suivant";
"debug_log.alerts.empty_log.message" = "Le journal de débogage est vide. ";
"vpn.connecting" = "Connection...";
"vpn.active" = "Actif";
"vpn.disconnecting" = "Déconnection...";
"vpn.inactive" = "Inactid";
"vpn.disabled" = "Désactivé";
"vpn.errors.timeout" = "Délais dépassé";
"vpn.errors.dns" = "Échec DNS";
"vpn.errors.auth" = "Échec Auth";
"vpn.errors.tls" = "Échec TLS";
"vpn.errors.encryption" = "Échec du cryptage";
"vpn.errors.compression" = "Compression non supportée";
"vpn.errors.network" = "Réseau modifié";
"vpn.errors.routing" = "Routage manquant";
"vpn.errors.gateway" = "Aucune passerelle";
"issue_reporter.title" = "Rapporter un problème";
"issue_reporter.message" = "Le journal débogage de votre dernière connection est crucial pour résoudre vos problèmes de connection et est entièrement anonyme.\n\nLe fichier de configuration .ovpn, si disponible, est attaché et supprimé de toute information confidentielle.\n\nVeuillez contre-vérifier les fichiers attachés au courriel si incertain.";
"issue_reporter.buttons.accept" = "Je comprends";
"translations.title" = "Traductions";
"shortcuts.add.title" = "Ajouter un raccourcis";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Cellulaire";
"shortcuts.add.cells.connect.caption" = "Connecter à";
"shortcuts.add.cells.enable_vpn.caption" = "Activer VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Désactiver VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Faire confiance au présent réseau Wi-Fi";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Retirer le présent réseau Wi-Fi des réseaux de confiance.";
"shortcuts.add.cells.trust_cellular.caption" = "Faire confiance au présent réseau cellulaire";
"shortcuts.add.cells.untrust_cellular.caption" = "Retirer le présent réseau cellulaire des réseaux de confiance.";
"shortcuts.add.alerts.no_profiles.message" = "Il n'y a aucun profile pour se connecter.";
"shortcuts.edit.title" = "Gérer les raccourcis";
"shortcuts.edit.sections.all.header" = "Raccourcis existants";
"shortcuts.edit.cells.add_shortcut.caption" = "Ajouter un raccourcis";
"about.title" = "À propos";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Partager";
"about.cells.credits.caption" = "Crédits";
"about.cells.website.caption" = "Page d'accueil";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Avis de non-responsabilité";
"about.cells.privacy_policy.caption" = "Politique de la vie privée";
"about.cells.share_twitter.caption" = "Tweetez!";
"about.cells.share_generic.caption" = "Inviter un amis";
"donation.title" = "Faire un don";
"donation.sections.one_time.header" = "Une seule fois";
"donation.sections.one_time.footer" = "Si vous voulez manifester votre gratitude envers mon travail bénévole, voici certains montants pour faire un don instantanément.\n\n Vous n'allez être chargé qu'une seule fois par don et vous pouvez faire un don plus d'une fois.";
"donation.cells.loading.caption" = "Chargement des dons";
"donation.cells.purchasing.caption" = "Don en cours";
"donation.alerts.purchase.success.title" = "Merci";
"donation.alerts.purchase.success.message" = "Ceci signifie beaucoup pour moi et j'espère sincèrement que vous continuerez d'utiliser et de promouvoir cette app.";
"donation.alerts.purchase.failure.message" = "Impossible de faire le don. %@";
"share.message" = "Passepartout est un client OpenVPN simple d'utilisation et open source pour iOs et macOS";
"version.title" = "Version";
"version.labels.intro" = "Passepartout et TunnelKit sont codés et maintenu par Davide De Rosa (keeshux).\n\nLe code source de Passepartout et TunnelKit est publiquement disponible sur GitHub sous license GPLv3, vous pouvez trouver les liens sur la page d'accueil.\n\nPassepartout est un client non-officiel et n'est aucunement affilié avec OpenVPN Inc.";
"credits.title" = "Crédits";
"credits.sections.licenses.header" = "Licenses";
"credits.sections.notices.header" = "Préavis";
"credits.sections.translations.header" = "Traductions";
"label.license.error" = "Impossible de télécharger le contenu complet de la license.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Rimuove la rete mobile dalle reti sicure";
"1ZRTCZ" = "Disabilita VPN";
"66bZBE" = "Con il provider ${providerId}";
"7eoAss" = "Rimuove la Wi-Fi corrente dalle reti sicure";
"9GpJt5" = "Aggiunge la rete mobile alle reti sicure";
"BKxs8X" = "Aggiunge la Wi-Fi corrente alle reti sicure";
"H4taev" = "Aggiungi rete mobile sicura";
"KjkCfU" = "Avvia una connessione ad una regione specifica di un provider";
"LA99yM" = "Connetti alla VPN";
"U6o81V" = "Connettiti a ${profileId}";
"WnTPFg" = "Connettiti in ${poolName}";
"eQ1yzr" = "Disabilita il servizio VPN";
"eXXb2z" = "Avvia la connessione ad un host";
"lQ6ziK" = "Abilita VPN";
"m2E7SI" = "Aggiungi Wi-Fi sicura";
"qo3Szz" = "Connettiti a una regione del provider";
"rd1T8p" = "Rimuovi Wi-Fi sicura";
"wB1iYX" = "Rimuovi rete mobile sicura";
"xY97Vu" = "Abilita il servizio VPN con il profilo attualmente in uso";
"NCoK9B" = "Con il profilo in uso";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 6/13/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Annulla";
"global.next" = "Avanti";
"global.close" = "Chiudi";
"global.host.title_input.message" = "I caratteri ammessi sono gli alfanumerici e il trattino breve \"-\", il trattino basso \"_\" ed il punto \".\".";
"global.host.title_input.placeholder" = "Il mio profilo";
"global.email_not_configured" = "Nessun account e-mail configurato.";
"global.cells.enabled" = "Abilitato";
"global.cells.disabled" = "Disabilitato";
"global.cells.none" = "Nessuno";
"global.cells.automatic" = "Automatico";
"global.cells.manual" = "Manuale";
"reddit.title" = "Reddit";
"reddit.message" = "Sapevi che Passepartout ha un subreddit? Iscriviti per aggiornamenti o per discutere problemi, aggiunte, nuove piattaforme o qualunque cosa tu voglia.\n\nÈ anche un ottimo modo per dimostrare che hai a cuore questo progetto.";
"reddit.buttons.subscribe" = "Iscriviti ora!";
"reddit.buttons.remind" = "Ricordami più tardi";
"reddit.buttons.never" = "Non chiedere più";
"organizer.sections.providers.header" = "Provider";
"organizer.sections.providers.footer" = "Qui trovi alcuni provider con configurazioni precompilate.";
"organizer.sections.hosts.header" = "Host";
"organizer.sections.hosts.footer" = "Importa un host da un file di configurazione .ovpn.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Chiedi aiuto a Siri per velocizzare le tue interazioni più frequenti con l'app.";
"organizer.sections.support.header" = "Supporto";
"organizer.sections.feedback.header" = "Feedback";
"organizer.cells.profile.value.current" = "In uso";
"organizer.cells.add_provider.caption" = "Aggiungi provider";
"organizer.cells.add_host.caption" = "Aggiungi host";
"organizer.cells.siri_shortcuts.caption" = "Gestisci comandi rapidi";
"organizer.cells.join_community.caption" = "Entra nella community";
"organizer.cells.write_review.caption" = "Scrivi una recensione";
"organizer.cells.donate.caption" = "Fai una donazione";
"organizer.cells.patreon.caption" = "Supportami su Patreon";
"organizer.cells.translate.caption" = "Offri una traduzione";
"organizer.cells.about.caption" = "Informazioni su %@";
"organizer.cells.uninstall.caption" = "Rimuovi configurazione VPN";
"organizer.alerts.exhausted_providers.message" = "Hai creato profili per tutti i provider disponibili.";
"organizer.alerts.add_host.message" = "Apri l'URL di un file di configurazione .ovpn da Safari, Mail o da un'altra app per configurare un host.\n\nPuoi anche importare un file .ovpn con iTunes File Sharing.";
"organizer.alerts.cannot_donate.message" = "Nessun metodo di pagamento configurato su questo dispositivo.";
"organizer.alerts.delete_vpn_profile.message" = "Vuoi veramente cancellare la configurazione VPN dalle impostazioni del tuo dispositivo? Quest'azione potrebbe risolvere alcuni stati erronei della VPN e non altererà i tuoi provider e i tuoi host.";
"wizards.host.cells.title_input.caption" = "Titolo";
"wizards.host.sections.existing.header" = "Profili esistenti";
"wizards.host.alerts.existing.message" = "Esiste già un host con lo stesso titolo. Sostituire?";
"parsed_file.alerts.malformed.message" = "La configurazione contiene un'opzione malformata (%@).";
"parsed_file.alerts.missing.message" = "La configurazione non contiene un'opzione obbligatoria (%@).";
"parsed_file.alerts.unsupported.message" = "La configurazione contiene un'opzione non supportata (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "La configurazione è corretto ma contiene un'opzione potenzialmente non supportata (%@).\n\nLa connettività potrebbe fallire a seconda delle impostazioni del server.";
"parsed_file.alerts.encryption_passphrase.message" = "Per favore inserisci la passphrase di criptazione.";
"parsed_file.alerts.decryption.message" = "La configurazione contiene una chiave privata criptata e non è stato possibile decriptarla. Controlla la tua passphrase.";
"parsed_file.alerts.parsing.message" = "Impossibile processare il file di configurazione specificato (%@).";
"parsed_file.alerts.buttons.report" = "Segnala un problema";
"imported_hosts.title" = "Host importati";
"service.welcome.message" = "Benvenuto in Passepartout!\n\nUsa il menu per aggiungere un nuovo profilo.";
"service.sections.general.header" = "Generale";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "La connessione sarà stabilita ogni volta che è necessario.";
"service.sections.status.header" = "Connessione";
"service.sections.configuration.header" = "Configurazione";
"service.sections.provider_infrastructure.footer" = "Ultimo aggiornamento: %@.";
"service.sections.vpn_survives_sleep.footer" = "Disabilita per migliorare il consumo della batteria, a discapito di rallentamenti occasionali causati dalle riconnessioni.";
"service.sections.vpn_resolves_hostname.footer" = "Preferibile nella maggior parte delle reti e necessario in alcune reti IPv6. Disabilita dove il DNS è bloccato, o per velocizzare la negoziazione quando il DNS tarda a rispondere.";
//"service.sections.vpn_prefers_udp.footer" = "UDP is faster than TCP, but may not work in some networks. Disable in networks where UDP might be blocked.";
"service.sections.trusted.header" = "Reti sicure";
"service.sections.trusted.footer" = "Entrando in una rete sicura, normalmente la VPN viene spenta e mantenuta disconnessa. Disabilita quest'opzione per non imporre questo comportamento.";
"service.sections.diagnostics.header" = "Diagnostica";
"service.sections.diagnostics.footer" = "Il mascheramento sarà effettivo dopo una riconnessione. I dati di rete sono hostname, indirizzi IP, routing, SSID. Credenziali e chiavi private non sono registrati in ogni caso.";
//"service.sections.destruction.footer" = "Delete configuration from device settings.";
"service.cells.use_profile.caption" = "Usa questo profilo";
"service.cells.vpn_service.caption" = "Abilitato";
"service.cells.connection_status.caption" = "Stato";
"service.cells.reconnect.caption" = "Riconnetti";
"service.cells.account.caption" = "Account";
"service.cells.account.none" = "Non configurato";
"service.cells.endpoint.caption" = "Endpoint";
"service.cells.provider.pool.caption" = "Regione";
"service.cells.provider.preset.caption" = "Preset";
"service.cells.provider.refresh.caption" = "Aggiorna infrastruttura";
"service.cells.host.parameters.caption" = "Parametri";
"service.cells.network_settings.caption" = "Impostazioni di rete";
"service.cells.vpn_survives_sleep.caption" = "Mantieni attivo in sleep";
"service.cells.vpn_resolves_hostname.caption" = "Risolvi hostname del server";
//"service.cells.vpn_prefers_udp.caption" = "Prefer UDP socket";
"service.cells.trusted_mobile.caption" = "Rete cellulare";
"service.cells.trusted_add_wifi.caption" = "Aggiungi Wi-Fi corrente";
"service.cells.trusted_policy.caption" = "Spegni VPN in rete sicura";
"service.cells.test_connectivity.caption" = "Verifica connettività";
"service.cells.data_count.caption" = "Dati scambiati";
"service.cells.data_count.none" = "Non disponibile";
"service.cells.debug_log.caption" = "Debug log";
"service.cells.masks_private_data.caption" = "Maschera dati rete";
"service.cells.report_issue.caption" = "Segnala problema connettività";
"service.alerts.rename.title" = "Rinomina profilo";
"service.alerts.credentials_needed.message" = "Devi prima inserire le tue credenziali.";
"service.alerts.reconnect_vpn.message" = "Vuoi riconnetterti alla VPN?";
"service.alerts.trusted.no_network.message" = "Non sei connesso/a a nessuna rete Wi-Fi.";
"service.alerts.trusted.will_disconnect_trusted.message" = "Rendendo questa rete sicura, la VPN potrebbe essere disconnessa. Continuare?";
"service.alerts.trusted.will_disconnect_policy.message" = "Cambiando la politica delle reti sicure, la VPN potrebbe essere disconnessa. Continuare?";
"service.alerts.test_connectivity.title" = "Connettività";
"service.alerts.test_connectivity.messages.success" = "Il tuo dispositivo è connesso a Internet!";
"service.alerts.test_connectivity.messages.failure" = "Il tuo dispositivo non è connesso a Internet, per favore controlla i parametri del tuo profilo.";
"service.alerts.masks_private_data.messages.must_reconnect" = "Per azzerare il debug log ed applicare la nuova preferenza di mascheramento, devi riconnetterti alla VPN.";
"service.alerts.buttons.reconnect" = "Riconnetti";
"service.alerts.download.title" = "Download necessario";
"service.alerts.download.message" = "%@ richiede lo scaricamento di file di configurazione aggiuntivi.\n\nConferma per avviare lo scaricamento.";
"service.alerts.download.failed" = "Impossibile scaricare i file di configurazione. %@";
"service.alerts.download.hud.extracting" = "Estraendo i file, un attimo di pazienza...";
"account.sections.credentials.header" = "Credenziali";
"account.sections.guidance.footer.infrastructure.mullvad" = "Usa le credenziali del sito di %@. Il tuo username è generalmente numerico.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Usa le credenziali del sito di %@. Il tuo username è generalmente la tua e-mail.";
"account.sections.guidance.footer.infrastructure.pia" = "Usa le credenziali del sito di %@. Il tuo username è generalmente numerico con un prefisso \"p\".";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Trova le tue credenziali nella sezione \"Account > OpenVPN / IKEv2 Username\" del sito di %@.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Usa le credenziali del sito di %@. Il tuo username è generalmente la tua e-mail.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Usa le credenziali del sito di %@. Il tuo username è generalmente la tua e-mail.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Trova le tue credenziali nell'OpenVPN Config Generator sul sito di %@.";
"account.sections.registration.footer" = "Registra un account sul sito di %@.";
"account.cells.username.caption" = "Username";
"account.cells.username.placeholder" = "username";
"account.cells.password.caption" = "Password";
"account.cells.password.placeholder" = "segreto";
//"account.cells.password_confirm.caption" = "Confirm";
//"account.cells.password_confirm.mismatch" = "Passwords don't match!";
"account.cells.open_guide.caption" = "Vedi le tue credenziali";
"account.cells.signup.caption" = "Registrati con %@";
"endpoint.sections.location_addresses.header" = "Indirizzi";
"endpoint.sections.location_protocols.header" = "Protocolli";
"endpoint.cells.any_address.caption" = "Automatico";
"endpoint.cells.any_protocol.caption" = "Automatico";
"provider.preset.cells.tech_details.caption" = "Dettagli tecnici";
//"provider.preset.sections.main.footer" = "Tap info button to disclose technical details.";
"configuration.sections.communication.header" = "Comunicazione";
"configuration.sections.reset.footer" = "Se ti trovi con una connettività compromessa dopo aver cambiato i parametri di comunicazione, tocca per tornare alla configurazione originale.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Compressione";
"configuration.sections.network.header" = "Rete";
"configuration.sections.other.header" = "Altro";
"configuration.cells.cipher.caption" = "Cifratura";
"configuration.cells.digest.caption" = "Autenticazione";
"configuration.cells.digest.value.embedded" = "Incorporata";
"configuration.cells.reset_original.caption" = "Ripristina configurazione";
"configuration.cells.client.caption" = "Certificato client";
"configuration.cells.client.value.enabled" = "Verificato";
"configuration.cells.client.value.disabled" = "Non verificato";
"configuration.cells.tls_wrapping.caption" = "Wrapping";
"configuration.cells.tls_wrapping.value.auth" = "Autenticazione";
"configuration.cells.tls_wrapping.value.crypt" = "Criptazione";
"configuration.cells.eku.caption" = "Verifica estesa";
"configuration.cells.default_gateway.caption" = "Gateway predefinito";
"configuration.cells.dns_server.caption" = "Indirizzo";
"configuration.cells.dns_domain.caption" = "Dominio";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Framing";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Algoritmo";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Non supportato";
"configuration.cells.keep_alive.caption" = "Keep-alive";
"configuration.cells.keep_alive.value.seconds" = "%d secondi";
"configuration.cells.renegotiation_seconds.caption" = "Rinegoziazione";
"configuration.cells.renegotiation_seconds.value.after" = "dopo %@";
"configuration.cells.random_endpoint.caption" = "Endpoint casuale";
"network_settings.cells.choice.client" = "Leggi .ovpn";
"network_settings.cells.choice.server" = "Ottieni dal server";
"network_settings.cells.address.caption" = "Indirizzo";
"network_settings.cells.port.caption" = "Porta";
"network_settings.cells.add_dns_server.caption" = "Aggiungi indirizzo";
"network_settings.cells.proxy_bypass.caption" = "Dominio ignorato";
"network_settings.cells.add_proxy_bypass.caption" = "Aggiungi dominio ignorato";
"debug_log.buttons.previous" = "Precedente";
"debug_log.buttons.next" = "Successivo";
"debug_log.alerts.empty_log.message" = "Il debug log è vuoto.";
"vpn.connecting" = "Connettendo";
"vpn.active" = "Attiva";
"vpn.disconnecting" = "Disconnettendo";
"vpn.inactive" = "Inattiva";
"vpn.disabled" = "Disabilitata";
"vpn.errors.timeout" = "Timeout";
"vpn.errors.dns" = "DNS fallito";
"vpn.errors.auth" = "Autenticazione fallita";
"vpn.errors.tls" = "TLS fallito";
"vpn.errors.encryption" = "Crittografia fallita";
"vpn.errors.compression" = "Compressione non supportata";
"vpn.errors.network" = "Rete cambiata";
"vpn.errors.routing" = "Routing mancante";
"vpn.errors.gateway" = "Nessun gateway";
"issue_reporter.title" = "Segnala problema";
"issue_reporter.message" = "Il debug log delle tue ultime connessioni è cruciale per risolvere i tuoi problemi di connettività ed è completamente anonimo.\n\nIl file di configurazione .ovpn, se presente, è allegato privato di ogni dato sensibile.\n\nPer favore controlla gli allegati dell'e-mail se non sei sicuro/a.";
"issue_reporter.buttons.accept" = "Ho capito";
"translations.title" = "Traduzioni";
"shortcuts.add.title" = "Aggiungi comando rapido";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Cellulare";
"shortcuts.add.cells.connect.caption" = "Connetti a";
"shortcuts.add.cells.enable_vpn.caption" = "Abilita VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Disabilita VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Aggiungi Wi-Fi sicura";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Rimuovi Wi-Fi sicura";
"shortcuts.add.cells.trust_cellular.caption" = "Aggiungi rete mobile sicura";
"shortcuts.add.cells.untrust_cellular.caption" = "Rimuovi rete mobile sicura";
"shortcuts.add.alerts.no_profiles.message" = "Non c'è nessun profilo a cui connettersi.";
"shortcuts.edit.title" = "Gestisci comandi rapidi";
"shortcuts.edit.sections.all.header" = "Comandi esistenti";
"shortcuts.edit.cells.add_shortcut.caption" = "Aggiungi comando rapido";
"about.title" = "Informazioni su";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Condividi";
"about.cells.credits.caption" = "Credits";
"about.cells.website.caption" = "Home page";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Disclaimer";
"about.cells.privacy_policy.caption" = "Privacy policy";
"about.cells.share_twitter.caption" = "Manda un Tweet!";
"about.cells.share_generic.caption" = "Invita un amico";
"donation.title" = "Donazione";
"donation.sections.one_time.header" = "Unica";
"donation.sections.one_time.footer" = "Se vuoi mostrare gratitudine per il mio lavoro a titolo gratuito, qui trovi varie somme da donare all'istante.\n\nLa donazione ti sarà addebitata solo una volta, e puoi effettuare più donazioni.";
"donation.cells.loading.caption" = "Caricando donazioni";
"donation.cells.purchasing.caption" = "Effettuando donazione";
"donation.alerts.purchase.success.title" = "Grazie";
"donation.alerts.purchase.success.message" = "Questo significa molto per me e spero vivamente che tu continui ad usare e promuovere quest'applicazione.";
"donation.alerts.purchase.failure.message" = "Impossibile effettuare la donazione. %@";
"share.message" = "Passepartout è un client OpenVPN user-friendly ed open source per iOS e macOS";
"version.title" = "Versione";
"version.labels.intro" = "Passepartout e TunnelKit sono scritti e mantenuti da Davide De Rosa (keeshux).\n\nIl codice sorgente di Passepartout e TunnelKit è pubblicamente disponibile su GitHub in accordo con la GPLv3, puoi trovare i link nella home page.\n\nPassepartout è un client non ufficiale e non è affiliato ad OpenVPN Inc. in alcuna maniera.";
"credits.title" = "Credits";
"credits.sections.licenses.header" = "Licenze";
"credits.sections.notices.header" = "Notice";
"credits.sections.translations.header" = "Traduzioni";
"label.license.error" = "Impossibile scaricare il contenuto completo della licenza.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Verwijder Mobielnetwerk van vertrouwde netwerken";
"1ZRTCZ" = "Disable VPN";
"66bZBE" = "Met ${providerId} aanbieder";
"7eoAss" = "Verwijder huidige Wi-Fi van vertrouwde netwerken";
"9GpJt5" = "Voeg Mobielnetwerk to aan vertrouwde netwerken";
"BKxs8X" = "Voeg huidig Wi-Fi toe aan vertrouwde netwerken";
"H4taev" = "Vertrouw mobiel netwerk";
"KjkCfU" = "Maak verbinding met een specifieke lokatie van een aanbieder profiel";
"LA99yM" = "Verbind VPN";
"U6o81V" = "Verbind met ${profileId}";
"WnTPFg" = "Verbind met ${poolName}";
"eQ1yzr" = "Schakel VPN service uit";
"eXXb2z" = "Verbind met een host profiel";
"lQ6ziK" = "Schakel VPN in";
"m2E7SI" = "Vertrouw huidig Wi-Fi netwerk";
"qo3Szz" = "Maak verbinding met de locatie van de aanbieder";
"rd1T8p" = "Wantrouw huidig Wi-Fi netwerk";
"wB1iYX" = "Wantrouw modbiel netwerk";
"xY97Vu" = "Schakel de VPN-service in met het profiel dat momenteel in gebruik is";
"NCoK9B" = "Met het profiel dat momenteel in gebruik is";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 6/13/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Afbreken";
"global.next" = "Volgende";
"global.close" = "Afsluiten";
"global.host.title_input.message" = "Toegestane karakters zijn: alfanumerieke en streepjes \"-\", onderliggend streepje \"_\" en punten \".\".";
"global.host.title_input.placeholder" = "Mijn Profiel";
"global.email_not_configured" = "Er is geen email adres geconfigureerd.";
"global.cells.enabled" = "Ingeschakeld";
"global.cells.disabled" = "Uitgeschakeld";
"global.cells.none" = "Geen";
"global.cells.automatic" = "Automatisch";
"global.cells.manual" = "Handmatig";
"reddit.title" = "Reddit";
"reddit.message" = "Wist je dat Passepartout een eigen subreddit heeft? Schrijf je in voor updates, of discussiëren over problemen, (nieuwe) mogelijkheden, nieuwe platformen of wat je maar wil.\n\nHet is ook een goede manier om te laten zien dat je om dit project geeft.";
"reddit.buttons.subscribe" = "Schfijf je nu in!";
"reddit.buttons.remind" = "Herinner me later";
"reddit.buttons.never" = "Vraag dit niet meer";
"organizer.sections.providers.header" = "Aanbieders";
"organizer.sections.providers.footer" = "Hier vind je aan aantal aanbieders met configuratie profielen.";
"organizer.sections.hosts.header" = "Hosts";
"organizer.sections.hosts.footer" = "Importeer hosts met raw .ovpn configuratie bestanden.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Krijg hulp van Siri en versnel de meest gebruikte interacties binnen de app.";
"organizer.sections.support.header" = "Ondersteuning";
"organizer.sections.feedback.header" = "Terugkoppeling";
"organizer.cells.profile.value.current" = "In gebruik";
"organizer.cells.add_provider.caption" = "Voeg nieuwe aanbieder toe";
"organizer.cells.add_host.caption" = "Voeg nieuwe host toe";
"organizer.cells.siri_shortcuts.caption" = "Beheer snelkoppelingen";
"organizer.cells.join_community.caption" = "Word lid van de gemeenschap";
"organizer.cells.write_review.caption" = "Schrijf een beoordeling";
"organizer.cells.donate.caption" = "Doneer een gift";
"organizer.cells.patreon.caption" = "Steun me op Patreon";
"organizer.cells.translate.caption" = "Help met vertalen";
"organizer.cells.about.caption" = "Over %@";
"organizer.cells.uninstall.caption" = "Verwijder VPN configuratie";
"organizer.alerts.exhausted_providers.message" = "Er zijn profielen gemaakt voor elke beschikbare aanbieder.";
"organizer.alerts.add_host.message" = "Open een URL naar een .ovpn configuratie bestand met Safari, Mail of een andere app om een host profile te configureren.\n\nJe kan ook een .ovpn importeren met behulp van iTunes bestandsdeling.";
"organizer.alerts.cannot_donate.message" = "Er is geen betaalmethode geconfigureerd op dit apparaat.";
"organizer.alerts.delete_vpn_profile.message" = "Wilt u de VPN-configuratie van uw apparaatinstellingen verwijderen? Dit kan enkele problemen met VPN oplossen en heeft geen invloed op uw provider- en hostprofielen.";
"wizards.host.cells.title_input.caption" = "Titel";
"wizards.host.sections.existing.header" = "Bestaande profielen";
"wizards.host.alerts.existing.message" = "Er bestaat al een host profiel met deze titel, wil je hem vervangen?";
"parsed_file.alerts.malformed.message" = "Het configuratie betand bevat ongeldige optie(s) (%@).";
"parsed_file.alerts.missing.message" = "Het configuratiebestand mist een vereiste optie (%@).";
"parsed_file.alerts.unsupported.message" = "Het configuratiebestand bevat een niet-ondersteunde optie (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "Het configuratiebestand is correct maar bevat mogelijk een niet-ondersteunde optie (%@).\n\nConnectiviteit kan hier door niet werken, afhankelijk van de serverinstellingen.";
"parsed_file.alerts.encryption_passphrase.message" = "Voer een coderingswachtwoord in";
"parsed_file.alerts.decryption.message" = "De configuratie bevat een gecodeerde privésleutel en deze kan niet worden gedecodeerd. Controleer de ingevoerde wachtwoordzin nogmaals.";
"parsed_file.alerts.parsing.message" = "Kan het opgegeven configuratiebestand niet verwerken (%@).";
"parsed_file.alerts.buttons.report" = "Een probleem melden";
"imported_hosts.title" = "Geïmporteerde hosts";
"service.welcome.message" = "Welkom bij Passepartout!\n\nGebruik de organizer om een nieuw profiel toe te voegen.";
"service.sections.general.header" = "Algemeen";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "De verbinding zal worden gestart wanneer nodig.";
"service.sections.status.header" = "Verbinding";
"service.sections.configuration.header" = "Configuratie";
"service.sections.provider_infrastructure.footer" = "Laatste update was op %@.";
"service.sections.vpn_survives_sleep.footer" = "Uitschakelen om het batterijverbruik te verbeteren, ten koste van incidentele vertragingen als gevolg van het opnieuw opstarten na wake-up.";
"service.sections.vpn_resolves_hostname.footer" = "Voorkeur om dit aan te zetten voor de meeste netwerken en vereist in sommige IPv6-netwerken. Uitschakelen waar DNS wordt geblokkeerd, of om de onderhandelingen te versnellen wanneer DNS traag reageert.";
//"service.sections.vpn_prefers_udp.footer" = "UDP is sneller dan TCP, maar werkt mogelijk niet met sommige netwerken. Uitschakelen in netwerken waar UDP geblokkeerd is.";
"service.sections.trusted.header" = "Vertrouwde netwerken";
"service.sections.trusted.footer" = "Bij het invoeren van een vertrouwd netwerk wordt de VPN uitgeschakeld en niet verbonden gehouden. Schakel deze optie uit om dergelijk gedrag niet af te dwingen.";
"service.sections.diagnostics.header" = "Diagnose";
"service.sections.diagnostics.footer" = "De maskeerstatus is effectief na opnieuw verbinden. Netwerkgegevens zijn hostnamen, IP-adressen, routing, SSID's. Inloggegevens en privésleutels worden niet geregistreerd.";
//"service.sections.destruction.footer" = "Configuratie uit apparaatinstellingen verwijderen.";
"service.cells.use_profile.caption" = "Gebruik dit profiel";
"service.cells.vpn_service.caption" = "Ingeschakeld";
"service.cells.connection_status.caption" = "Status";
"service.cells.reconnect.caption" = "Opnieuw verbinden";
"service.cells.account.caption" = "Account";
"service.cells.account.none" = "Niets geconfigureerd";
"service.cells.endpoint.caption" = "Endpoint";
"service.cells.provider.pool.caption" = "Locatie";
"service.cells.provider.preset.caption" = "Voorkeur";
"service.cells.provider.refresh.caption" = "Vernieuw de infrastructuur";
"service.cells.host.parameters.caption" = "Parameters";
"service.cells.network_settings.caption" = "Netwerk instellingen";
"service.cells.vpn_survives_sleep.caption" = "Actief tijdens slaapstand";
"service.cells.vpn_resolves_hostname.caption" = "Haal de naam van de host op";
//"service.cells.vpn_prefers_udp.caption" = "Voorkeur voor UDP socket";
"service.cells.trusted_mobile.caption" = "Mobiel netwerk";
"service.cells.trusted_add_wifi.caption" = "Voeg huidige WiFi toe";
"service.cells.trusted_policy.caption" = "Trust disables VPN";
"service.cells.test_connectivity.caption" = "Test connectiviteit";
"service.cells.data_count.caption" = "Gegegevens uitgewisseld";
"service.cells.data_count.none" = "Niet beschikbaar";
"service.cells.debug_log.caption" = "Foutopsporingslogboek";
"service.cells.masks_private_data.caption" = "Netwerkgegevens maskeren";
"service.cells.report_issue.caption" = "Probleem met connectiviteit melden";
"service.alerts.rename.title" = "Profiel hernoemen";
"service.alerts.credentials_needed.message" = "Voer eerst de accountgegevens in.";
"service.alerts.reconnect_vpn.message" = "Opnieuw verbinding maken met de VPN?";
"service.alerts.trusted.no_network.message" = "U bent niet verbonden met een Wi-Fi-netwerk.";
"service.alerts.trusted.will_disconnect_trusted.message" = "Door dit netwerk te vertrouwen, kan de verbinding met de VPN mogelijk worden verbroken. Doorgaan?";
"service.alerts.trusted.will_disconnect_policy.message" = "Door het vertrouwensbeleid te wijzigen, kan de verbinding met de VPN mogelijk worden verbroken. Doorgaan?";
"service.alerts.test_connectivity.title" = "Connectiviteit";
"service.alerts.test_connectivity.messages.success" = "Apparaat is verbonden met internet!";
"service.alerts.test_connectivity.messages.failure" = "Uw apparaat heeft geen internetverbinding. Controleer uw profielparameters.";
"service.alerts.masks_private_data.messages.must_reconnect" = "Om het huidige foutopsporingslogboek veilig opnieuw in te stellen en de nieuwe maskeervoorkeur toe te passen, moet u nu opnieuw verbinding maken met VPN.";
"service.alerts.buttons.reconnect" = "Opnieuw verbinden";
"service.alerts.download.title" = "Download vereist";
"service.alerts.download.message" = "%@ vereist het downloaden van extra configuratiebestanden.\n\nBevestig om het downloaden te starten.";
"service.alerts.download.failed" = "Downloaden van configuratiebestanden is mislukt. %@";
"service.alerts.download.hud.extracting" = "Bestanden uitpakken, even geduld...";
"account.sections.credentials.header" = "Inloggegevens";
"account.sections.guidance.footer.infrastructure.mullvad" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal numeriek.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal uw e-mailadres.";
"account.sections.guidance.footer.infrastructure.pia" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal numeriek met \"p\" als voorvoegsel.";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Vind de inloggegevens van %@ in \"Account > OpenVPN / IKEv2 Username\" onderdeel van de website.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal uw e-mailadres.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Gebruik de inloggegevens van %@ Uw gebruikersnaam is meestal uw e-mailadres.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Vind de inloggegevens van %@ in de OpenVPN Config Generator op de website.";
"account.sections.registration.footer" = "Registreer voor een %@ account op de website.";
"account.cells.username.caption" = "Gebruikersnaam";
"account.cells.username.placeholder" = "gebruikersnaam";
"account.cells.password.caption" = "Wachtwoord";
"account.cells.password.placeholder" = "geheim";
//"account.cells.password_confirm.caption" = "Bevestig";
//"account.cells.password_confirm.mismatch" = "Wachtwoorden verschillen!";
"account.cells.open_guide.caption" = "Bekijk de inloggegevens";
"account.cells.signup.caption" = "Registreer bij %@";
"endpoint.sections.location_addresses.header" = "Adressen";
"endpoint.sections.location_protocols.header" = "Protocollen";
"endpoint.cells.any_address.caption" = "Automatisch";
"endpoint.cells.any_protocol.caption" = "Automatisch";
"provider.preset.cells.tech_details.caption" = "Technische details";
//"provider.preset.sections.main.footer" = "Tik op de info knop voor technische details.";
"configuration.sections.communication.header" = "Communicatie";
"configuration.sections.reset.footer" = "Tik hier als de connectiviteit niet meer werkt na het aanpassen van instellingen, om terug te gaan naar de originele configuratie.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Compressie";
"configuration.sections.network.header" = "Netwerk";
"configuration.sections.other.header" = "Ander";
"configuration.cells.cipher.caption" = "Cipher";
"configuration.cells.digest.caption" = "Authenticatie";
"configuration.cells.digest.value.embedded" = "Embedded";
"configuration.cells.reset_original.caption" = "Reset configuratie";
"configuration.cells.client.caption" = "Client certificaat";
"configuration.cells.client.value.enabled" = "Geverifieerd";
"configuration.cells.client.value.disabled" = "Niet geverifieerd";
"configuration.cells.tls_wrapping.caption" = "Wrapping";
"configuration.cells.tls_wrapping.value.auth" = "Authenticatie";
"configuration.cells.tls_wrapping.value.crypt" = "Versleuteling";
"configuration.cells.eku.caption" = "Uitgebreide verificatie";
"configuration.cells.default_gateway.caption" = "Standaard gateway";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Domein";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Framing";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Algoritme";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Niet ondersteund";
"configuration.cells.keep_alive.caption" = "Keep-alive";
"configuration.cells.keep_alive.value.seconds" = "%d seconden";
"configuration.cells.renegotiation_seconds.caption" = "Renegotiation";
"configuration.cells.renegotiation_seconds.value.after" = "na %@";
"configuration.cells.random_endpoint.caption" = "Willekeurig eindpunt";
"network_settings.cells.choice.client" = "Gebruik .ovpn";
"network_settings.cells.choice.server" = "Haal op van server";
"network_settings.cells.address.caption" = "Adress";
"network_settings.cells.port.caption" = "Port";
"network_settings.cells.add_dns_server.caption" = "Voeg adress toe";
"network_settings.cells.proxy_bypass.caption" = "Omzeil domein";
"network_settings.cells.add_proxy_bypass.caption" = "Voeg omzeil optie voor domein toe";
"debug_log.buttons.previous" = "Vorige";
"debug_log.buttons.next" = "Volgende";
"debug_log.alerts.empty_log.message" = "Het logboek voor foutopsporing is leeg.";
"vpn.connecting" = "Verbinden";
"vpn.active" = "Actief";
"vpn.disconnecting" = "Verbinding verbreken";
"vpn.inactive" = "Inactief";
"vpn.disabled" = "Uitgeschakeld";
"vpn.errors.timeout" = "Time-out";
"vpn.errors.dns" = "DNS niet gelukt";
"vpn.errors.auth" = "Auth niet gelukt";
"vpn.errors.tls" = "TLS niet gelukt";
"vpn.errors.encryption" = "Versleuteling mislukt";
"vpn.errors.compression" = "Compressie wordt niet ondersteund";
"vpn.errors.network" = "Netwerk veranderd";
"vpn.errors.routing" = "Ontbrekende routering";
"vpn.errors.gateway" = "Geen gateway";
"issue_reporter.title" = "Meld een probleem";
"issue_reporter.message" = "Het foutopsporingslogboek van uw laatste verbindingen is cruciaal om uw verbindingsproblemen op te lossen en is volledig geanonimiseerd.\n\nHet .ovpn configuratiebestand, indien aanwezig, is ontdaan van alle gevoelige gegevens.\n\nControleer de e-mailbijlagen als je niet zeker bent dat alles verwijderd is.";
"issue_reporter.buttons.accept" = "Ik ga akkoord";
"translations.title" = "Vertalingen";
"shortcuts.add.title" = "Voeg snelkoppeling toe";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Mobiel";
"shortcuts.add.cells.connect.caption" = "Verbind met";
"shortcuts.add.cells.enable_vpn.caption" = "Activeer VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Deactiveer VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Vertrouw huidig Wi-Fi netwerk";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Untrust current Wi-Fi";
"shortcuts.add.cells.trust_cellular.caption" = "Vertouw mobiel netwerk";
"shortcuts.add.cells.untrust_cellular.caption" = "Untrust mobiel netwerk";
"shortcuts.add.alerts.no_profiles.message" = "Er is geen profiel om verbinding mee te maken.";
"shortcuts.edit.title" = "Beheer snelkoppelingen";
"shortcuts.edit.sections.all.header" = "Bestaande snelkoppelingen";
"shortcuts.edit.cells.add_shortcut.caption" = "Voeg snelkoppeling toe";
"about.title" = "Over";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Delen";
"about.cells.credits.caption" = "Credits";
"about.cells.website.caption" = "Home page";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Vrijwaring";
"about.cells.privacy_policy.caption" = "Privacybeleid";
"about.cells.share_twitter.caption" = "Tweet about it!";
"about.cells.share_generic.caption" = "Nodig een vriend uit";
"donation.title" = "Donatie";
"donation.sections.one_time.header" = "Eenmalig";
"donation.sections.one_time.footer" = "Als je dankbaarheid wilt tonen voor mijn gratis werk, zijn hier een paar bedragen die je direct kunt doneren.\n\nHet bedrag wordt slechts één keer per donatie in rekening gebracht en u kunt meerdere keren doneren.";
"donation.cells.loading.caption" = "Ophalen donaties";
"donation.cells.purchasing.caption" = "Doneren";
"donation.alerts.purchase.success.title" = "Hartelijk dank";
"donation.alerts.purchase.success.message" = "Dit betekent veel voor mij en ik hoop echt dat je deze app blijft gebruiken en promoten.";
"donation.alerts.purchase.failure.message" = "Donatie mislukt. %@";
"share.message" = "Passepartout is een gebruiksvriendelijke open source OpenVPN-client voor iOS en macOS";
"version.title" = "Versie";
"version.labels.intro" = "Passepartout en TunnelKit zijn geschreven en worden onderhouden door Davide De Rosa (keeshux).\n\nDe broncode voor Passepartout en TunnelKit is openbaar beschikbaar op GitHub onder de GPLv3, je kunt links op de startpagina vinden.\n\nPassepartout is een niet-officiële client en is op geen enkele manier verbonden aan OpenVPN Inc.";
"credits.title" = "Credits";
"credits.sections.licenses.header" = "Licenties";
"credits.sections.notices.header" = "Mededelingen";
"credits.sections.translations.header" = "Vertalingen";
"label.license.error" = "Kan volledige licentie-inhoud niet downloaden.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Remover celular de conexões seguras";
"1ZRTCZ" = "Desativar VPN";
"66bZBE" = "Com o provedor ${providerId}";
"7eoAss" = "Remover Wi-Fi atual de conexões seguras";
"9GpJt5" = "Adicionar celular em conexões seguras";
"BKxs8X" = "Adicionar Wi-Fi atual em conexões seguras";
"H4taev" = "Confiar em rede celular";
"KjkCfU" = "Conectar em uma localização específica de um provedor";
"LA99yM" = "Conectar VPN";
"U6o81V" = "Conectar ${profileId}";
"WnTPFg" = "Conectar ${poolName}";
"eQ1yzr" = "Desabilitar serviço de VPN";
"eXXb2z" = "Conectar em um host";
"lQ6ziK" = "Ativar VPN";
"m2E7SI" = "Confiar na Wi-Fi atual";
"qo3Szz" = "Conectar em uma região do provedor";
"rd1T8p" = "Não confiar na Wi-Fi atual";
"wB1iYX" = "Não confiar na conexão celular";
"xY97Vu" = "Ativar o serviço VPN no perfil em uso";
"NCoK9B" = "Com o perfil em uso";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 6/13/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Cancelar";
"global.next" = "Próximo";
"global.close" = "Fechar";
"global.host.title_input.message" = "Caracteres aceitos são alfanuméricos, mais traço \"-\", underline \"_\" e ponto \".\".";
"global.host.title_input.placeholder" = "Meu perfil";
"global.email_not_configured" = "Nenhuma conta de email configurada.";
"global.cells.enabled" = "Ativado";
"global.cells.disabled" = "Desativado";
"global.cells.none" = "Nenhum";
"global.cells.automatic" = "Automático";
"global.cells.manual" = "Manual";
"reddit.title" = "Reddit";
"reddit.message" = "Você sabia que Passepartout tem um subreddit? Siga-nos para atualizações ou para discutir problemas, novas funcionalidades, ou qualquer outro tópico.\n\nÉ uma boa maneira de mostrar seu interesse pelo projeto.";
"reddit.buttons.subscribe" = "Seguir!";
"reddit.buttons.remind" = "Lembrar-me depois";
"reddit.buttons.never" = "Não perguntar novamente";
"organizer.sections.providers.header" = "Provedores";
"organizer.sections.providers.footer" = "Aqui você encontra um provedor com o perfil pré-configurado.";
"organizer.sections.hosts.header" = "Hosts";
"organizer.sections.hosts.footer" = "Importar hosts de um arquivo de configuração .ovpn.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Peça ajuda para Siri para agilar tarefas comum do aplicativo.";
"organizer.sections.support.header" = "Suporte";
"organizer.sections.feedback.header" = "Feedback";
"organizer.cells.profile.value.current" = "Ativo";
"organizer.cells.add_provider.caption" = "Adicionar novo perfil";
"organizer.cells.add_host.caption" = "Adicionar novo host";
"organizer.cells.siri_shortcuts.caption" = "Gerenciar atalhos";
"organizer.cells.join_community.caption" = "Participar da comunidade";
"organizer.cells.write_review.caption" = "Escrever avaliação";
"organizer.cells.donate.caption" = "Fazer doação";
"organizer.cells.patreon.caption" = "Contribuir no Patreon";
"organizer.cells.translate.caption" = "Ajudar-nos na tradução";
"organizer.cells.about.caption" = "Sobre %@";
"organizer.cells.uninstall.caption" = "Remover configuração VPN";
"organizer.alerts.exhausted_providers.message" = "Você criou um perfil para qualquer provedor disponível.";
"organizer.alerts.add_host.message" = "Abre uma URL para um arquivo de configuração .ovpn no Safari, Mail ou outro aplicativo.\n\nVocê pode também importar um .ovpn com o compartilhamento de arquivos do iTunes.";
"organizer.alerts.cannot_donate.message" = "Nenhum meio de pagamento configurado nesse dispositivo.";
"organizer.alerts.delete_vpn_profile.message" = "Tem certeza que deseja remover as configurações de VPN do seu dispositivo? Isso poderá corrigir problemas com o estado atual, sem afetar seu provedor e perfis do host.";
"wizards.host.cells.title_input.caption" = "Título";
"wizards.host.sections.existing.header" = "Perfis existentes";
"wizards.host.alerts.existing.message" = "Já existe um perfil com esse nome, deseja substituí-lo?";
"parsed_file.alerts.malformed.message" = "O arquivo de configuração possui uma opção não formatada corretamente (%@).";
"parsed_file.alerts.missing.message" = "O arquivo não possui todas configurações requeridas (%@).";
"parsed_file.alerts.unsupported.message" = "O arquivo de configuração possui uma opção não suportada (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "O arquivo de configuração está correto, mas provavelmente possui uma opção não suportada (%@).\n\nSua conexão poderá ser instável dependendo as configurações do servidor.";
"parsed_file.alerts.encryption_passphrase.message" = "Por favor, digite sua senha de criptografia.";
"parsed_file.alerts.decryption.message" = "Sua configiração possui uma chave privada criptografada que talvez não possa ser descriptografada. Verifique novamente sua senha de criptografia.";
"parsed_file.alerts.parsing.message" = "Não foi possível processar as configurações do arquivo (%@).";
"parsed_file.alerts.buttons.report" = "Reportar problema";
"imported_hosts.title" = "Hosts importados";
"service.welcome.message" = "Bem-vindo ao Passepartout!\n\nUse o organizador para adicionar um novo perfil.";
"service.sections.general.header" = "Geral";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "A conexão será estabeleciada assim que necessária.";
"service.sections.status.header" = "Conexão";
"service.sections.configuration.header" = "Configuração";
"service.sections.provider_infrastructure.footer" = "Última atualização em %@.";
"service.sections.vpn_survives_sleep.footer" = "Desative para melhorar o consumo de bateria, o que poderá ocasionar queda de performance quando o restabelecimento de conexão for realizado.";
"service.sections.vpn_resolves_hostname.footer" = "Recomendado para maioria das redes e requirido em algumas redes IPv6. Desative se o DNS estiver bloqueado, ou para acelerar o DNS quando o mesmo está devagar.";
//"service.sections.vpn_prefers_udp.footer" = "UDP é mais rápido que TCP, mas pode não funcionar em algumas redes. Desative em redes onde UDP possa estar bloqueado.";
"service.sections.trusted.header" = "Redes seguras";
"service.sections.trusted.footer" = "Ao entrar em uma rede segura, a VPN é normalmente é desconectada e mantido inativa. Desative essa opção para não forçar esse comportamento.";
"service.sections.diagnostics.header" = "Diagnóstico";
"service.sections.diagnostics.footer" = "O status será escondido após reconectado. Os dados da rede são hostnames, endereços de IP, rotas, SSID. Credenciais e chaves privadas não será logadas em nenhum dos casos.";
//"service.sections.destruction.footer" = "Deletar configuração das preferências do dispositivo.";
"service.cells.use_profile.caption" = "Usar esse perfil";
"service.cells.vpn_service.caption" = "Ativado";
"service.cells.connection_status.caption" = "Status";
"service.cells.reconnect.caption" = "Reconectar";
"service.cells.account.caption" = "Conta";
"service.cells.account.none" = "Nenhum configurado";
"service.cells.endpoint.caption" = "Endereço";
"service.cells.provider.pool.caption" = "Localização";
"service.cells.provider.preset.caption" = "Pré-definição";
"service.cells.provider.refresh.caption" = "Atualizar infraestrutura";
"service.cells.host.parameters.caption" = "Parâmetros";
"service.cells.network_settings.caption" = "Configurações de rede";
"service.cells.vpn_survives_sleep.caption" = "Manter ativo em modo descanço";
"service.cells.vpn_resolves_hostname.caption" = "Resolver hostname do servidor";
//"service.cells.vpn_prefers_udp.caption" = "Preferir socket UDP";
"service.cells.trusted_mobile.caption" = "Rede celular";
"service.cells.trusted_add_wifi.caption" = "Adicionar Wi-Fi atual";
"service.cells.trusted_policy.caption" = "Trust disables VPN";
"service.cells.test_connectivity.caption" = "Testar conexão";
"service.cells.data_count.caption" = "Dados transferidos";
"service.cells.data_count.none" = "Indisponível";
"service.cells.debug_log.caption" = "Log de Debug";
"service.cells.masks_private_data.caption" = "Esconder dados da rede";
"service.cells.report_issue.caption" = "Reportar problemas de conexão";
"service.alerts.rename.title" = "Renomear perfil";
"service.alerts.credentials_needed.message" = "Primeiramente você precisa preencher suas credenciais.";
"service.alerts.reconnect_vpn.message" = "Deseja reconectar à VPN?";
"service.alerts.trusted.no_network.message" = "Você não está conectado em nenhuma rede Wi-Fi.";
"service.alerts.trusted.will_disconnect_trusted.message" = "Ao confiar nessa rede, sua VPN provavelmente será desconectada. Deseja continuar?";
"service.alerts.trusted.will_disconnect_policy.message" = "Ao alterar a política de segurança, sua VPN provavelmente será desconectada. Deseja continuar?";
"service.alerts.test_connectivity.title" = "Conectividade";
"service.alerts.test_connectivity.messages.success" = "Seu dispositivo está conectado à Internet!";
"service.alerts.test_connectivity.messages.failure" = "Seu dispositivo não está conectado à Internet, por favor, verifique sua configurações.";
"service.alerts.masks_private_data.messages.must_reconnect" = "Para garantir uma restauração segura do seu log de debug, você precisa reconectar à VPN.";
"service.alerts.buttons.reconnect" = "Reconectar";
"service.alerts.download.title" = "Download requirido";
"service.alerts.download.message" = "%@ requer o download de arquivos de configuração adicionais.\n\nConfirme para iniciar.";
"service.alerts.download.failed" = "Erro no download do arquivo de configuração. %@";
"service.alerts.download.hud.extracting" = "Extraindo arquivos, seja paciente...";
"account.sections.credentials.header" = "Credenciais";
"account.sections.guidance.footer.infrastructure.mullvad" = "Utilize %@ credenciais do site. Seu usuário é normalmente numérico.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Utilize %@ credenciais do site. Seu usuário é normalmente o seu email.";
"account.sections.guidance.footer.infrastructure.pia" = "Utilize %@ credenciais do site. Seu usuário é normalmente numérico com prefixo \"p\".";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Encontre %@ credenciais na sessão \"Account > OpenVPN / IKEv2 Username\" do site.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Utilize %@ credenciais do site. Seu usuário é normalmente o seu email.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Utilize %@ credenciais do site. Seu usuário é normalmente o seu email.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Encontre %@ credenciais no gerador de configuração OpenVPN do site.";
"account.sections.registration.footer" = "Registrar em %@ website.";
"account.cells.username.caption" = "Usuário";
"account.cells.username.placeholder" = "usuário";
"account.cells.password.caption" = "Senha";
"account.cells.password.placeholder" = "senha secreta";
//"account.cells.password_confirm.caption" = "Confirmar";
//"account.cells.password_confirm.mismatch" = "Senhas não conferem!";
"account.cells.open_guide.caption" = "Ver sua credenciais";
"account.cells.signup.caption" = "Registrar com %@";
"endpoint.sections.location_addresses.header" = "Endereços";
"endpoint.sections.location_protocols.header" = "Protocolos";
"endpoint.cells.any_address.caption" = "Automático";
"endpoint.cells.any_protocol.caption" = "Automático";
"provider.preset.cells.tech_details.caption" = "Detalhes técnicos";
//"provider.preset.sections.main.footer" = "Toque no botão \"Informação.\" para mostrar detalhes técnicos.";
"configuration.sections.communication.header" = "Comunicação";
"configuration.sections.reset.footer" = "Se você foi desconectado após mudar parâmetros de comunicação, toque para restaurar a configuração original.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Compressão";
"configuration.sections.network.header" = "Rede";
"configuration.sections.other.header" = "Outro";
"configuration.cells.cipher.caption" = "Criptografada";
"configuration.cells.digest.caption" = "Autenticação";
"configuration.cells.digest.value.embedded" = "Agregado";
"configuration.cells.reset_original.caption" = "Restaurar configuração";
"configuration.cells.client.caption" = "Certificado do cliente";
"configuration.cells.client.value.enabled" = "Verificado";
"configuration.cells.client.value.disabled" = "Não verificado";
"configuration.cells.tls_wrapping.caption" = "Wrapping";
"configuration.cells.tls_wrapping.value.auth" = "Autenticação";
"configuration.cells.tls_wrapping.value.crypt" = "Criptografia";
"configuration.cells.eku.caption" = "Verificação extendida";
"configuration.cells.default_gateway.caption" = "Gateway padrão";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Domínio";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Framing";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Algorítimo";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Não suportado";
"configuration.cells.keep_alive.caption" = "Manter ativo";
"configuration.cells.keep_alive.value.seconds" = "%d segundos";
"configuration.cells.renegotiation_seconds.caption" = "Renegociando";
"configuration.cells.renegotiation_seconds.value.after" = "depois de %@";
"configuration.cells.random_endpoint.caption" = "Destino randômico";
"network_settings.cells.choice.client" = "Ler .ovpn";
"network_settings.cells.choice.server" = "Puxar do servidor";
"network_settings.cells.address.caption" = "Endereço";
"network_settings.cells.port.caption" = "Porta";
"network_settings.cells.add_dns_server.caption" = "Adicionar endereço";
"network_settings.cells.proxy_bypass.caption" = "Domínio ignorado";
"network_settings.cells.add_proxy_bypass.caption" = "Adicionar domínio ignorado";
"debug_log.buttons.previous" = "Anterior";
"debug_log.buttons.next" = "Próximo";
"debug_log.alerts.empty_log.message" = "O log está vazio.";
"vpn.connecting" = "Conectando";
"vpn.active" = "Ativa";
"vpn.disconnecting" = "Desconectando";
"vpn.inactive" = "Inativo";
"vpn.disabled" = "Desativado";
"vpn.errors.timeout" = "Timeout";
"vpn.errors.dns" = "Falha no DNS";
"vpn.errors.auth" = "Falha na autenticação";
"vpn.errors.tls" = "Falha no TLS";
"vpn.errors.encryption" = "Falha na criptografia";
"vpn.errors.compression" = "Compressão não suportada";
"vpn.errors.network" = "Rede alterada";
"vpn.errors.routing" = "Rota necessária";
"vpn.errors.gateway" = "Sem gateway";
"issue_reporter.title" = "Reportar problema";
"issue_reporter.message" = "O log de suas últimas conexões é crucial para resolver problemas de conectividade e é completamemnte anônimo.\n\nO arquivo de conexão .ovpn, se existente, é anexado sem nenhum dado sensitivo.\n\nPor favor, verique o anexos de email se necessário.";
"issue_reporter.buttons.accept" = "Eu concordo";
"translations.title" = "Traduções";
"shortcuts.add.title" = "Adicionar atalho";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Celular";
"shortcuts.add.cells.connect.caption" = "Conectar à";
"shortcuts.add.cells.enable_vpn.caption" = "Ativar VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Desativar VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Confiar na Wi-Fi atual";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Não confiar na Wi-Fi atual";
"shortcuts.add.cells.trust_cellular.caption" = "Confiar em rede celular";
"shortcuts.add.cells.untrust_cellular.caption" = "Não confiar em rede celular";
"shortcuts.add.alerts.no_profiles.message" = "Ainda não existe nenhum perfil para se conectar.";
"shortcuts.edit.title" = "Configuração de atalhos";
"shortcuts.edit.sections.all.header" = "Atalhos existentes";
"shortcuts.edit.cells.add_shortcut.caption" = "Adicionar atalho";
"about.title" = "Sobre";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Compartilhar";
"about.cells.credits.caption" = "Créditos";
"about.cells.website.caption" = "Home page";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Disclaimer";
"about.cells.privacy_policy.caption" = "Política de privacidade";
"about.cells.share_twitter.caption" = "Tweet sobre isso!";
"about.cells.share_generic.caption" = "Convide um amigo";
"donation.title" = "Doar";
"donation.sections.one_time.header" = "Uma vez";
"donation.sections.one_time.footer" = "Se você deseja mostrar gratidão pelo meu trabalho, aqui estão alguns valores do qual você pode contribuir.\n\nVocé só será cobrado uma única vez, ou doar mais vezes caso desejar.";
"donation.cells.loading.caption" = "Carregando doações";
"donation.cells.purchasing.caption" = "Efetuando doação";
"donation.alerts.purchase.success.title" = "Obrigado";
"donation.alerts.purchase.success.message" = "Isso significa muito para mim! Espero que você continue usando e promovendo esse aplicativo.";
"donation.alerts.purchase.failure.message" = "Não foi possível realizar doação. %@";
"share.message" = "Passepartout é um cliente OpenVPN fácil e open-source para iOS e macOS";
"version.title" = "Versão";
"version.labels.intro" = "Passepartout e TunnelKit são desenvolvidos e mantidos por Davide De Rosa (keeshux).\n\nO código de fonte está disponível no GitHub sobre a licença GPLv3, você pode encontrar links na home page.\n\nPassepartout não é um cliente oficial e não possui nenhuma ligação com a OpenVPN Inc.";
"credits.title" = "Créditos";
"credits.sections.licenses.header" = "Licenças";
"credits.sections.notices.header" = "Notices";
"credits.sections.translations.header" = "Traduções";
"label.license.error" = "Não foi possível realizar o download da licença.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Удаляет мобильную сеть из доверенных подключений";
"1ZRTCZ" = "Отключить VPN";
"66bZBE" = "С ${providerId} провайдером";
"7eoAss" = "Удаляет текущий Wi-Fi из доверенных подключений";
"9GpJt5" = "Добавляет мобильную сеть в доверенные подключения";
"BKxs8X" = "Добавляет текущий  Wi-Fi в доверенные подключения";
"H4taev" = "Доверять мобильной сети";
"KjkCfU" = "Подключиться к конкретному местоположению провайдера";
"LA99yM" = "Подключиться к VPN";
"U6o81V" = "Подключиться к ${profileId}";
"WnTPFg" = "Подключиться к ${poolName}";
"eQ1yzr" = "Отключить этот VPN сервис";
"eXXb2z" = "Подключается к хост профилю";
"lQ6ziK" = "Включить VPN";
"m2E7SI" = "Доверять текущему Wi-Fi";
"qo3Szz" = "Подключиться к местоположению провайдера";
"rd1T8p" = "Не доверять текущему Wi-Fi";
"wB1iYX" = "Не доверять мобильной сети";
"xY97Vu" = "Включает VPN с используемым профилем";
"NCoK9B" = "С используемым профилем";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 4/23/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "Ок";
"global.cancel" = "Отменить";
"global.next" = "Далее";
"global.close" = "Закрыть";
"global.host.title_input.message" = "Разрешены буквы латиницы, дэш \"-\", нижнее подчеркивание \"_\" и точка \".\".";
"global.host.title_input.placeholder" = "Мой профиль";
"global.email_not_configured" = "E-mail аккаунт не создан.";
"global.cells.enabled" = "Включен";
"global.cells.disabled" = "Выключен";
"global.cells.none" = "Нет";
"global.cells.automatic" = "Автоматически";
"global.cells.manual" = "Вручную";
"reddit.title" = "Reddit";
"reddit.message" = "А Вы знали, что Passepartout имеет свой сабреддит? Подписывайтесь для получения обновлений, обсуждения проблем, функций, новых платформ или чего угодно.\n\nЭто также отличный способ показать поддержку проекта.";
"reddit.buttons.subscribe" = "Подписаться сейчас!";
"reddit.buttons.remind" = "Напомнить позже";
"reddit.buttons.never" = "Больше не спрашивать";
"organizer.sections.providers.header" = "Провайдеры";
"organizer.sections.providers.footer" = "Здесь Вы найдёте несколько провайдеров с уже созданными профилями.";
"organizer.sections.hosts.header" = "Хосты|Hosts";
"organizer.sections.hosts.footer" = "Импорт хостов из .ovpn файлов";
"organizer.sections.siri.header" = "Сири";
"organizer.sections.siri.footer" = "Получить помощь Сири, чтобы ускорить частые действия с приложением.";
"organizer.sections.support.header" = "Поддержка";
"organizer.sections.feedback.header" = "Отзыв";
"organizer.cells.profile.value.current" = "Используется";
"organizer.cells.add_provider.caption" = "Добавить нового провайдера";
"organizer.cells.add_host.caption" = "Добавить новый хост";
"organizer.cells.siri_shortcuts.caption" = "Управлять коммандами";
"organizer.cells.join_community.caption" = "Вступить в сообщество";
"organizer.cells.write_review.caption" = "Написать отзыв";
"organizer.cells.donate.caption" = "Сделать пожертвование";
"organizer.cells.patreon.caption" = "Поддержите меня на Patreon";
"organizer.cells.translate.caption" = "Помощь с переводом";
"organizer.cells.about.caption" = "Об %@";
"organizer.cells.uninstall.caption" = "Удалить VPN конфигурацию";
"organizer.alerts.exhausted_providers.message" = "Вы создали профили для всех доступных провайдеров.";
"organizer.alerts.add_host.message" = "Откройте ссылку на .ovpn файл конфигурации через Safari, Почту или другое приложение для добавление хост профиля.\n\nВы также можете импортировать .ovpn файл через общие файлы iTunes.";
"organizer.alerts.cannot_donate.message" = "На этом усторйстве не выбран способ платежа.";
"organizer.alerts.delete_vpn_profile.message" = "Вы действительно хотите убрать VPN конфигурацию из настроек устройства? Это может исправить несколько VPN ошибок, но не изменит установки приложения.";
"wizards.host.cells.title_input.caption" = "Название";
"wizards.host.sections.existing.header" = "Существующие профили";
"wizards.host.alerts.existing.message" = "Хост профиль с этим названием уже существует. Заменить?";
"parsed_file.alerts.malformed.message" = "Файл конфигурации содержит неверную опцию (%@).";
"parsed_file.alerts.missing.message" = "Файл конфигурации не содержит необходимую опцию (%@).";
"parsed_file.alerts.unsupported.message" = "Файл конфигурации содержит неподдерживаемую опцию (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "Файл конфигурации верный, но возможно содержит неподдерживаемую опцию (%@).\n\nСоединение может прерваться - зависит от настроек сервера.";
"parsed_file.alerts.encryption_passphrase.message" = "Пожалуйста, введите кодовую фразу шифрования";
"parsed_file.alerts.decryption.message" = "Конфигурация содержит зашифрованный приватный ключ, он не может быть расшифрован. Перепроверьте кодовую фразу.";
"parsed_file.alerts.parsing.message" = "Не получается разобрать предоставленный файл конфигурации (%@).";
"parsed_file.alerts.buttons.report" = "Сообщить о проблеме";
"imported_hosts.title" = "Импортированные хост профили";
"service.welcome.message" = "Добро пожаловать в Passepartout!\n\nИспользуйте организатор для добавления нового профиля.";
"service.sections.general.header" = "Основное";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "Соединение будет установлено при необходимости.";
"service.sections.status.header" = "Соединение";
"service.sections.configuration.header" = "Конфигурация";
"service.sections.provider_infrastructure.footer" = "Последнее обновление %@.";
"service.sections.vpn_survives_sleep.footer" = "Отключите для уменьшения расхода заряда аккумулятора, может привести к временным замедлениям в связи с повторным подключением после \"пробуждения\".";
"service.sections.vpn_resolves_hostname.footer" = "Предпочтительно в большинстве сетей и необходимо в некоторых IPv6 сетях. Отключите если  DNS заблокирован, или для увеличения скорости в случае медленных ответов DNS.";
//"service.sections.vpn_prefers_udp.footer" = "UDP быстрее TCP, но может не работать в некоторых сетях. Отключите в случае блокировки UDP в Вашей сети.";
"service.sections.trusted.header" = "Доверенные сети";
"service.sections.trusted.footer" = "При подключении к доверенным сетям VPN обычно выключается, и остаётся отключенным. Отключите эту опцию чтобы оставлять VPN подключенным.";
"service.sections.diagnostics.header" = "Диагностика";
"service.sections.diagnostics.footer" = "Маскировка включится после повторного подключения. Информация о сети - это названия хост профилей, IP адрес, маршрутизация и SSID. Данные для входа и приватные ключи не собираются.";
//"service.sections.destruction.footer" = "Удалить конфигурацию из настроек устройства.";
"service.cells.use_profile.caption" = "Использовать это профиль.";
"service.cells.vpn_service.caption" = "Включен";
"service.cells.connection_status.caption" = "Статус";
"service.cells.reconnect.caption" = "Переподключиться";
"service.cells.account.caption" = "Аккаунт";
"service.cells.account.none" = "Ничего не создано";
"service.cells.endpoint.caption" = "Конечная точка";
"service.cells.provider.pool.caption" = "Местоположение";
"service.cells.provider.preset.caption" = "Пресет";
"service.cells.provider.refresh.caption" = "Обновить инфраструктуру";
"service.cells.host.parameters.caption" = "Параметры";
"service.cells.network_settings.caption" = "Сетевые настройки";
"service.cells.vpn_survives_sleep.caption" = "Оставлять включенным во время сна";
"service.cells.vpn_resolves_hostname.caption" = "Разрешить имя хоста сервера";
//"service.cells.vpn_prefers_udp.caption" = "Предпочитать UDP подключение";
"service.cells.trusted_mobile.caption" = "Мобильная сеть";
"service.cells.trusted_add_wifi.caption" = "Добавить текущий Wi-Fi";
"service.cells.trusted_policy.caption" = "Дов. сеть отключает VPN";
"service.cells.test_connectivity.caption" = "Проверить подключение";
"service.cells.data_count.caption" = "Переданная информация";
"service.cells.data_count.none" = "Недоступно";
"service.cells.debug_log.caption" = "Журнал отладки";
"service.cells.masks_private_data.caption" = "Маскировать информацию сети";
"service.cells.report_issue.caption" = "Сообщить о проблеме подкл.";
"service.alerts.rename.title" = "Переименовать профиль";
"service.alerts.credentials_needed.message" = "Сначала нужно ввести данные аккаунта.";
"service.alerts.reconnect_vpn.message" = "Хотите заново подключиться к VPN?";
"service.alerts.trusted.no_network.message" = "Вы не подключены к Wi-Fi.";
"service.alerts.trusted.will_disconnect_trusted.message" = "При доверии этой сети VPN может быть отключен. Продолжить?";
"service.alerts.trusted.will_disconnect_policy.message" = "При изменении установок доверия VPN может быть отключен. Продолжить?";
"service.alerts.test_connectivity.title" = "Связь";
"service.alerts.test_connectivity.messages.success" = "Ваше устройство подключено к интернету!";
"service.alerts.test_connectivity.messages.failure" = "Ваше устройство не подключено к интернету, пожалйста проверьте установки Вашего профиля.";
"service.alerts.masks_private_data.messages.must_reconnect" = "Для безопасного сброса журнала отладки и изменения маскировки информации сети Вы должны заново подключиться к VPN.";
"service.alerts.buttons.reconnect" = "Переподключить";
"service.alerts.download.title" = "Необходимо скачивание";
"service.alerts.download.message" = "%@ необходимы дополнительные файлы конфигурации.\n\nПодтвердите для скачивания.";
"service.alerts.download.failed" = "Не удалось скачать файлы конфигурации.%@";
"service.alerts.download.hud.extracting" = "Извлечение файлов, пожалуста подождите...";
"account.sections.credentials.header" = "Данные для входа";
"account.sections.guidance.footer.infrastructure.mullvad" = "Используйте Ваши данные для входа с веб-сайта %@. Ваш логин обычно числовой с.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Используйте данные для входа на %@ веб-сайт. Ваш логин обычно Ваш e-mail.";
"account.sections.guidance.footer.infrastructure.pia" = "Используйте Ваши данные для входа с веб-сайта %@. Ваш логин обычно числовой с приставкой \"p\".";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Найдите Ваши данные для входа %@ \"Account > OpenVPN / IKEv2 Username\" секции веб-сайта.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Используйте данные для входа на %@ веб-сайт. Ваш логин обычно Ваш e-mail.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Используйте данные для входа на %@ веб-сайт. Ваш логин обычно Ваш e-mail.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Найдите Ваши данные для входа %@ в OpenVPN Config Generator на веб-сайте.";
"account.sections.registration.footer" = "Создайте аккаунт на %@ веб-сайте.";
"account.cells.username.caption" = "Логин";
"account.cells.username.placeholder" = "логин";
"account.cells.password.caption" = "Пароль";
"account.cells.password.placeholder" = "пароль";
//"account.cells.password_confirm.caption" = "Подтвердить";
//"account.cells.password_confirm.mismatch" = "Пароли не совпадают!";
"account.cells.open_guide.caption" = "Проверьте Ваши данные";
"account.cells.signup.caption" = "Зарегистрируйтесь с %@";
"endpoint.sections.location_addresses.header" = "Адреса";
"endpoint.sections.location_protocols.header" = "Протоколы";
"endpoint.cells.any_address.caption" = "Автоматически";
"endpoint.cells.any_protocol.caption" = "Автоматически";
"provider.preset.cells.tech_details.caption" = "Техническая информация";
//"provider.preset.sections.main.footer" = "Нажмите i для раскрытия технической информации.";
"configuration.sections.communication.header" = "Связь";
"configuration.sections.reset.footer" = "Если после изменения параметров связи у Вас разорвалось соединение, нажмите, чтобы вернуться к исходной конфигурации.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Компресия";
"configuration.sections.network.header" = "Сеть";
"configuration.sections.other.header" = "Другое";
"configuration.cells.cipher.caption" = "Шифруем";
"configuration.cells.digest.caption" = "Аутентификация";
"configuration.cells.digest.value.embedded" = "Внедрена";
"configuration.cells.reset_original.caption" = "Сброс конфигурации";
"configuration.cells.client.caption" = "Сертификат клиента";
"configuration.cells.client.value.enabled" = "Проверено";
"configuration.cells.client.value.disabled" = "Не проверено";
"configuration.cells.tls_wrapping.caption" = "Упаковываем";
"configuration.cells.tls_wrapping.value.auth" = "Аутентификация";
"configuration.cells.tls_wrapping.value.crypt" = "Шифрование";
"configuration.cells.eku.caption" = "Расширенная проверка";
"configuration.cells.default_gateway.caption" = "Шлюз по умолчанию";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Домен";
"configuration.cells.proxy_http.caption" = "Прокси";
"configuration.cells.proxy_https.caption" = "Прокси (HTTPS)";
"configuration.cells.compression_framing.caption" = "Фрейминг";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Алгоритм";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Неподдерживаемое";
"configuration.cells.keep_alive.caption" = "Поддерживаем";
"configuration.cells.keep_alive.value.seconds" = "%d секунд";
"configuration.cells.renegotiation_seconds.caption" = "Перезаключение";
"configuration.cells.renegotiation_seconds.value.after" = "после %@";
"configuration.cells.random_endpoint.caption" = "Рандомная конечная точка";
"network_settings.cells.choice.client" = "Читать .ovpn";
"network_settings.cells.choice.server" = "Вытащить с сервера";
"network_settings.cells.address.caption" = "Адрес";
"network_settings.cells.port.caption" = "порт";
"network_settings.cells.add_dns_server.caption" = "Добавить адрес";
"network_settings.cells.proxy_bypass.caption" = "Обход домена";
"network_settings.cells.add_proxy_bypass.caption" = "Добавить обходной домен";
"debug_log.buttons.previous" = "Предыдущий";
"debug_log.buttons.next" = "Следующий";
"debug_log.alerts.empty_log.message" = "Журнал отладки пуст.";
"vpn.connecting" = "Подключается";
"vpn.active" = "Активен";
"vpn.disconnecting" = "Отключается";
"vpn.inactive" = "Не активен";
"vpn.disabled" = "Отключен";
"vpn.errors.timeout" = "Тайм-аут";
"vpn.errors.dns" = "Ошибка DNS";
"vpn.errors.auth" = "Ошибка аутентификации";
"vpn.errors.tls" = "Ошибка TSL";
"vpn.errors.encryption" = "Ошибка расшифровки";
"vpn.errors.compression" = "Сжатие не поддерживается";
"vpn.errors.network" = "Изменение сети";
"vpn.errors.routing" = "Отсутствует маршрутизация";
"vpn.errors.gateway" = "Нет шлюза";
"issue_reporter.title" = "Сообщить о проблеме";
"issue_reporter.message" = "Журнал отладки Вашего последнего соединения необходим для разрешения проблем подключения, и является полностью анонимным.\n\n .ovpn файл, если есть, прикреплён без каких-либо конфиденциальных данных .\n\nПожалуйста, перепроверьте прикреплённые файлы, если не уверены.";
"issue_reporter.buttons.accept" = "Я понимаю";
"translations.title" = "Переводы";
"shortcuts.add.title" = "Создать команду";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Мобильная сеть";
"shortcuts.add.cells.connect.caption" = "Подключиться к";
"shortcuts.add.cells.enable_vpn.caption" = "Включи VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Выключи VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Доверять текущему Wi-Fi";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Не доверять текущему Wi-Fi";
"shortcuts.add.cells.trust_cellular.caption" = "Доверять мобильной сети";
"shortcuts.add.cells.untrust_cellular.caption" = "Не доверять мобильной сети";
"shortcuts.add.alerts.no_profiles.message" = "Нет профиля для подключения.";
"shortcuts.edit.title" = "Управлять командами";
"shortcuts.edit.sections.all.header" = "Существующие команды";
"shortcuts.edit.cells.add_shortcut.caption" = "Создать команду";
"about.title" = "О нас";
"about.sections.web.header" = "Веб";
"about.sections.share.header" = "Поделиться";
"about.cells.credits.caption" = "Благодарности";
"about.cells.website.caption" = "Домашняя страница";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Предупреждение";
"about.cells.privacy_policy.caption" = "Политика конфиденциальности";
"about.cells.share_twitter.caption" = "Твитнуть о нас!";
"about.cells.share_generic.caption" = "Пригласить друга";
"donation.title" = "Пожертвовать";
"donation.sections.one_time.header" = "Один раз";
"donation.sections.one_time.footer" = "Если Вы хотите поблагодарить мою бесплатную работу, здесь есть несколько сумм, которые Вы можете пожертвовать прямо сейчас.\n\nСумма будет списана только один раз, а Вы можете пожертвовать несколько раз.";
"donation.cells.loading.caption" = "Загружаем пожертвования";
"donation.cells.purchasing.caption" = "Исполняется";
"donation.alerts.purchase.success.title" = "Спасибо";
"donation.alerts.purchase.success.message" = "Это значит многое для меня, и, я надеюсь, Вы продолжить использовать и рассказывать об этом приложении.";
"donation.alerts.purchase.failure.message" = "Не получается совершить пожертвование. %@";
"share.message" = "Passepartout - это простой в использовании OpenVPN клиент для iOS и macOS, с открытым исходным кодом";
"version.title" = "Версия";
"version.labels.intro" = "Passepartout и TunnelKit написаны и установлены Davide De Rosa (keeshux).\n\nИсходные коды для Passepartout и TunnelKit публично доступны на GitHub под GPLv3, вы можете найти ссылки на домашней странице.\n\nPassepartout является неофициальным клиентом, и никаким образом не связан с OpenVPN Inc.";
"credits.title" = "Благодарность";
"credits.sections.licenses.header" = "Лицензии";
"credits.sections.notices.header" = "Упоминания";
"credits.sections.translations.header" = "Переводы";
"label.license.error" = "Не получается загрузить полную лицензию.";

View File

@ -1,39 +0,0 @@
"0jRWn5" = "Tar bort cellular från betrodda nätverk";
"1ZRTCZ" = "Avstäng VPN";
"66bZBE" = "Med ${providerId} leverantör";
"7eoAss" = "Tar bort nuvarande Wi-Fi från betrodda nätverk";
"9GpJt5" = "Tillsätter cellular till lita betrodda nätverk";
"BKxs8X" = "Tillsätter nuvarande Wi-Fi till betrodda nätverk";
"H4taev" = "Lita på cellular nätverk";
"KjkCfU" = "Koppla till en specifik plats från en leverantör profil";
"LA99yM" = "Koppla till VPN";
"U6o81V" = "Koppla till ${profileId}";
"WnTPFg" = "Koppla till ${poolName}";
"eQ1yzr" = "Avstäng VPN service";
"eXXb2z" = "Koppla till en host profil";
"lQ6ziK" = "Avstäng VPN";
"m2E7SI" = "lita på närvarande Wi-Fi";
"qo3Szz" = "Koppla till provider plats";
"rd1T8p" = "Untrust närvarande Wi-Fi";
"wB1iYX" = "Untrust cellular nätverk";
"xY97Vu" = "På-sätter VPN service med närvarande profil";
"NCoK9B" = "Med profil i andvänding";

View File

@ -1,283 +0,0 @@
//
// Localizable.strings
// Passepartout
//
// Created by Davide De Rosa on 6/13/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
"global.ok" = "OK";
"global.cancel" = "Annullera";
"global.next" = "Nästa";
"global.close" = "Stäng";
"global.host.title_input.message" = "Godtagbara tecken är alfanumeriska plus bindestreck \" - \", understrykning \" _ \"och punkt \". \".";
"global.host.title_input.placeholder" = "Min profil";
"global.email_not_configured" = "Inget e-postkonto är konfigurerat.";
"global.cells.enabled" = "Aktiverad";
"global.cells.disabled" = "Inaktiverad";
"global.cells.none" = "Ingen";
"global.cells.automatic" = "Automatiskt";
"global.cells.manual" = "Manuellt";
"reddit.title" = "Reddit";
"reddit.message" = "Visste du att Passepartout har en subreddit? Prenumerera på uppdateringar eller diskutera problem, funktioner, nya plattformar eller vad du vill. \n\nDet är också ett bra sätt att visa dig bryr dig om detta projekt." ;
"reddit.buttons.subscribe" = "Prenumerera nu!";
"reddit.buttons.remind" = "Påminn mig senare";
"reddit.buttons.never" = "Fråga inte igen";
"organizer.sections.providers.header" = "Providers";
"organizer.sections.providers.footer" = "Här hittar du några leverantörer med förinställda konfigurationsprofiler.";
"organizer.sections.hosts.header" = "Värdar";
"organizer.sections.hosts.footer" = "Importera värdar från .ovpn konfigurationsfiler.";
"organizer.sections.siri.header" = "Siri";
"organizer.sections.siri.footer" = "Få hjälp från Siri för att påskynda dina vanligaste interaktioner med appen.";
"organizer.sections.support.header" = "Support";
"organizer.sections.feedback.header" = "Feedback";
"organizer.cells.profile.value.current" = "Under användning";
"organizer.cells.add_provider.caption" = "Lägg till ny leverantör";
"organizer.cells.add_host.caption" = "Lägg till ny värd";
"organizer.cells.siri_shortcuts.caption" = "Hantera genvägar";
"organizer.cells.join_community.caption" = "Gå med i samhället";
"organizer.cells.write_review.caption" = "Skriv en recension";
"organizer.cells.donate.caption" = "Gör en donation";
"organizer.cells.patreon.caption" = "Stötta mig på Patreon";
"organizer.cells.translate.caption" = "Erbjuda att översätta";
"organizer.cells.about.caption" = "Om %@";
"organizer.cells.uninstall.caption" = "Ta bort VPN-konfiguration";
"organizer.alerts.exhausted_providers.message" = "Du har skapat profiler för alla tillgängliga leverantörer.";
"organizer.alerts.add_host.message" = "Öppna en URL till en .ovpn konfigurationsfil från Safari, Mail eller en annan app för att skapa en värdprofil. \n\nDu kan också importera en .ovpn med iTunes Fildelning." ;
"organizer.alerts.cannot_donate.message" = "Det finns ingen betalningsmetod konfigurerad på den här enheten.";
"organizer.alerts.delete_vpn_profile.message" = "Vill du verkligen radera VPN-konfigurationen från enhetens inställningar? Detta kan fixas med några brutna VPN-stater och påverkar inte dina leverantörs- och värdprofiler.";
"wizards.host.cells.title_input.caption" = "Namn";
"wizards.host.sections.existing.header" = "Befintliga profiler";
"wizards.host.alerts.existing.message" = "En värdprofil med samma namn finns redan. Byt ut det?";
"parsed_file.alerts.malformed.message" = "Konfigurationsfilen innehåller ett felaktigt val (%@).";
"parsed_file.alerts.missing.message" = "Konfigurationsfilen saknar ett obligatoriskt val (%@).";
"parsed_file.alerts.unsupported.message" = "Konfigurationsfilen innehåller ett stöd som inte stöds (%@).";
"parsed_file.alerts.potentially_unsupported.message" = "Konfigurationsfilen är korrekt men innehåller ett eventuellt stöd som inte stöds (%@). \n\nConnectivity kan bryta beroende på serverinställningar.";
"parsed_file.alerts.encryption_passphrase.message" = "Var god ange krypterings lösenordsfrasen.";
"parsed_file.alerts.decryption.message" = "Konfigurationen innehåller en krypterad privat nyckel och den kan inte dekrypteras. Dubbelkontrollera ditt inmatade lösenfras.";
"parsed_file.alerts.parsing.message" = "Det gick inte att analysera den angivna konfigurationsfilen (%@).";
"parsed_file.alerts.buttons.report" = "Rapportera ett problem";
"imported_hosts.title" = "Importerade värdar";
"service.welcome.message" = "Välkommen till Passepartout! \n\nAnvänd arrangören för att lägga till en ny profil.";
"service.sections.general.header" = "Allmän";
"service.sections.vpn.header" = "VPN";
"service.sections.vpn.footer" = "Anslutningen kommer att upprättas vid behov.";
"service.sections.status.header" = "Koppling";
"service.sections.configuration.header" = "Konfiguration";
"service.sections.provider_infrastructure.footer" = "Senast uppdaterad på %@.";
"service.sections.vpn_survives_sleep.footer" = "Inaktivera för att förbättra batterianvändningen, på bekostnad av tillfälliga avmattningar på grund av återuppkoppling.";
"service.sections.vpn_resolves_hostname.footer" = "Föredragna i de flesta nätverk och krävs i vissa IPv6-nätverk. Inaktivera var DNS blockeras eller för att påskynda förhandlingar när DNS är långsamt att svara.";
//"service.sections.vpn_prefers_udp.footer "=" UDP är snabbare än TCP, men fungerar kanske inte i vissa nätverk. Inaktivera i nätverk där UDP kan blockeras. ";
"service.sections.trusted.header" = "Trusted networks";
"service.sections.trusted.footer" = "När du advänder ett betrodat nätverk, VPNet stängs normalt och hålls bortkopplat. Avaktivera detta alternativet för att inte genomdriva sådant beteende.";
"service.sections.diagnostics.header" = "Diagnostics";
"service.sections.diagnostics.footer" = "Masking status kommer att fungera efter återanslutning. Nätverksdata är värdnamn, IP-adresser, routing, SSID. Referenser och privata nycklar loggas inte oavsett.";
//"service.sections.destruction.footer "=" Radera konfiguration från enhetsinställningar. ";
"service.cells.use_profile.caption" = "Använd den här profilen";
"service.cells.vpn_service.caption" = "Aktiverad";
"service.cells.connection_status.caption" = "Status";
"service.cells.reconnect.caption" = "Återanslut";
"service.cells.account.caption" = "Konto";
"service.cells.account.none" = "Ingen konfigurerad";
"service.cells.endpoint.caption" = "Slutpunkt";
"service.cells.provider.pool.caption" = "Plats";
"service.cells.provider.preset.caption" = "Förinställt";
"service.cells.provider.refresh.caption" = "Uppdatera infrastruktur";
"service.cells.host.parameters.caption" = "Parametrar";
"service.cells.network_settings.caption" = "Nätverksinställningar";
"service.cells.vpn_survives_sleep.caption" = "Håll dig levande i sömnen";
"service.cells.vpn_resolves_hostname.caption" = "Lösa server värdnamn";
//"service.cells.vpn_prefers_udp.caption "=" Välj UDP-uttag ";
"service.cells.trusted_mobile.caption" = "Mobilt nätverk";
"service.cells.trusted_add_wifi.caption" = "Lägg till nuvarande Wi-Fi";
"service.cells.trusted_policy.caption" = "Förtroende inaktiverar VPN";
"service.cells.test_connectivity.caption" = "Testanslutning";
"service.cells.data_count.caption" = "Utbyttad data";
"service.cells.data_count.none" = "Otillgänglig";
"service.cells.debug_log.caption" = "Debug log";
"service.cells.masks_private_data.caption" = "Mask nätverksdata";
"service.cells.report_issue.caption" = "Rapportera anslutningsproblem";
"service.alerts.rename.title" = "Byt namn på profil";
"service.alerts.credentials_needed.message" = "Du måste ange kontouppgifterna först.";
"service.alerts.reconnect_vpn.message" = "Vill du återansluta till VPN?";
"service.alerts.trusted.no_network.message" = "Du är inte ansluten till något Wi-Fi-nätverk.";
"service.alerts.trusted.will_disconnect_trusted.message" = "Genom att lita på detta nätverk kan VPN kopplas bort. Fortsätt?";
"service.alerts.trusted.will_disconnect_policy.message" = "Genom att byta tillitspolicy kan VPN kopplas bort. Fortsätt?";
"service.alerts.test_connectivity.title" = "Anslutningar";
"service.alerts.test_connectivity.messages.success" = "Din enhet är ansluten till Internet!";
"service.alerts.test_connectivity.messages.failure" = "Din enhet har ingen Internetanslutning, var god granska dina profilparametrar.";
"service.alerts.masks_private_data.messages.must_reconnect" = "För att säkert återställa den aktuella felsökningsloggen och tillämpa den nya maskeringspreferensen måste du återansluta till VPN nu.";
"service.alerts.buttons.reconnect" = "Reconnect";
"service.alerts.download.title" = "Ladda ner krävs";
"service.alerts.download.message" = "%@ kräver nedladdning av ytterligare konfigurationsfiler. \n\nKontrollera för att starta nedladdningen.";
"service.alerts.download.failed" = "Misslyckades med att ladda ner konfigurationsfiler. %@";
"service.alerts.download.hud.extracting" = "Extraherar filer, var så tålmodig ...";
"account.sections.credentials.header" = "Referenser";
"account.sections.guidance.footer.infrastructure.mullvad" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis numeriskt.";
"account.sections.guidance.footer.infrastructure.nordvpn" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis ditt e-postmeddelande.";
"account.sections.guidance.footer.infrastructure.pia" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis numeriskt med ett prefix för \" p \".";
"account.sections.guidance.footer.infrastructure.protonvpn" = "Hitta dina %@ credentials i avsnittet \" Konto> OpenVPN / IKEv2 Användarnamn \"på webbplatsen.";
"account.sections.guidance.footer.infrastructure.tunnelbear" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis ditt e-postmeddelande.";
"account.sections.guidance.footer.infrastructure.vyprvpn" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis ditt e-postmeddelande.";
"account.sections.guidance.footer.infrastructure.windscribe" = "Hitta din %@ credentials i OpenVPN Config Generator på webbplatsen.";
"account.sections.registration.footer" = "Hämta ett konto på %@ webbplatsen.";
"account.cells.username.caption" = "Användarnamn";
"account.cells.username.placeholder" = "användarnamn";
"account.cells.password.caption" = "Lösenord";
"account.cells.password.placeholder" = "hemlighet";
//"account.cells.password_confirm.caption "=" Bekräfta ";
//"account.cells.password_confirm.mismatch "=" Lösenorden matchar inte! ";
"account.cells.open_guide.caption" = "Visa dina uppgifter";
"account.cells.signup.caption" = "Registrera med %@";
"endpoint.sections.location_addresses.header" = "Adresser";
"endpoint.sections.location_protocols.header" = "Protokoll";
"endpoint.cells.any_address.caption" = "Automatiskt";
"endpoint.cells.any_protocol.caption" = "Automatiskt";
"provider.preset.cells.tech_details.caption" = "Tekniska detaljer";
//"provider.preset.sections.main.footer "=" Tryck på info-knappen för att avslöja tekniska detaljer. ";
"configuration.sections.communication.header" = "Communication";
"configuration.sections.reset.footer" = "Om du slutade med bruten anslutning efter att ha ändrat kommunikationsparametrarna trycker du på för att återgå till den ursprungliga konfigurationen.";
"configuration.sections.tls.header" = "TLS";
"configuration.sections.compression.header" = "Kompression";
"configuration.sections.network.header" = "Nätverk";
"configuration.sections.other.header" = "Other";
"configuration.cells.cipher.caption" = "Cipher";
"configuration.cells.digest.caption" = "Autentisering";
"configuration.cells.digest.value.embedded" = "Inbäddad";
"configuration.cells.reset_original.caption" = "Återställ konfiguration";
"configuration.cells.client.caption" = "Klientcertifikat";
"configuration.cells.client.value.enabled" = "Verified";
"configuration.cells.client.value.disabled" = "Ej verifierad";
"configuration.cells.tls_wrapping.caption" = "Omslagning";
"configuration.cells.tls_wrapping.value.auth" = "Autentisering";
"configuration.cells.tls_wrapping.value.crypt" = "Kryptering";
"configuration.cells.eku.caption" = "Förlängd verifering";
"configuration.cells.default_gateway.caption" = "Normal gateway";
"configuration.cells.dns_server.caption" = "DNS";
"configuration.cells.dns_domain.caption" = "Domain";
"configuration.cells.proxy_http.caption" = "Proxy";
"configuration.cells.proxy_https.caption" = "Proxy (HTTPS)";
"configuration.cells.compression_framing.caption" = "Inramning";
"configuration.cells.compression_framing.value.lzo" = "--comp-lzo";
"configuration.cells.compression_framing.value.compress" = "--compress";
"configuration.cells.compression_algorithm.caption" = "Algoritm";
"configuration.cells.compression_algorithm.value.lzo" = "LZO";
"configuration.cells.compression_algorithm.value.other" = "Utan stöd";
"configuration.cells.keep_alive.caption" = "hål-vid-liv";
"configuration.cells.keep_alive.value.seconds" = "%d seconds";
"configuration.cells.renegotiation_seconds.caption" = "Omförhandling";
"configuration.cells.renegotiation_seconds.value.after" = "efter %@";
"configuration.cells.random_endpoint.caption" = "Omställ slutpunkt pa slumpmässigt sätt";
"network_settings.cells.choice.client" = "Läs .ovpn";
"network_settings.cells.choice.server" = "Dra från server";
"network_settings.cells.address.caption" = "Adress";
"network_settings.cells.port.caption" = "Port";
"network_settings.cells.add_dns_server.caption" = "Lägg till adress";
"network_settings.cells.proxy_bypass.caption" = "Bypass-domän";
"network_settings.cells.add_proxy_bypass.caption" = "Add bypass domain";
"debug_log.buttons.previous" = "Previous";
"debug_log.buttons.next" = "Next";
"debug_log.alerts.empty_log.message" = "Felsökningsloggen är tom.";
"vpn.connecting" = "Anslutning";
"vpn.active" = "Activ";
"vpn.disconnecting" = "Koppla bort";
"vpn.inactive" = "Inaktiv";
"vpn.disabled" = "Inaktiverad";
"vpn.errors.timeout" = "Timeout";
"vpn.errors.dns" = "DNS misslyckades";
"vpn.errors.auth" = "Auth failed";
"vpn.errors.tls" = "TLS misslyckades";
"vpn.errors.encryption" = "Kryptering misslyckades";
"vpn.errors.compression" = "Komprimering utan stöd";
"vpn.errors.network" = "Nätverk ändrat";
"vpn.errors.routing" = "Saknad routing";
"vpn.errors.gateway" = "Ingen gateway";
"issue_reporter.title" = "Rapportera problem";
"issue_reporter.message" = "Felsökningsloggen för dina senaste anslutningar är avgörande för att lösa dina anslutningsproblem och är helt anonym. \n\nHanteringsfilen .ovpn är eventuellt bifogad av någon känslig data. \n\nPresentera dubbelkolla e-postbilagorna om du är osäker. ";
"issue_reporter.buttons.accept" = "Jag förstår";
"translations.title" = "Översättningar";
"shortcuts.add.title" = "Lägg till genväg";
"shortcuts.add.sections.vpn.header" = "VPN";
"shortcuts.add.sections.wifi.header" = "Wi-Fi";
"shortcuts.add.sections.cellular.header" = "Cellular";
"shortcuts.add.cells.connect.caption" = "Anslut till";
"shortcuts.add.cells.enable_vpn.caption" = "Aktivera VPN";
"shortcuts.add.cells.disable_vpn.caption" = "Inaktivera VPN";
"shortcuts.add.cells.trust_current_wifi.caption" = "Lita på nuvarande Wi-Fi";
"shortcuts.add.cells.untrust_current_wifi.caption" = "Avaktivera nuvarande Wi-Fi";
"shortcuts.add.cells.trust_cellular.caption" = "Lita på mobilnätverk";
"shortcuts.add.cells.untrust_cellular.caption" = "Untrust cellular network";
"shortcuts.add.alerts.no_profiles.message" = "Det finns ingen profil att ansluta till.";
"shortcuts.edit.title" = "Hantera genvägar";
"shortcuts.edit.sections.all.header" = "Befintliga genvägar";
"shortcuts.edit.cells.add_shortcut.caption" = "Lägg till genväg";
"about.title" = "About";
"about.sections.web.header" = "Web";
"about.sections.share.header" = "Dela";
"about.cells.credits.caption" = "Credits";
"about.cells.website.caption" = "Hemsida";
"about.cells.faq.caption" = "FAQ";
"about.cells.disclaimer.caption" = "Disclaimer";
"about.cells.privacy_policy.caption" = "Sekretesspolicy";
"about.cells.share_twitter.caption" = "Tweet om det!";
"about.cells.share_generic.caption" = "Bjud in en vän";
"donation.title" = "Donera";
"donation.sections.one_time.header" = "En gång";
"donation.sections.one_time.footer" = "Om du vill visa tacksamhet för mitt fria arbete, här är några belopp du kan donera direkt. \n\nDu betalas endast en gång per donation, och du kan donera flera gånger. ";
"donation.cells.loading.caption" = "Laddar donationer";
"donation.cells.purchasing.caption" = "Performing donation";
"donation.alerts.purchase.success.title" = "Tack";
"donation.alerts.purchase.success.message" = "Detta betyder mycket för mig och jag hoppas verkligen att du fortsätter att använda och marknadsföra denna app.";
"donation.alerts.purchase.failure.message" = "Kan inte göra donationen. %@";
"share.message" = "Passepartout är en användarvänlig öppen källkod OpenVPN-klient för iOS och macOS";
"version.title" = "Version";
"version.labels.intro" = "Passepartout och TunnelKit skrivs och underhålls av Davide De Rosa (keeshux). \n\nKällkod för Passepartout och TunnelKit är offentligt tillgänglig på GitHub under GPLv3, du kan hitta länkar på hemsidan. \n\nPassepartout är en icke-officiell klient och är inte på något sätt ansluten till OpenVPN Inc. ";
"credits.title" = "Credits";
"credits.sections.licenses.header" = "Licenses";
"credits.sections.notices.header" = "Meddelanden";
"credits.sections.translations.header" = "Translations";
"label.license.error" = "Kan inte ladda ner fullständigt licensinnehåll.";

View File

@ -1,344 +0,0 @@
//
// AppConstants.swift
// Passepartout
//
// Created by Davide De Rosa on 9/15/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
import SwiftyBeaver
public class AppConstants {
public class App {
public static let appStoreId: String = {
guard let identifier = GroupConstants.App.config["appstore_id"] as? String else {
fatalError("Missing appstore_id from Info.plist config")
}
return identifier
}()
public static let tunnelBundleId: String = {
guard let identifier = Bundle.main.infoDictionary?[kCFBundleIdentifierKey as String] as? String else {
fatalError("Missing kCFBundleIdentifierKey from Info.plist")
}
return "\(identifier).Tunnel"
}()
}
public class Flags {
public static let isBeta = false
}
public class Domain {
public static let name = "passepartoutvpn.app"
}
public class Store {
public static let serviceFilename = "ConnectionService.json"
public static let webCacheDirectory = "Web"
public static let providersDirectory = "Providers"
public static let hostsDirectory = "Hosts"
}
public class Web {
private static let version = "v2"
private static let baseURL = Repos.api.appendingPathComponent(version)
public static func url(path: String) -> URL {
return baseURL.appendingPathComponent(path)
}
public static let timeout: TimeInterval = 3.0
public static let minimumUpdateInterval: TimeInterval = 600.0 // 10 minutes
private static let connectivityStrings: [String] = [
"https://www.amazon.com",
"https://www.google.com",
"https://www.twitter.com",
"https://www.facebook.com",
"https://www.instagram.com"
]
public static let connectivityURL = URL(string: connectivityStrings.customRandomElement())!
public static let connectivityTimeout: TimeInterval = 10.0
}
public class Log {
public static let level: SwiftyBeaver.Level = .debug
public static let debugFormat = "$DHH:mm:ss$d - $M"
public static var debugSnapshot: () -> String = { TransientStore.shared.service.vpnLog }
public static let viewerRefreshInterval: TimeInterval = 3.0
private static let fileName = "Debug.log"
public static var fileURL: URL {
return GroupConstants.App.cachesURL.appendingPathComponent(fileName)
}
private static let console: ConsoleDestination = {
let dest = ConsoleDestination()
dest.minLevel = level
dest.useNSLog = true
return dest
}()
private static let file: FileDestination = {
let dest = FileDestination()
dest.minLevel = level
dest.logFileURL = fileURL
_ = dest.deleteLogFile()
return dest
}()
public static func configure() {
SwiftyBeaver.addDestination(console)
SwiftyBeaver.addDestination(file)
}
}
public class IssueReporter {
public class Email {
public static let recipient = "issues@\(Domain.name)"
public static let subject = "\(GroupConstants.App.name) - Report issue"
public static func body(_ description: String, _ metadata: String) -> String {
return "Hi,\n\n\(description)\n\n\(metadata)\n\nRegards"
}
public static let template = "description of the issue: "
}
public class Filenames {
public static var debugLog: String {
let fmt = DateFormatter()
fmt.dateFormat = "yyyyMMdd-HHmmss"
let iso = fmt.string(from: Date())
return "debug-\(iso).txt"
}
public static let configuration = "profile.ovpn"
// public static let configuration = "profile.ovpn.txt"
}
public class MIME {
public static let debugLog = "text/plain"
// public static let configuration = "application/x-openvpn-profile"
public static let configuration = "text/plain"
}
}
public class Translations {
public class Email {
public static let recipient = "translate@\(Domain.name)"
public static let subject = "\(GroupConstants.App.name) - Translations"
public static func body(_ description: String) -> String {
return "Hi,\n\n\(description)\n\nRegards"
}
public static let template = "I offer to translate to: "
}
public static let authorByLanguage: [String: String] = [
"de": "Christian Lederer",
"el": "Konstantinos Koukoulakis",
"es": "Davide De Rosa, Elena Vivó",
"fr-FR": "Julien Laniel",
"it": "Davide De Rosa",
"nl": "Norbert de Vreede",
"pt-BR": "Helder Santana",
"ru": "Alexander Korobynikov",
"sv": "Henry Gross-Hellsen"
]
}
public class URLs {
public static let website = URL(string: "https://\(Domain.name)")!
public static let faq = website.appendingPathComponent("faq")
public static let disclaimer = website.appendingPathComponent("disclaimer")
public static let privacyPolicy = website.appendingPathComponent("privacy")
public static let readme = Repos.ios.appendingPathComponent("blob/master/README.md")
public static let changelog = Repos.ios.appendingPathComponent("blob/master/CHANGELOG.md")
public static let subreddit = URL(string: "https://www.reddit.com/r/passepartout")!
public static let patreon = URL(string: "https://www.patreon.com/keeshux")!
private static let twitterHashtags = ["OpenVPN", "iOS", "macOS"]
public static var twitterIntent: URL {
var text = L10n.Share.message
for ht in twitterHashtags {
text = text.replacingOccurrences(of: ht, with: "#\(ht)")
}
var comps = URLComponents(string: "https://twitter.com/intent/tweet")!
comps.queryItems = [
URLQueryItem(name: "url", value: website.absoluteString),
URLQueryItem(name: "via", value: "keeshux"),
URLQueryItem(name: "text", value: text)
]
return comps.url!
}
public static func review(withId id: String) -> URL {
return URL(string: "https://itunes.apple.com/app/id\(id)?action=write-review")!
}
public static let guidances: [Infrastructure.Name: String] = [
.protonVPN: "https://account.protonvpn.com/settings",
.windscribe: "https://windscribe.com/getconfig/openvpn"
]
public static let referrals: [Infrastructure.Name: String] = [
.mullvad: "https://mullvad.net/en/account/create/",
.nordVPN: "https://go.nordvpn.net/SH21Z",
.pia: "https://www.privateinternetaccess.com/pages/buy-vpn/",
.protonVPN: "https://protonvpn.net/?aid=keeshux",
.tunnelBear: "https://click.tunnelbear.com/SHb8",
.vyprVPN: "https://www.vyprvpn.com/",
.windscribe: "https://secure.link/kCsD0prd"
]
public static let externalResources: [Infrastructure.Name: String] = [
.nordVPN: "https://downloads.nordcdn.com/configs/archives/certificates/servers.zip" // 9MB
]
}
public class Repos {
private static let githubRoot = URL(string: "https://github.com/passepartoutvpn/")!
private static let githubRawRoot = URL(string: "https://\(Domain.name)/")!
private static func github(repo: String) -> URL {
return githubRoot.appendingPathComponent(repo)
}
private static func githubRaw(repo: String) -> URL {
return githubRawRoot.appendingPathComponent(repo)
}
public static let ios = github(repo: "passepartout-ios")
public static let api = githubRaw(repo: "api")
}
public struct License {
public let name: String
public let type: String
public let url: URL
public init(_ name: String, _ type: String, _ urlString: String) {
self.name = name
self.type = type
url = URL(string: urlString)!
}
public static let all: [License] = [
License(
"lzo",
"GPLv2",
"https://www.gnu.org/licenses/gpl-2.0.txt"
),
License(
"MBProgressHUD",
"MIT",
"https://raw.githubusercontent.com/jdg/MBProgressHUD/master/LICENSE"
),
License(
"OpenSSL",
"OpenSSL",
"https://www.openssl.org/source/license.txt"
),
License(
"PIATunnel",
"MIT",
"https://raw.githubusercontent.com/pia-foss/tunnel-apple/master/LICENSE"
),
License(
"SSZipArchive",
"MIT",
"https://raw.githubusercontent.com/samsoffes/ssziparchive/master/LICENSE"
),
License(
"SwiftGen",
"MIT",
"https://raw.githubusercontent.com/SwiftGen/SwiftGen/master/LICENCE"
),
License(
"SwiftyBeaver",
"MIT",
"https://raw.githubusercontent.com/SwiftyBeaver/SwiftyBeaver/master/LICENSE"
)
]
public static var cachedContent: [String: String] = [:]
}
public struct Notice {
public let name: String
public let statement: String
public init(_ name: String, _ statement: String) {
self.name = name
self.statement = statement
}
public static let all: [Notice] = [
Notice(
"Circle Icons",
"The logo is taken from the awesome Circle Icons set by Nick Roach."
),
Notice(
"Country flags",
"The country flags are taken from: https://github.com/lipis/flag-icon-css/"
),
Notice(
"OpenVPN",
"© 2002-2018 OpenVPN Inc. - OpenVPN is a registered trademark of OpenVPN Inc."
)
]
}
public struct Rating {
public static let eventCount = 3
}
}

View File

@ -1,38 +0,0 @@
//
// ApplicationError.swift
// Passepartout
//
// Created by Davide De Rosa on 6/12/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public enum ApplicationError: String, Error {
case missingProfile
case missingCredentials
case migration
case inactiveProfile
case externalResources
}

View File

@ -1,87 +0,0 @@
//
// GroupConstants.swift
// Passepartout
//
// Created by Davide De Rosa on 6/7/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public class GroupConstants {
public class App {
public static let name = "Passepartout"
public static let tunnelKitName = "TunnelKit"
public static let title = name
// public static let title = "\u{1F511}"
public static let versionNumber = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
public static let buildNumber = Int(Bundle.main.infoDictionary![kCFBundleVersionKey as String] as! String)!
public static let versionString = "\(versionNumber) (\(buildNumber))"
public static let config: [String: Any] = {
guard let cfg = Bundle.main.infoDictionary?["com.algoritmico.Passepartout.config"] as? [String: Any] else {
fatalError("Missing app config from Info.plist")
}
return cfg
}()
public static let groupId: String = {
guard let identifier = config["group_id"] as? String else {
fatalError("Missing group_id from Info.plist config")
}
return identifier
}()
private static var containerURL: URL {
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupId) else {
print("Unable to access App Group container")
return FileManager.default.userURL(for: .documentDirectory, appending: nil)
}
return url
}
public static let documentsURL: URL = {
let url = containerURL.appendingPathComponent("Documents", isDirectory: true)
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
return url
}()
public static let cachesURL: URL = {
let url = containerURL.appendingPathComponent("Library/Caches", isDirectory: true)
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
return url
}()
public static let externalURL = cachesURL.appendingPathComponent("External")
}
public class VPN {
public static let dnsTimeout = 5000
public static let sessionMarker = "--- EOF ---"
public static let dataCountInterval = 5000
}
}

View File

@ -1,318 +0,0 @@
//
// IntentDispatcher.swift
// Passepartout
//
// Created by Davide De Rosa on 3/8/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import Intents
import SwiftyBeaver
private let log = SwiftyBeaver.self
public extension Notification.Name {
static let IntentDidUpdateService = Notification.Name("IntentDidUpdateService")
}
@available(iOS 12, *)
public class IntentDispatcher {
private class Groups {
static let vpn = "VPN"
static let trust = "Trust"
}
// MARK: Intents
public static func intentConnect(profile: ConnectionProfile) -> ConnectVPNIntent {
let intent = ConnectVPNIntent()
intent.context = profile.context.rawValue
intent.profileId = profile.id
return intent
}
public static func intentMoveTo(profile: ProviderConnectionProfile, pool: Pool) -> MoveToLocationIntent {
let intent = MoveToLocationIntent()
intent.providerId = profile.id
intent.poolId = pool.id
intent.poolName = pool.localizedId
return intent
}
public static func intentEnable() -> EnableVPNIntent {
return EnableVPNIntent()
}
public static func intentDisable() -> DisableVPNIntent {
return DisableVPNIntent()
}
public static func intentTrustWiFi() -> TrustCurrentNetworkIntent {
return TrustCurrentNetworkIntent()
}
public static func intentUntrustWiFi() -> UntrustCurrentNetworkIntent {
return UntrustCurrentNetworkIntent()
}
public static func intentTrustCellular() -> TrustCellularNetworkIntent {
return TrustCellularNetworkIntent()
}
public static func intentUntrustCellular() -> UntrustCellularNetworkIntent {
return UntrustCellularNetworkIntent()
}
// MARK: Donations
public static func donateConnection(with profile: ConnectionProfile) {
let genericIntent: INIntent
if let provider = profile as? ProviderConnectionProfile, let pool = provider.pool {
genericIntent = intentMoveTo(profile: provider, pool: pool)
} else {
genericIntent = intentConnect(profile: profile)
}
let interaction = INInteraction(intent: genericIntent, response: nil)
interaction.groupIdentifier = ProfileKey(profile).rawValue
interaction.donateAndLog()
}
public static func donateEnableVPN() {
let interaction = INInteraction(intent: intentEnable(), response: nil)
interaction.groupIdentifier = Groups.vpn
interaction.donateAndLog()
}
public static func donateDisableVPN() {
let interaction = INInteraction(intent: intentDisable(), response: nil)
interaction.groupIdentifier = Groups.vpn
interaction.donateAndLog()
}
public static func donateTrustCurrentNetwork() {
let interaction = INInteraction(intent: intentTrustWiFi(), response: nil)
interaction.groupIdentifier = Groups.trust
interaction.donateAndLog()
}
public static func donateUntrustCurrentNetwork() {
let interaction = INInteraction(intent: intentUntrustWiFi(), response: nil)
interaction.groupIdentifier = Groups.trust
interaction.donateAndLog()
}
public static func donateTrustCellularNetwork() {
let interaction = INInteraction(intent: intentTrustCellular(), response: nil)
interaction.groupIdentifier = Groups.trust
interaction.donateAndLog()
}
public static func donateUntrustCellularNetwork() {
let interaction = INInteraction(intent: intentUntrustCellular(), response: nil)
interaction.groupIdentifier = Groups.trust
interaction.donateAndLog()
}
//
public static func handleInteraction(_ interaction: INInteraction, completionHandler: ((Error?) -> Void)?) {
handleIntent(interaction.intent, interaction: interaction, completionHandler: completionHandler)
}
public static func handleIntent(_ intent: INIntent, interaction: INInteraction?, completionHandler: ((Error?) -> Void)?) {
if let custom = intent as? ConnectVPNIntent {
handleConnectVPN(custom, interaction: interaction, completionHandler: completionHandler)
} else if let custom = intent as? EnableVPNIntent {
handleEnableVPN(custom, interaction: interaction, completionHandler: completionHandler)
} else if let custom = intent as? DisableVPNIntent {
handleDisableVPN(custom, interaction: interaction, completionHandler: completionHandler)
} else if let custom = intent as? MoveToLocationIntent {
handleMoveToLocation(custom, interaction: interaction, completionHandler: completionHandler)
} else if let _ = intent as? TrustCurrentNetworkIntent {
handleCurrentNetwork(trust: true, interaction: interaction, completionHandler: completionHandler)
} else if let _ = intent as? UntrustCurrentNetworkIntent {
handleCurrentNetwork(trust: false, interaction: interaction, completionHandler: completionHandler)
} else if let _ = intent as? TrustCellularNetworkIntent {
handleCellularNetwork(trust: true, interaction: interaction, completionHandler: completionHandler)
} else if let _ = intent as? UntrustCellularNetworkIntent {
handleCellularNetwork(trust: false, interaction: interaction, completionHandler: completionHandler)
}
}
public static func handleConnectVPN(_ intent: ConnectVPNIntent, interaction: INInteraction?, completionHandler: ((Error?) -> Void)?) {
guard let contextValue = intent.context, let context = Context(rawValue: contextValue), let id = intent.profileId else {
if let interactionIdentifier = interaction?.identifier {
INInteraction.delete(with: [interactionIdentifier], completion: nil)
}
// FIXME: error = missing data, programming error
completionHandler?(nil)
return
}
let profileKey = ProfileKey(context, id)
log.info("Connect to profile: \(profileKey)")
let service = TransientStore.shared.service
let vpn = VPN.shared
guard !(service.isActiveProfile(profileKey) && (vpn.status == .connected)) else {
log.info("Profile is already active and connected")
completionHandler?(nil)
return
}
guard let profile = service.profile(withContext: context, id: id) else {
// FIXME: error = no profile
completionHandler?(nil)
return
}
service.activateProfile(profile)
refreshVPN(service: service, doReconnect: true, completionHandler: completionHandler)
}
public static func handleMoveToLocation(_ intent: MoveToLocationIntent, interaction: INInteraction?, completionHandler: ((Error?) -> Void)?) {
guard let providerId = intent.providerId, let poolId = intent.poolId else {
// FIXME: error = no provider/pool
completionHandler?(nil)
return
}
let service = TransientStore.shared.service
guard let providerProfile = service.profile(withContext: .provider, id: providerId) as? ProviderConnectionProfile else {
// FIXME: error = no provider
completionHandler?(nil)
return
}
log.info("Connect to provider location: \(providerId) @ [\(poolId)]")
let vpn = VPN.shared
guard !(service.isActiveProfile(providerProfile) && (providerProfile.poolId == poolId) && (vpn.status == .connected)) else {
log.info("Profile is already active and connected to \(poolId)")
completionHandler?(nil)
return
}
providerProfile.poolId = poolId
service.activateProfile(providerProfile)
refreshVPN(service: service, doReconnect: true, completionHandler: completionHandler)
}
public static func handleEnableVPN(_ intent: EnableVPNIntent, interaction: INInteraction?, completionHandler: ((Error?) -> Void)?) {
let service = TransientStore.shared.service
log.info("Enabling VPN...")
refreshVPN(service: service, doReconnect: true, completionHandler: completionHandler)
}
public static func handleDisableVPN(_ intent: DisableVPNIntent, interaction: INInteraction?, completionHandler: ((Error?) -> Void)?) {
log.info("Disabling VPN...")
let vpn = VPN.shared
vpn.prepare {
vpn.disconnect { (error) in
notifyServiceUpdate()
completionHandler?(error)
}
}
}
public static func handleCurrentNetwork(trust: Bool, interaction: INInteraction?, completionHandler: ((Error?) -> Void)?) {
guard let currentWifi = Utils.currentWifiNetworkName() else {
// FIXME: error = not connected to wifi
completionHandler?(nil)
return
}
let service = TransientStore.shared.service
service.preferences.trustedWifis[currentWifi] = trust
TransientStore.shared.serialize(withProfiles: false)
log.info("\(trust ? "Trusted" : "Untrusted") Wi-Fi: \(currentWifi)")
refreshVPN(service: service, doReconnect: false, completionHandler: completionHandler)
}
public static func handleCellularNetwork(trust: Bool, interaction: INInteraction?, completionHandler: ((Error?) -> Void)?) {
guard Utils.hasCellularData() else {
// FIXME: error = has no mobile data
completionHandler?(nil)
return
}
let service = TransientStore.shared.service
service.preferences.trustsMobileNetwork = trust
TransientStore.shared.serialize(withProfiles: false)
log.info("\(trust ? "Trusted" : "Untrusted") cellular network")
refreshVPN(service: service, doReconnect: false, completionHandler: completionHandler)
}
private static func refreshVPN(service: ConnectionService, doReconnect: Bool, completionHandler: ((Error?) -> Void)?) {
let configuration: VPNConfiguration
do {
configuration = try service.vpnConfiguration()
} catch let e {
log.error("Unable to build VPN configuration: \(e)")
notifyServiceUpdate()
completionHandler?(e)
return
}
let vpn = VPN.shared
if doReconnect {
log.info("Reconnecting VPN: \(configuration)")
vpn.reconnect(configuration: configuration) { (error) in
notifyServiceUpdate()
completionHandler?(error)
}
} else {
log.info("Reinstalling VPN: \(configuration)")
vpn.install(configuration: configuration) { (error) in
notifyServiceUpdate()
completionHandler?(error)
}
}
}
//
public static func forgetProfile(withKey profileKey: ProfileKey) {
INInteraction.delete(with: profileKey.rawValue) { (error) in
if let error = error {
log.error("Unable to forget interactions: \(error)")
return
}
log.debug("Removed profile \(profileKey) interactions")
}
}
//
private static func notifyServiceUpdate() {
NotificationCenter.default.post(name: .IntentDidUpdateService, object: nil)
}
}
private extension INInteraction {
func donateAndLog() {
donate { (error) in
if let error = error {
log.error("Unable to donate interaction: \(error)")
}
log.debug("Donated \(self.intent)")
}
}
}

View File

@ -1,92 +0,0 @@
//
// ConnectionProfile.swift
// Passepartout
//
// Created by Davide De Rosa on 9/2/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
import NetworkExtension
public enum Context: String, Codable {
case provider
case host
}
public protocol ConnectionProfile: class, EndpointDataSource, CustomStringConvertible {
var context: Context { get }
var id: String { get }
var username: String? { get set }
var requiresCredentials: Bool { get }
var networkChoices: ProfileNetworkChoices? { get set }
var manualNetworkSettings: ProfileNetworkSettings? { get set }
func generate(from configuration: OpenVPNTunnelProvider.Configuration, preferences: Preferences) throws -> OpenVPNTunnelProvider.Configuration
func with(newId: String) -> ConnectionProfile
}
public extension ConnectionProfile {
var passwordKey: String? {
guard let username = username else {
return nil
}
return "\(Bundle.main.bundleIdentifier!).\(context.rawValue).\(id).\(username)"
}
func password(in keychain: Keychain) -> String? {
guard let key = passwordKey else {
return nil
}
return try? keychain.password(for: key)
}
func setPassword(_ password: String?, in keychain: Keychain) throws {
guard let key = passwordKey else {
return
}
guard let password = password else {
keychain.removePassword(for: key)
return
}
try keychain.set(password: password, for: key, label: key)
}
func removePassword(in keychain: Keychain) {
guard let key = passwordKey else {
return
}
keychain.removePassword(for: key)
}
}
public extension ConnectionProfile {
var description: String {
return "(\(context):\(id))"
}
}

View File

@ -1,69 +0,0 @@
//
// ConnectionService+Configurations.swift
// Passepartout
//
// Created by Davide De Rosa on 10/22/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import SwiftyBeaver
private let log = SwiftyBeaver.self
public extension ConnectionService {
func save(configurationURL: URL, for key: ProfileKey) throws -> URL {
let destinationURL = targetConfigurationURL(for: key)
let fm = FileManager.default
try? fm.removeItem(at: destinationURL)
try fm.copyItem(at: configurationURL, to: destinationURL)
return destinationURL
}
func save(configurationURL: URL, for profile: ConnectionProfile) throws -> URL {
return try save(configurationURL: configurationURL, for: ProfileKey(profile))
}
func configurationURL(for key: ProfileKey) -> URL? {
let url = targetConfigurationURL(for: key)
guard FileManager.default.fileExists(atPath: url.path) else {
return nil
}
return url
}
func configurationURL(for profile: ConnectionProfile) -> URL? {
return configurationURL(for: ProfileKey(profile))
}
func targetConfigurationURL(for key: ProfileKey) -> URL {
return contextURL(key).appendingPathComponent(key.id).appendingPathExtension("ovpn")
}
func pendingConfigurationURLs() -> [URL] {
do {
let list = try FileManager.default.contentsOfDirectory(at: rootURL, includingPropertiesForKeys: nil, options: [])
return list.filter { $0.pathExtension == "ovpn" }
} catch let e {
log.error("Could not list imported configurations: \(e)")
return []
}
}
}

View File

@ -1,53 +0,0 @@
//
// ConnectionService+Migration.swift
// Passepartout
//
// Created by Davide De Rosa on 10/25/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import SwiftyBeaver
private let log = SwiftyBeaver.self
public extension ConnectionService {
static func migrateJSON(from: URL, to: URL) {
do {
let newData = try migrateJSON(at: from)
// log.verbose(String(data: newData, encoding: .utf8)!)
try newData.write(to: to)
} catch let e {
log.error("Could not migrate service: \(e)")
}
}
static func migrateJSON(at url: URL) throws -> Data {
let data = try Data(contentsOf: url)
guard var json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
throw ApplicationError.migration
}
// put migration logic here
let _ = json["build"] as? Int ?? 0
return try JSONSerialization.data(withJSONObject: json, options: [])
}
}

View File

@ -1,616 +0,0 @@
//
// ConnectionService.swift
// Passepartout
//
// Created by Davide De Rosa on 9/3/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
import NetworkExtension
import SwiftyBeaver
private let log = SwiftyBeaver.self
public protocol ConnectionServiceDelegate: class {
func connectionService(didAdd profile: ConnectionProfile)
func connectionService(didRename oldProfile: ConnectionProfile, to newProfile: ConnectionProfile)
func connectionService(didRemoveProfileWithKey key: ProfileKey)
func connectionService(willDeactivate profile: ConnectionProfile)
func connectionService(didActivate profile: ConnectionProfile)
}
public extension Notification.Name {
static let ConnectionServiceDidUpdateDataCount = Notification.Name("ConnectionServiceDidUpdateDataCount")
}
public class ConnectionService: Codable {
public enum CodingKeys: String, CodingKey {
case build
case appGroup
case baseConfiguration
case activeProfileKey
case preferences
}
public struct NotificationKeys {
public static let dataCount = "DataCount"
}
public var directory: String? = nil
public var rootURL: URL {
var url = GroupConstants.App.documentsURL
if let directory = directory {
url.appendPathComponent(directory)
}
return url
}
private var providersURL: URL {
return rootURL.appendingPathComponent(AppConstants.Store.providersDirectory)
}
private var hostsURL: URL {
return rootURL.appendingPathComponent(AppConstants.Store.hostsDirectory)
}
private var build: Int
private let appGroup: String
private let defaults: UserDefaults
private let keychain: Keychain
public var baseConfiguration: OpenVPNTunnelProvider.Configuration
private var cache: [ProfileKey: ConnectionProfile]
private var dataCountObserver: Timer?
public private(set) var activeProfileKey: ProfileKey? {
willSet {
if let oldProfile = activeProfile {
delegate?.connectionService(willDeactivate: oldProfile)
}
}
didSet {
if let newProfile = activeProfile {
delegate?.connectionService(didActivate: newProfile)
}
}
}
public var activeProfile: ConnectionProfile? {
guard let id = activeProfileKey else {
return nil
}
var hit = cache[id]
if let placeholder = hit as? PlaceholderConnectionProfile {
hit = profile(withContext: placeholder.context, id: placeholder.id)
cache[id] = hit
}
return hit
}
public let preferences: EditablePreferences
public weak var delegate: ConnectionServiceDelegate?
public init(withAppGroup appGroup: String, baseConfiguration: OpenVPNTunnelProvider.Configuration) {
guard let defaults = UserDefaults(suiteName: appGroup) else {
fatalError("No entitlements for group '\(appGroup)'")
}
build = GroupConstants.App.buildNumber
self.appGroup = appGroup
self.defaults = defaults
keychain = Keychain(group: appGroup)
self.baseConfiguration = baseConfiguration
activeProfileKey = nil
preferences = EditablePreferences()
cache = [:]
}
deinit {
dataCountObserver?.invalidate()
}
// MARK: Codable
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let appGroup = try container.decode(String.self, forKey: .appGroup)
guard let defaults = UserDefaults(suiteName: appGroup) else {
fatalError("No entitlements for group '\(appGroup)'")
}
build = try container.decode(Int.self, forKey: .build)
self.appGroup = appGroup
self.defaults = defaults
keychain = Keychain(group: appGroup)
baseConfiguration = try container.decode(OpenVPNTunnelProvider.Configuration.self, forKey: .baseConfiguration)
activeProfileKey = try container.decodeIfPresent(ProfileKey.self, forKey: .activeProfileKey)
preferences = try container.decode(EditablePreferences.self, forKey: .preferences)
cache = [:]
}
public func encode(to encoder: Encoder) throws {
build = GroupConstants.App.buildNumber
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(build, forKey: .build)
try container.encode(appGroup, forKey: .appGroup)
try container.encode(baseConfiguration, forKey: .baseConfiguration)
try container.encodeIfPresent(activeProfileKey, forKey: .activeProfileKey)
try container.encode(preferences, forKey: .preferences)
}
// MARK: Serialization
public func loadProfiles() {
let fm = FileManager.default
try? fm.createDirectory(at: providersURL, withIntermediateDirectories: false, attributes: nil)
try? fm.createDirectory(at: hostsURL, withIntermediateDirectories: false, attributes: nil)
do {
let files = try fm.contentsOfDirectory(at: providersURL, includingPropertiesForKeys: nil, options: [])
// log.debug("Found \(files.count) provider files: \(files)")
for entry in files {
guard let id = ConnectionService.profileId(fromURL: entry) else {
continue
}
let key = ProfileKey(.provider, id)
cache[key] = PlaceholderConnectionProfile(key)
}
} catch let e {
log.warning("Could not list provider contents: \(e) (\(providersURL))")
}
do {
let files = try fm.contentsOfDirectory(at: hostsURL, includingPropertiesForKeys: nil, options: [])
// log.debug("Found \(files.count) host files: \(files)")
for entry in files {
guard let id = ConnectionService.profileId(fromURL: entry) else {
continue
}
let key = ProfileKey(.host, id)
cache[key] = PlaceholderConnectionProfile(key)
}
} catch let e {
log.warning("Could not list host contents: \(e) (\(hostsURL))")
}
}
public func saveProfiles() {
let encoder = JSONEncoder()
ensureDirectoriesExistence()
for profile in cache.values {
saveProfile(profile, withEncoder: encoder, checkDirectories: false)
}
}
private func ensureDirectoriesExistence() {
let fm = FileManager.default
try? fm.createDirectory(at: providersURL, withIntermediateDirectories: false, attributes: nil)
try? fm.createDirectory(at: hostsURL, withIntermediateDirectories: false, attributes: nil)
}
private func saveProfile(_ profile: ConnectionProfile, withEncoder encoder: JSONEncoder, checkDirectories: Bool) {
if checkDirectories {
ensureDirectoriesExistence()
}
do {
let url = profileURL(ProfileKey(profile))
var optData: Data?
if let providerProfile = profile as? ProviderConnectionProfile {
optData = try encoder.encode(providerProfile)
} else if let hostProfile = profile as? HostConnectionProfile {
optData = try encoder.encode(hostProfile)
} else if let placeholder = profile as? PlaceholderConnectionProfile {
log.debug("Skipped placeholder \(placeholder)")
} else {
fatalError("Attempting to add an unhandled profile type: \(type(of: profile))")
}
guard let data = optData else {
return
}
try data.write(to: url)
log.debug("Serialized profile \(profile)")
} catch let e {
log.warning("Could not serialize profile \(profile): \(e)")
}
}
public func profile(withContext context: Context, id: String) -> ConnectionProfile? {
return profile(withKey: ProfileKey(context, id))
}
public func profile(withKey key: ProfileKey) -> ConnectionProfile? {
var profile = cache[key]
if let _ = profile as? PlaceholderConnectionProfile {
let decoder = JSONDecoder()
do {
let data = try profileData(key)
switch key.context {
case .provider:
let providerProfile = try decoder.decode(ProviderConnectionProfile.self, from: data)
// XXX: fix renamed presets, fall back to default
if providerProfile.preset == nil {
providerProfile.presetId = providerProfile.infrastructure.defaults.preset
}
// XXX: fix renamed pool, fall back to default
if providerProfile.pool == nil, let fallbackPool = providerProfile.infrastructure.defaultPool() {
providerProfile.poolId = fallbackPool.id
}
// XXX: fix unsupported preset
providerProfile.setSupportedPreset()
profile = providerProfile
case .host:
// let hostProfile = try decoder.decode(HostConnectionProfile.self, from: data)
//
// profile = hostProfile
break
}
cache[key] = profile
} catch let e {
log.error("Could not decode profile JSON: \(e)")
return nil
}
}
return profile
}
public func ids(forContext context: Context) -> [String] {
return cache.keys.filter { $0.context == context }.map { $0.id }
}
public func contextURL(_ key: ProfileKey) -> URL {
switch key.context {
case .provider:
return providersURL
case .host:
return hostsURL
}
}
public func profileURL(_ key: ProfileKey) -> URL {
return contextURL(key).appendingPathComponent(key.id).appendingPathExtension("json")
}
public func profileData(_ key: ProfileKey) throws -> Data {
return try Data(contentsOf: profileURL(key))
}
private static func profileId(fromURL url: URL) -> String? {
guard url.pathExtension == "json" else {
return nil
}
return url.deletingPathExtension().lastPathComponent
}
func reloadHostProfilesFromConfigurationFiles() -> Bool {
var anyReloaded = false
for entry in cache {
guard entry.value.context == .host else {
continue
}
guard let host = profile(withKey: entry.key) as? HostConnectionProfile else {
log.warning("Host context but not a HostConnectionProfile?")
continue
}
guard let url = configurationURL(for: entry.key) else {
continue
}
// can fail due to passphrase (migration is non-interactive)
if let result = try? OpenVPN.ConfigurationParser.parsed(fromURL: url) {
host.parameters = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: result.configuration).build()
} else {
// fall back to the safer option
var builder = host.parameters.builder()
var sessionBuilder = builder.sessionConfiguration.builder()
sessionBuilder.routingPolicies = [.IPv4]
builder.sessionConfiguration = sessionBuilder.build()
host.parameters = builder.build()
}
cache[entry.key] = host
anyReloaded = true
}
return anyReloaded
}
// MARK: Profiles
public func hasProfiles() -> Bool {
return !cache.isEmpty
}
public func addProfile(_ profile: ConnectionProfile, credentials: Credentials?) -> Bool {
guard cache.index(forKey: ProfileKey(profile)) == nil else {
return false
}
addOrReplaceProfile(profile, credentials: credentials)
return true
}
public func addOrReplaceProfile(_ profile: ConnectionProfile, credentials: Credentials?) {
let key = ProfileKey(profile)
cache[key] = profile
try? setCredentials(credentials, for: profile)
if cache.count == 1 {
activeProfileKey = key
}
delegate?.connectionService(didAdd: profile)
// serialization (can fail)
saveProfile(profile, withEncoder: JSONEncoder(), checkDirectories: true)
}
@discardableResult
public func renameProfile(_ key: ProfileKey, to newId: String) -> ConnectionProfile? {
precondition(newId != key.id)
// WARNING: can be a placeholder
guard let oldProfile = cache[key] else {
return nil
}
let fm = FileManager.default
let temporaryDelegate = delegate
delegate = nil
// 1. add renamed profile
let newProfile = oldProfile.with(newId: newId)
let newKey = ProfileKey(newProfile)
let sameCredentials = credentials(for: oldProfile)
addOrReplaceProfile(newProfile, credentials: sameCredentials)
// 2. rename .ovpn (if present)
if let cfgFrom = configurationURL(for: key) {
let cfgTo = targetConfigurationURL(for: newKey)
try? fm.removeItem(at: cfgTo)
try? fm.moveItem(at: cfgFrom, to: cfgTo)
}
// 3. remove old entry
removeProfile(key)
// 4. replace active key (if active)
if key == activeProfileKey {
activeProfileKey = newKey
}
delegate = temporaryDelegate
delegate?.connectionService(didRename: oldProfile, to: newProfile)
return newProfile
}
@discardableResult
public func renameProfile(_ profile: ConnectionProfile, to id: String) -> ConnectionProfile? {
return renameProfile(ProfileKey(profile), to: id)
}
public func removeProfile(_ key: ProfileKey) {
guard let profile = cache[key] else {
return
}
cache.removeValue(forKey: key)
removeCredentials(for: profile)
if cache.isEmpty {
activeProfileKey = nil
}
delegate?.connectionService(didRemoveProfileWithKey: key)
// serialization (can fail)
do {
let fm = FileManager.default
if let cfg = configurationURL(for: key) {
try? fm.removeItem(at: cfg)
}
let url = profileURL(key)
try fm.removeItem(at: url)
log.debug("Deleted removed profile '\(profile.id)'")
} catch let e {
log.warning("Could not delete profile '\(profile.id)': \(e)")
}
}
public func containsProfile(_ key: ProfileKey) -> Bool {
return cache.index(forKey: key) != nil
}
public func containsProfile(_ profile: ConnectionProfile) -> Bool {
return containsProfile(ProfileKey(profile))
}
public func hasActiveProfile() -> Bool {
return activeProfileKey != nil
}
public func isActiveProfile(_ key: ProfileKey) -> Bool {
return key == activeProfileKey
}
public func isActiveProfile(_ profile: ConnectionProfile) -> Bool {
return isActiveProfile(ProfileKey(profile))
}
public func activateProfile(_ profile: ConnectionProfile) {
activeProfileKey = ProfileKey(profile)
}
// MARK: Credentials
public func needsCredentials(for profile: ConnectionProfile) -> Bool {
guard profile.requiresCredentials else {
return false
}
guard let creds = credentials(for: profile) else {
return true
}
return creds.isEmpty
}
public func credentials(for profile: ConnectionProfile) -> Credentials? {
guard let username = profile.username, let key = profile.passwordKey else {
return nil
}
guard let password = try? keychain.password(for: key) else {
return nil
}
return Credentials(username, password)
}
public func setCredentials(_ credentials: Credentials?, for profile: ConnectionProfile) throws {
profile.username = credentials?.username
try profile.setPassword(credentials?.password, in: keychain)
}
public func removeCredentials(for profile: ConnectionProfile) {
profile.removePassword(in: keychain)
}
// MARK: VPN
public func vpnConfiguration() throws -> NetworkExtensionVPNConfiguration {
guard let profile = activeProfile else {
throw ApplicationError.missingProfile
}
let creds = credentials(for: profile)
if profile.requiresCredentials {
guard creds != nil else {
throw ApplicationError.missingCredentials
}
}
var cfg = try profile.generate(from: baseConfiguration, preferences: preferences)
// override network settings
if let choices = profile.networkChoices, let settings = profile.manualNetworkSettings {
var builder = cfg.builder()
var sessionBuilder = builder.sessionConfiguration.builder()
sessionBuilder.applyGateway(from: choices, settings: settings)
sessionBuilder.applyDNS(from: choices, settings: settings)
sessionBuilder.applyProxy(from: choices, settings: settings)
builder.sessionConfiguration = sessionBuilder.build()
cfg = builder.build()
}
let protocolConfiguration = try cfg.generatedTunnelProtocol(
withBundleIdentifier: AppConstants.App.tunnelBundleId,
appGroup: appGroup,
credentials: creds
)
protocolConfiguration.disconnectOnSleep = preferences.disconnectsOnSleep
log.verbose("Configuration:")
log.verbose(protocolConfiguration)
var rules: [NEOnDemandRule] = []
#if os(iOS)
if preferences.trustsMobileNetwork {
let rule = policyRule()
rule.interfaceTypeMatch = .cellular
rules.append(rule)
}
#endif
let reallyTrustedWifis = Array(preferences.trustedWifis.filter { $1 }.keys)
if !reallyTrustedWifis.isEmpty {
let rule = policyRule()
rule.interfaceTypeMatch = .wiFi
rule.ssidMatch = reallyTrustedWifis
rules.append(rule)
}
let connection = NEOnDemandRuleConnect()
connection.interfaceTypeMatch = .any
rules.append(connection)
return NetworkExtensionVPNConfiguration(protocolConfiguration: protocolConfiguration, onDemandRules: rules)
}
private func policyRule() -> NEOnDemandRule {
switch preferences.trustPolicy {
case .ignore:
return NEOnDemandRuleIgnore()
case .disconnect:
return NEOnDemandRuleDisconnect()
}
}
public var vpnLog: String {
return baseConfiguration.existingLog(in: appGroup) ?? ""
}
public func eraseVpnLog() {
log.info("Erasing VPN log...")
guard let url = baseConfiguration.urlForLog(in: appGroup) else {
return
}
try? FileManager.default.removeItem(at: url)
}
public var vpnLastError: OpenVPNTunnelProvider.ProviderError? {
return baseConfiguration.lastError(in: appGroup)
}
public func clearVpnLastError() {
baseConfiguration.clearLastError(in: appGroup)
}
public func observeVPNDataCount(interval: TimeInterval) {
dataCountObserver?.invalidate()
dataCountObserver = Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: { [weak self] (_) in
guard let dataCount = self?.vpnDataCount else {
return
}
NotificationCenter.default.post(name: .ConnectionServiceDidUpdateDataCount, object: nil, userInfo: [NotificationKeys.dataCount: dataCount])
})
}
public var vpnDataCount: (Int, Int)? {
return baseConfiguration.dataCount(in: appGroup)
}
}

View File

@ -1,41 +0,0 @@
//
// Credentials.swift
// Passepartout
//
// Created by Davide De Rosa on 6/7/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public typealias Credentials = OpenVPN.Credentials
public extension Credentials {
var isEmpty: Bool {
return username.isEmpty || password.isEmpty
}
func trimmed() -> Credentials {
let trimmedUsername = username.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedPassword = password.trimmingCharacters(in: .whitespacesAndNewlines)
return Credentials(trimmedUsername, trimmedPassword)
}
}

View File

@ -1,93 +0,0 @@
//
// DataUnit.swift
// Passepartout
//
// Created by Davide De Rosa on 3/30/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public enum DataUnit: Int, CustomStringConvertible {
case byte = 1
case kilobyte = 1024
case megabyte = 1048576
case gigabyte = 1073741824
fileprivate var showsDecimals: Bool {
switch self {
case .byte, .kilobyte:
return false
case .megabyte, .gigabyte:
return true
}
}
fileprivate var boundary: Int {
return Int(0.1 * Double(rawValue))
}
// MARK: CustomStringConvertible
public var description: String {
switch self {
case .byte:
return "B"
case .kilobyte:
return "kB"
case .megabyte:
return "MB"
case .gigabyte:
return "GB"
}
}
}
public extension Int {
private static let allUnits: [DataUnit] = [
.gigabyte,
.megabyte,
.kilobyte,
.byte
]
var dataUnitDescription: String {
if self == 0 {
return "0B"
}
for u in Int.allUnits {
if self >= u.boundary {
if !u.showsDecimals {
return "\(self / u.rawValue)\(u)"
}
let count = Double(self) / Double(u.rawValue)
return String(format: "%.2f%@", count, u.description)
}
}
fatalError("Number is negative")
}
}

View File

@ -1,84 +0,0 @@
//
// DebugLog.swift
// Passepartout
//
// Created by Davide De Rosa on 6/26/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
#if os(iOS)
import UIKit
#else
import Cocoa
#endif
public struct DebugLog {
private let raw: String
public init(raw: String) {
self.raw = raw
}
public func string() -> String {
return raw
}
public func data() -> Data? {
return raw.data(using: .utf8)
}
public func decoratedString() -> String {
let appName = GroupConstants.App.name
let appVersion = GroupConstants.App.versionString
var metadata: [String] = []
let osVersion: String
let deviceType: String?
#if os(iOS)
let device = UIDevice.current
osVersion = "\(device.systemName) \(device.systemVersion)"
deviceType = device.model
#else
let os = ProcessInfo().operatingSystemVersion
osVersion = "macOS \(os.majorVersion).\(os.minorVersion).\(os.patchVersion)"
deviceType = nil
#endif
metadata.append("App: \(appName) \(appVersion)")
metadata.append("OS: \(osVersion)")
if let deviceType = deviceType {
metadata.append("Device: \(deviceType)")
}
var fullText = metadata.joined(separator: "\n")
fullText += "\n\n"
fullText += raw
return fullText
}
public func decoratedData() -> Data {
guard let data = decoratedString().data(using: .utf8) else {
fatalError("Could not encode log metadata to UTF8?")
}
return data
}
}

View File

@ -1,41 +0,0 @@
//
// EndpointDataSource.swift
// Passepartout
//
// Created by Davide De Rosa on 9/5/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public protocol EndpointDataSource {
var mainAddress: String? { get }
var addresses: [String] { get }
var protocols: [EndpointProtocol] { get }
var canCustomizeEndpoint: Bool { get }
var customAddress: String? { get }
var customProtocol: EndpointProtocol? { get }
}

View File

@ -1,54 +0,0 @@
//
// Preferences.swift
// Passepartout
//
// Created by Davide De Rosa on 9/4/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public protocol Preferences {
var resolvesHostname: Bool { get }
var disconnectsOnSleep: Bool { get }
#if os(iOS)
var trustsMobileNetwork: Bool { get }
#endif
var trustedWifis: [String: Bool] { get }
var trustPolicy: TrustPolicy { get }
}
public class EditablePreferences: Preferences, Codable {
public var resolvesHostname: Bool = true
public var disconnectsOnSleep: Bool = false
#if os(iOS)
public var trustsMobileNetwork: Bool = false
#endif
public var trustedWifis: [String: Bool] = [:]
public var trustPolicy: TrustPolicy = .disconnect
}

View File

@ -1,168 +0,0 @@
//
// ProfileNetworkSettings.swift
// Passepartout
//
// Created by Davide De Rosa on 04/28/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public enum NetworkChoice: String, Codable {
case client
case server // erase client settings
case manual
}
public class ProfileNetworkChoices: Codable {
public var gateway: NetworkChoice
public var dns: NetworkChoice
public var proxy: NetworkChoice
public init(choice: NetworkChoice) {
gateway = choice
dns = choice
proxy = choice
}
}
public class ProfileNetworkSettings: Codable, CustomStringConvertible {
public var gatewayPolicies: [OpenVPN.RoutingPolicy]?
public var dnsServers: [String]?
public var dnsDomainName: String?
public var proxyAddress: String?
public var proxyPort: UInt16?
public var proxyServer: Proxy? {
guard let address = proxyAddress, let port = proxyPort, !address.isEmpty, port > 0 else {
return nil
}
return Proxy(address, port)
}
public var proxyBypassDomains: [String]?
public init() {
gatewayPolicies = [.IPv4, .IPv6]
}
public init(from configuration: OpenVPN.Configuration) {
gatewayPolicies = configuration.routingPolicies
dnsDomainName = configuration.searchDomain
dnsServers = configuration.dnsServers
proxyAddress = configuration.httpProxy?.address
proxyPort = configuration.httpProxy?.port
proxyBypassDomains = configuration.proxyBypassDomains
}
public func copy(from settings: ProfileNetworkSettings) {
copyGateway(from: settings)
copyDNS(from: settings)
copyProxy(from: settings)
}
public func copyGateway(from settings: ProfileNetworkSettings) {
gatewayPolicies = settings.gatewayPolicies
}
public func copyDNS(from settings: ProfileNetworkSettings) {
dnsDomainName = settings.dnsDomainName
dnsServers = settings.dnsServers?.filter { !$0.isEmpty }
}
public func copyProxy(from settings: ProfileNetworkSettings) {
proxyAddress = settings.proxyAddress
proxyPort = settings.proxyPort
proxyBypassDomains = settings.proxyBypassDomains?.filter { !$0.isEmpty }
}
// MARK: CustomStringConvertible
public var description: String {
let comps: [String] = [
"gw: \(gatewayPolicies?.description ?? "")",
"dns: {domain: \(dnsDomainName ?? ""), servers: \(dnsServers?.description ?? "[]")}",
"proxy: {address: \(proxyAddress ?? ""), port: \(proxyPort?.description ?? ""), bypass: \(proxyBypassDomains?.description ?? "[]")}"
]
return "{\(comps.joined(separator: ", "))}"
}
}
extension OpenVPN.ConfigurationBuilder {
public mutating func applyGateway(from choices: ProfileNetworkChoices, settings: ProfileNetworkSettings) {
switch choices.gateway {
case .client:
break
case .server:
routingPolicies = nil
case .manual:
routingPolicies = settings.gatewayPolicies
}
}
public mutating func applyDNS(from choices: ProfileNetworkChoices, settings: ProfileNetworkSettings) {
switch choices.dns {
case .client:
break
case .server:
dnsServers = nil
searchDomain = nil
case .manual:
dnsServers = settings.dnsServers?.filter { !$0.isEmpty }
searchDomain = settings.dnsDomainName
}
}
public mutating func applyProxy(from choices: ProfileNetworkChoices, settings: ProfileNetworkSettings) {
switch choices.proxy {
case .client:
break
case .server:
httpProxy = nil
httpsProxy = nil
proxyBypassDomains = nil
case .manual:
if let proxyServer = settings.proxyServer {
httpProxy = proxyServer
httpsProxy = proxyServer
proxyBypassDomains = settings.proxyBypassDomains?.filter { !$0.isEmpty }
} else {
httpProxy = nil
httpsProxy = nil
proxyBypassDomains = nil
}
}
}
}

View File

@ -1,120 +0,0 @@
//
// HostConnectionProfile.m
// Passepartout
//
// Created by Davide De Rosa on 9/2/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public class HostConnectionProfile: ConnectionProfile, Codable, Equatable {
public var title: String
public let hostname: String
public var parameters: OpenVPNTunnelProvider.Configuration
public init(title: String, hostname: String) {
self.title = title
self.hostname = hostname
let sessionConfiguration = OpenVPN.ConfigurationBuilder().build()
parameters = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionConfiguration).build()
}
// MARK: ConnectionProfile
public let context: Context = .host
public var id: String {
return title
}
public var username: String?
public var requiresCredentials: Bool {
return false
}
public var networkChoices: ProfileNetworkChoices?
public var manualNetworkSettings: ProfileNetworkSettings?
public func generate(from configuration: OpenVPNTunnelProvider.Configuration, preferences: Preferences) throws -> OpenVPNTunnelProvider.Configuration {
guard let endpointProtocols = parameters.sessionConfiguration.endpointProtocols, !endpointProtocols.isEmpty else {
preconditionFailure("No endpointProtocols")
}
// XXX: copy paste, error prone
var builder = parameters.builder()
builder.mtu = configuration.mtu
builder.shouldDebug = configuration.shouldDebug
builder.debugLogFormat = configuration.debugLogFormat
builder.masksPrivateData = configuration.masksPrivateData
// forcibly override hostname with profile hostname (never nil)
var sessionBuilder = builder.sessionConfiguration.builder()
sessionBuilder.hostname = hostname
sessionBuilder.tlsSecurityLevel = 0 // lowest, tolerate widest range of certificates
builder.sessionConfiguration = sessionBuilder.build()
return builder.build()
}
public func with(newId: String) -> ConnectionProfile {
let profile = HostConnectionProfile(title: newId, hostname: hostname)
profile.username = username
profile.parameters = parameters
return profile
}
}
public extension HostConnectionProfile {
static func ==(lhs: HostConnectionProfile, rhs: HostConnectionProfile) -> Bool {
return lhs.id == rhs.id
}
}
public extension HostConnectionProfile {
var mainAddress: String? {
return hostname
}
var addresses: [String] {
return [hostname]
}
var protocols: [EndpointProtocol] {
return parameters.sessionConfiguration.endpointProtocols ?? []
}
var canCustomizeEndpoint: Bool {
return false
}
var customAddress: String? {
return nil
}
var customProtocol: EndpointProtocol? {
return nil
}
}

View File

@ -1,71 +0,0 @@
//
// PlaceholderConnectionProfile.swift
// Passepartout
//
// Created by Davide De Rosa on 11/6/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public class PlaceholderConnectionProfile: ConnectionProfile {
public let context: Context
public let id: String
public var username: String? = nil
public var requiresCredentials: Bool = false
public var networkChoices: ProfileNetworkChoices?
public var manualNetworkSettings: ProfileNetworkSettings?
public func generate(from configuration: OpenVPNTunnelProvider.Configuration, preferences: Preferences) throws -> OpenVPNTunnelProvider.Configuration {
fatalError("Generating configuration from a PlaceholderConnectionProfile")
}
public func with(newId: String) -> ConnectionProfile {
return PlaceholderConnectionProfile(context, newId)
}
public var mainAddress: String? = nil
public var addresses: [String] = []
public var protocols: [EndpointProtocol] = []
public var canCustomizeEndpoint: Bool = false
public var customAddress: String?
public var customProtocol: EndpointProtocol?
public init(_ context: Context, _ id: String) {
self.context = context
self.id = id
}
public init(_ key: ProfileKey) {
context = key.context
id = key.id
}
}

View File

@ -1,34 +0,0 @@
//
// PoolCategory.swift
// Passepartout
//
// Created by Davide De Rosa on 4/11/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public struct PoolCategory: Codable {
public let name: String
public let groups: [PoolGroup]
public let presets: [String]?
}

View File

@ -1,72 +0,0 @@
//
// ProfileKey.swift
// Passepartout
//
// Created by Davide De Rosa on 11/6/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public struct ProfileKey: RawRepresentable, Hashable, Codable, CustomStringConvertible {
private static let separator: Character = "."
public let context: Context
public let id: String
public init(_ context: Context, _ id: String) {
self.context = context
self.id = id
}
public init(_ profile: ConnectionProfile) {
context = profile.context
id = profile.id
}
// MARK: RawRepresentable
public var rawValue: String {
return "\(context)\(ProfileKey.separator)\(id)"
}
public init?(rawValue: String) {
guard let separatorIndex = rawValue.firstIndex(of: ProfileKey.separator) else {
return nil
}
let contextValue = rawValue[rawValue.startIndex..<separatorIndex]
guard let context = Context(rawValue: String(contextValue)) else {
return nil
}
self.context = context
let idStart = rawValue.index(after: separatorIndex)
let idEnd = rawValue.endIndex
id = String(rawValue[idStart..<idEnd])
}
// MARK: CustomStringConvertible
public var description: String {
return "{context=\(context), id=\(id)}"
}
}

View File

@ -1,217 +0,0 @@
//
// ProviderConnectionProfile.swift
// Passepartout
//
// Created by Davide De Rosa on 9/2/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
public let name: Infrastructure.Name
public var infrastructure: Infrastructure {
return InfrastructureFactory.shared.get(name)
}
public var poolId: String {
didSet {
validateEndpoint()
}
}
public var pool: Pool? {
return infrastructure.pool(for: poolId)
}
public var presetId: String {
didSet {
validateEndpoint()
}
}
public var preset: InfrastructurePreset? {
return infrastructure.preset(for: presetId)
}
public var manualAddress: String?
public var manualProtocol: EndpointProtocol?
public var networkChoices: ProfileNetworkChoices?
public var manualNetworkSettings: ProfileNetworkSettings?
public var usesProviderEndpoint: Bool {
return (manualAddress != nil) || (manualProtocol != nil)
}
public init(name: Infrastructure.Name) {
self.name = name
poolId = ""
presetId = ""
username = nil
poolId = infrastructure.defaultPool()?.id ?? infrastructure.defaults.pool
presetId = infrastructure.defaults.preset
}
public func setSupportedPreset() {
guard let pool = pool else {
return
}
let supported = pool.supportedPresetIds(in: infrastructure)
if let current = preset?.id, !supported.contains(current), let fallback = supported.first {
presetId = fallback
}
}
private func validateEndpoint() {
guard let pool = pool, let preset = preset else {
manualAddress = nil
manualProtocol = nil
return
}
if let address = manualAddress, !pool.hasAddress(address) {
manualAddress = nil
}
if let proto = manualProtocol, !preset.hasProtocol(proto) {
manualProtocol = nil
}
}
// MARK: ConnectionProfile
public let context: Context = .provider
public var id: String {
return name.rawValue
}
public var username: String?
public var requiresCredentials: Bool {
return true
}
public func generate(from configuration: OpenVPNTunnelProvider.Configuration, preferences: Preferences) throws -> OpenVPNTunnelProvider.Configuration {
guard let pool = pool else {
preconditionFailure("Nil pool?")
}
guard let preset = preset else {
preconditionFailure("Nil preset?")
}
// assert(!pool.numericAddresses.isEmpty)
// XXX: copy paste, error prone
var builder = preset.configuration.builder()
builder.mtu = configuration.mtu
builder.shouldDebug = configuration.shouldDebug
builder.debugLogFormat = configuration.debugLogFormat
builder.masksPrivateData = configuration.masksPrivateData
do {
try preset.injectExternalConfiguration(&builder, with: name, pool: pool)
} catch {
throw ApplicationError.externalResources
}
if let address = manualAddress {
builder.prefersResolvedAddresses = true
builder.resolvedAddresses = [address]
} else if builder.sessionConfiguration.hostname == nil {
builder.prefersResolvedAddresses = true
builder.resolvedAddresses = pool.addresses()
} else {
builder.prefersResolvedAddresses = !preferences.resolvesHostname
builder.resolvedAddresses = pool.addresses()
}
var sessionBuilder = builder.sessionConfiguration.builder()
if let proto = manualProtocol {
sessionBuilder.endpointProtocols = [proto]
} else {
// restrict "Any" protocol to UDP, unless there are no UDP endpoints
let allEndpoints = builder.sessionConfiguration.endpointProtocols
var endpoints = allEndpoints?.filter { $0.socketType == .udp }
if endpoints?.isEmpty ?? true {
endpoints = allEndpoints
}
sessionBuilder.endpointProtocols = endpoints
// sessionBuilder.endpointProtocols = [
// EndpointProtocol(.udp, 8080),
// EndpointProtocol(.tcp, 443)
// ]
}
builder.sessionConfiguration = sessionBuilder.build()
return builder.build()
}
public func with(newId: String) -> ConnectionProfile {
fatalError("Cannot rename a ProviderConnectionProfile")
}
}
public extension ProviderConnectionProfile {
static func ==(lhs: ProviderConnectionProfile, rhs: ProviderConnectionProfile) -> Bool {
return lhs.id == rhs.id
}
}
public extension ProviderConnectionProfile {
var mainAddress: String? {
guard let pool = pool else {
assertionFailure("Getting provider main address but no pool set")
return nil
}
return pool.hostname
}
var addresses: [String] {
var addrs = pool?.addresses() ?? []
if let pool = pool, pool.hostname == nil, !(pool.isResolved ?? false), let externalHostname = try? preset?.externalConfiguration(forKey: .hostname, infrastructureName: infrastructure.name, pool: pool) as? String {
addrs.insert(externalHostname, at: 0)
}
return addrs
}
var protocols: [EndpointProtocol] {
return preset?.configuration.sessionConfiguration.endpointProtocols ?? []
}
var canCustomizeEndpoint: Bool {
return true
}
var customAddress: String? {
return manualAddress
}
var customProtocol: EndpointProtocol? {
return manualProtocol
}
}

View File

@ -1,42 +0,0 @@
//
// SessionProxy+Communication.swift
// Passepartout
//
// Created by Davide De Rosa on 9/4/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public extension OpenVPN.ConfigurationBuilder {
// mutating func copyCommunication(from other: OpenVPN.ConfigurationBuilder) {
// cipher = other.cipher
// digest = other.digest
// compressionFraming = other.compressionFraming
// }
func canCommunicate(with other: OpenVPN.Configuration) -> Bool {
return
(cipher == other.cipher) &&
((digest == other.digest) || fallbackCipher.embedsDigest) &&
(compressionFraming == other.compressionFraming)
}
}

View File

@ -1,171 +0,0 @@
//
// TransientStore.swift
// Passepartout
//
// Created by Davide De Rosa on 7/16/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
import SwiftyBeaver
private let log = SwiftyBeaver.self
public class TransientStore {
private struct Keys {
static let didHandleSubreddit = "DidHandleSubreddit"
static let masksPrivateData = "MasksPrivateData"
// migrations
static let didMigrateHostsRoutingPolicies = "DidMigrateHostsRoutingPolicies"
}
public static let shared = TransientStore()
private static var serviceURL: URL {
return GroupConstants.App.documentsURL.appendingPathComponent(AppConstants.Store.serviceFilename)
}
public let service: ConnectionService
public static var didHandleSubreddit: Bool {
get {
return UserDefaults.standard.bool(forKey: Keys.didHandleSubreddit)
}
set {
UserDefaults.standard.set(newValue, forKey: Keys.didHandleSubreddit)
}
}
public static var masksPrivateData: Bool {
get {
return UserDefaults.standard.bool(forKey: Keys.masksPrivateData)
}
set {
UserDefaults.standard.set(newValue, forKey: Keys.masksPrivateData)
}
}
public static var didMigrateHostsRoutingPolicies: Bool {
get {
return UserDefaults.standard.bool(forKey: Keys.didMigrateHostsRoutingPolicies)
}
set {
UserDefaults.standard.set(newValue, forKey: Keys.didMigrateHostsRoutingPolicies)
}
}
public static var baseVPNConfiguration: OpenVPNTunnelProvider.ConfigurationBuilder {
let sessionBuilder = OpenVPN.ConfigurationBuilder()
var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
builder.mtu = 1250
builder.shouldDebug = true
// builder.debugLogFormat = "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
// builder.debugLogFormat = "$DHH:mm:ss$d $N.$F:$l - $M"
builder.debugLogFormat = AppConstants.Log.debugFormat
builder.masksPrivateData = masksPrivateData
return builder
}
private init() {
UserDefaults.standard.register(defaults: [
Keys.didHandleSubreddit: false,
Keys.masksPrivateData: true
])
TransientStore.migrateDocumentsToAppGroup()
// this must be graceful
ConnectionService.migrateJSON(from: TransientStore.serviceURL, to: TransientStore.serviceURL)
let cfg = TransientStore.baseVPNConfiguration.build()
do {
let data = try Data(contentsOf: TransientStore.serviceURL)
if let content = String(data: data, encoding: .utf8) {
log.verbose("Service JSON:")
log.verbose(content)
}
service = try JSONDecoder().decode(ConnectionService.self, from: data)
service.baseConfiguration = cfg
service.loadProfiles()
// do migrations
if !TransientStore.didMigrateHostsRoutingPolicies {
if service.reloadHostProfilesFromConfigurationFiles() {
service.saveProfiles()
}
TransientStore.didMigrateHostsRoutingPolicies = true
}
} catch let e {
log.error("Could not decode service: \(e)")
service = ConnectionService(
withAppGroup: GroupConstants.App.groupId,
baseConfiguration: cfg
)
// // hardcoded loading
// _ = service.addProfile(ProviderConnectionProfile(name: .pia), credentials: nil)
// _ = service.addProfile(HostConnectionProfile(title: "vps"), credentials: Credentials(username: "foo", password: "bar"))
// service.activateProfile(service.profiles.first!)
}
service.observeVPNDataCount(interval: TimeInterval(GroupConstants.VPN.dataCountInterval) / 1000.0)
}
public func serialize(withProfiles: Bool) {
try? JSONEncoder().encode(service).write(to: TransientStore.serviceURL)
if withProfiles {
service.saveProfiles()
}
}
//
private static func migrateDocumentsToAppGroup() {
var hasMigrated = false
let oldDocumentsURL = FileManager.default.userURL(for: .documentDirectory, appending: nil)
let newDocumentsURL = GroupConstants.App.documentsURL
log.debug("App documentsURL: \(oldDocumentsURL)")
log.debug("Group documentsURL: \(newDocumentsURL)")
let fm = FileManager.default
do {
for c in try fm.contentsOfDirectory(atPath: oldDocumentsURL.path) {
guard c != "Inbox" else {
continue
}
let old = oldDocumentsURL.appendingPathComponent(c)
let new = newDocumentsURL.appendingPathComponent(c)
log.verbose("Move:")
log.verbose("\tFROM: \(old)")
log.verbose("\tTO: \(new)")
try fm.moveItem(at: old, to: new)
hasMigrated = true
}
} catch let e {
hasMigrated = false
log.error("Could not migrate documents to App Group: \(e)")
}
if hasMigrated {
log.debug("Documents migrated to App Group")
}
}
}

View File

@ -1,32 +0,0 @@
//
// TrustPolicy.swift
// Passepartout
//
// Created by Davide De Rosa on 9/2/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public enum TrustPolicy: String, Codable {
case ignore
case disconnect
}

View File

@ -1,219 +0,0 @@
//
// TrustedNetworks.swift
// Passepartout
//
// Created by Davide De Rosa on 6/21/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public protocol TrustedNetworksModelDelegate: class {
func trustedNetworksCouldDisconnect(_: TrustedNetworksModel) -> Bool
func trustedNetworksShouldConfirmDisconnection(_: TrustedNetworksModel, triggeredAt rowIndex: Int, completionHandler: @escaping () -> Void)
func trustedNetworks(_: TrustedNetworksModel, shouldInsertWifiAt rowIndex: Int)
func trustedNetworks(_: TrustedNetworksModel, shouldReloadWifiAt rowIndex: Int, isTrusted: Bool)
func trustedNetworks(_: TrustedNetworksModel, shouldDeleteWifiAt rowIndex: Int)
func trustedNetworksShouldReinstall(_: TrustedNetworksModel)
}
public class TrustedNetworksModel {
public enum RowType {
case trustsMobile
case trustedWiFi
case addCurrentWiFi
}
public private(set) var trustedWifis: [String: Bool]
public private(set) var sortedWifis: [String]
#if os(iOS)
private let hasMobileNetwork: Bool
public private(set) var trustsMobileNetwork: Bool
public private(set) var rows: [RowType]
#endif
public weak var delegate: TrustedNetworksModelDelegate?
public init() {
trustedWifis = [:]
sortedWifis = []
#if os(iOS)
hasMobileNetwork = Utils.hasCellularData()
trustsMobileNetwork = false
rows = []
#endif
}
public func load(from preferences: Preferences) {
trustedWifis = preferences.trustedWifis
sortedWifis = trustedWifis.keys.sorted()
#if os(iOS)
trustsMobileNetwork = preferences.trustsMobileNetwork
rows.removeAll()
if hasMobileNetwork {
rows.append(.trustsMobile)
}
for _ in sortedWifis {
rows.append(.trustedWiFi)
}
rows.append(.addCurrentWiFi)
#endif
}
#if os(iOS)
public func setMobile(_ isTrusted: Bool) {
let completionHandler: () -> Void = {
self.trustsMobileNetwork = isTrusted
self.delegate?.trustedNetworksShouldReinstall(self)
}
guard !(isTrusted && mightDisconnect()) else {
delegate?.trustedNetworksShouldConfirmDisconnection(self, triggeredAt: 0, completionHandler: completionHandler)
return
}
completionHandler()
}
#endif
public func wifi(at rowIndex: Int) -> (String, Bool) {
let index = indexForWifi(at: rowIndex)
let wifiName = sortedWifis[index]
let isTrusted = trustedWifis[wifiName] ?? false
return (wifiName, isTrusted)
}
public func addCurrentWifi() -> Bool {
guard let currentWifi = Utils.currentWifiNetworkName() else {
return false
}
addWifi(currentWifi)
return true
}
public func addWifi(_ wifiToAdd: String) {
var index = 0
var isDuplicate = false
for wifi in sortedWifis {
guard wifiToAdd != wifi else {
isDuplicate = true
break
}
guard wifiToAdd > wifi else {
break
}
index += 1
}
guard !(trustedWifis[wifiToAdd] ?? false) else {
return
}
let isTrusted = false
let rowIndex = rowIndexForWifi(at: index)
trustedWifis[wifiToAdd] = isTrusted
if !isDuplicate {
sortedWifis.insert(wifiToAdd, at: index)
#if os(iOS)
rows.insert(.trustedWiFi, at: rowIndex)
#endif
delegate?.trustedNetworks(self, shouldInsertWifiAt: rowIndex)
} else {
delegate?.trustedNetworks(self, shouldReloadWifiAt: rowIndex, isTrusted: isTrusted)
}
delegate?.trustedNetworksShouldReinstall(self)
}
public func removeWifi(at rowIndex: Int) {
let index = indexForWifi(at: rowIndex)
let removedWifi = sortedWifis.remove(at: index)
trustedWifis.removeValue(forKey: removedWifi)
#if os(iOS)
rows.remove(at: rowIndex)
#endif
delegate?.trustedNetworks(self, shouldDeleteWifiAt: rowIndex)
delegate?.trustedNetworksShouldReinstall(self)
}
public func enableWifi(at rowIndex: Int) {
let index = indexForWifi(at: rowIndex)
let wifi = sortedWifis[index]
let completionHandler: () -> Void = {
self.trustedWifis[wifi] = true
self.delegate?.trustedNetworks(self, shouldReloadWifiAt: rowIndex, isTrusted: true)
self.delegate?.trustedNetworksShouldReinstall(self)
}
guard !mightDisconnect() else {
delegate?.trustedNetworksShouldConfirmDisconnection(self, triggeredAt: rowIndex, completionHandler: completionHandler)
return
}
completionHandler()
}
public func disableWifi(at rowIndex: Int) {
let index = indexForWifi(at: rowIndex)
let wifi = sortedWifis[index]
trustedWifis[wifi] = false
delegate?.trustedNetworks(self, shouldReloadWifiAt: rowIndex, isTrusted: false)
delegate?.trustedNetworksShouldReinstall(self)
}
public func isTrusted(wifi: String) -> Bool {
return trustedWifis[wifi] ?? false
}
private func indexForWifi(at rowIndex: Int) -> Int {
#if os(iOS)
return hasMobileNetwork ? (rowIndex - 1) : rowIndex
#else
return rowIndex
#endif
}
private func rowIndexForWifi(at index: Int) -> Int {
#if os(iOS)
return index + (hasMobileNetwork ? 1 : 0)
#else
return index
#endif
}
private func mightDisconnect() -> Bool {
return delegate?.trustedNetworksCouldDisconnect(self) ?? false
}
}

View File

@ -1,75 +0,0 @@
//
// Reviewer.swift
// Passepartout
//
// Created by Davide De Rosa on 12/10/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import StoreKit
import SwiftyBeaver
private let log = SwiftyBeaver.self
public class Reviewer {
private struct Keys {
static let eventCount = "ReviewerEventCount"
static let lastVersion = "ReviewerLastVersion"
}
public static let shared = Reviewer()
private let defaults: UserDefaults
public var eventCountBeforeRating = 3
private init() {
defaults = .standard
}
public func reportEvent() {
let currentVersion = GroupConstants.App.buildNumber
let lastVersion = defaults.integer(forKey: Keys.lastVersion)
if lastVersion > 0 {
log.debug("App last reviewed for version \(lastVersion)")
} else {
log.debug("App was never reviewed")
}
guard currentVersion != lastVersion else {
log.debug("App already reviewed for version \(currentVersion)")
return
}
var count = defaults.integer(forKey: Keys.eventCount)
count += 1
defaults.set(count, forKey: Keys.eventCount)
log.debug("Event reported for version \(currentVersion) (count: \(count), prompt: \(eventCountBeforeRating))")
guard count >= eventCountBeforeRating else {
return
}
log.debug("Prompting for review...")
SKStoreReviewController.requestReview()
defaults.removeObject(forKey: Keys.eventCount)
defaults.set(currentVersion, forKey: Keys.lastVersion)
}
}

View File

@ -1,140 +0,0 @@
//
// Infrastructure.swift
// Passepartout
//
// Created by Davide De Rosa on 6/11/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import SSZipArchive
public struct Infrastructure: Codable {
public enum Name: String, Codable, Comparable {
case mullvad = "Mullvad"
case nordVPN = "NordVPN"
case pia = "PIA"
case protonVPN = "ProtonVPN"
case tunnelBear = "TunnelBear"
case vyprVPN = "VyprVPN"
case windscribe = "Windscribe"
}
public struct Defaults: Codable {
public let username: String?
public let pool: String
public let preset: String
}
public let build: Int
public let name: Name
public let categories: [PoolCategory]
public let presets: [InfrastructurePreset]
public let defaults: Defaults
public static func loaded(from url: URL) throws -> Infrastructure {
let json = try Data(contentsOf: url)
return try JSONDecoder().decode(Infrastructure.self, from: json)
}
public func defaultPool() -> Pool? {
return pool(withPrefix: defaults.pool)
}
public func pool(for identifier: String) -> Pool? {
for cat in categories {
for group in cat.groups {
guard let found = group.pools.first(where: { $0.id == identifier }) else {
continue
}
return found
}
}
return nil
}
public func pool(withPrefix prefix: String) -> Pool? {
for cat in categories {
for group in cat.groups {
guard let found = group.pools.first(where: { $0.id.hasPrefix(prefix) }) else {
continue
}
return found
}
}
return nil
}
public func preset(for identifier: String) -> InfrastructurePreset? {
return presets.first { $0.id == identifier }
}
}
extension Infrastructure.Name {
public var webName: String {
return rawValue.lowercased()
}
public static func <(lhs: Infrastructure.Name, rhs: Infrastructure.Name) -> Bool {
return lhs.webName < rhs.webName
}
}
extension Infrastructure.Name {
public var externalURL: URL {
return GroupConstants.App.externalURL.appendingPathComponent(webName)
}
public func importExternalResources(from url: URL, completionHandler: @escaping () -> Void) {
var task: () -> Void
switch self {
case .nordVPN:
task = {
SSZipArchive.unzipFile(atPath: url.path, toDestination: self.externalURL.path)
}
default:
task = {}
}
execute(task: task, completionHandler: completionHandler)
}
private func execute(task: @escaping () -> Void, completionHandler: @escaping () -> Void) {
let queue: DispatchQueue = .global(qos: .background)
queue.async {
task()
DispatchQueue.main.async {
completionHandler()
}
}
}
}

View File

@ -1,272 +0,0 @@
//
// InfrastructureFactory.swift
// Passepartout
//
// Created by Davide De Rosa on 9/2/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import SwiftyBeaver
private let log = SwiftyBeaver.self
// TODO: retain max N infrastructures at a time (LRU)
public class InfrastructureFactory {
private static func embedded(withName name: Infrastructure.Name) -> Infrastructure {
guard let url = name.bundleURL else {
fatalError("Cannot find JSON for infrastructure '\(name)'")
}
do {
return try Infrastructure.loaded(from: url)
} catch let e {
fatalError("Cannot parse JSON for infrastructure '\(name)': \(e)")
}
}
private static func isNewer(cachedEntry: URL, thanBundleWithName name: Infrastructure.Name) -> Bool {
guard let cacheDate = FileManager.default.modificationDate(of: cachedEntry.path) else {
return false
}
guard let bundleURL = name.bundleURL else {
return true
}
guard let bundleDate = FileManager.default.modificationDate(of: bundleURL.path) else {
return true
}
return cacheDate > bundleDate
}
public static let shared = InfrastructureFactory()
// manually pre-sorted
public let allNames: [Infrastructure.Name] = [
.mullvad,
.nordVPN,
.pia,
.protonVPN,
.tunnelBear,
.vyprVPN,
.windscribe
]
private let bundle: [Infrastructure.Name: Infrastructure]
private let cachePath: URL
private var cache: [Infrastructure.Name: Infrastructure]
private var lastUpdate: [Infrastructure.Name: Date]
private init() {
var bundle: [Infrastructure.Name: Infrastructure] = [:]
allNames.forEach {
bundle[$0] = InfrastructureFactory.embedded(withName: $0)
}
self.bundle = bundle
cachePath = GroupConstants.App.cachesURL
cache = [:]
lastUpdate = [:]
}
public func loadCache() {
let cacheEntries: [URL]
let netPath = "\(AppConstants.Store.webCacheDirectory)/\(WebServices.Group.network.rawValue)"
do {
cacheEntries = try FileManager.default.contentsOfDirectory(
at: cachePath.appendingPathComponent(netPath),
includingPropertiesForKeys: nil
)
} catch let e {
log.verbose("Error loading cache: \(e)")
return
}
let decoder = JSONDecoder()
for entry in cacheEntries {
guard let data = try? Data(contentsOf: entry) else {
continue
}
let infra: Infrastructure
do {
infra = try decoder.decode(Infrastructure.self, from: data)
} catch let e {
log.warning("Unable to load infrastructure \(entry.lastPathComponent): \(e)")
if let json = String(data: data, encoding: .utf8) {
log.warning(json)
}
continue
}
// supersede if older than embedded
guard InfrastructureFactory.isNewer(cachedEntry: entry, thanBundleWithName: infra.name) else {
log.warning("Bundle is newer than cache, superseding cache for \(infra.name)")
cache[infra.name] = bundle[infra.name]
continue
}
cache[infra.name] = infra
log.debug("Loading cache for \(infra.name)")
}
}
public func get(_ name: Infrastructure.Name) -> Infrastructure {
guard let infra = cache[name] ?? bundle[name] else {
fatalError("No infrastructure embedded nor cached for '\(name)'")
}
return infra
}
public func update(_ name: Infrastructure.Name, notBeforeInterval minInterval: TimeInterval?, completionHandler: @escaping ((Infrastructure, Date)?, Error?) -> Void) -> Bool {
let ifModifiedSince = modificationDate(for: name)
if let lastInfrastructureUpdate = lastUpdate[name] {
log.debug("Last update for \(name): \(lastUpdate)")
if let minInterval = minInterval {
let elapsed = -lastInfrastructureUpdate.timeIntervalSinceNow
guard elapsed >= minInterval else {
log.warning("Skipping update, only \(elapsed) seconds elapsed (< \(minInterval))")
return false
}
}
}
WebServices.shared.network(with: name, ifModifiedSince: ifModifiedSince) { (response, error) in
if error == nil {
self.lastUpdate[name] = Date()
}
guard let response = response else {
log.error("No response from web service")
DispatchQueue.main.async {
completionHandler(nil, error)
}
return
}
if response.isCached {
log.debug("Cache is up to date")
DispatchQueue.main.async {
completionHandler(nil, error)
}
return
}
guard let infra = response.value, let lastModified = response.lastModified else {
log.error("No response from web service or missing Last-Modified")
DispatchQueue.main.async {
completionHandler(nil, error)
}
return
}
let appBuild = GroupConstants.App.buildNumber
guard appBuild >= infra.build else {
log.error("Response requires app build >= \(infra.build) (found \(appBuild))")
DispatchQueue.main.async {
completionHandler(nil, error)
}
return
}
var isNewer = true
if let bundleDate = self.bundleModificationDate(for: name) {
log.verbose("Bundle date: \(bundleDate)")
log.verbose("Web date: \(lastModified)")
isNewer = lastModified > bundleDate
}
guard isNewer else {
log.warning("Web service infrastructure is older than bundle, discarding")
DispatchQueue.main.async {
completionHandler(nil, error)
}
return
}
self.save(name, with: infra, lastModified: lastModified)
DispatchQueue.main.async {
completionHandler((infra, lastModified), nil)
}
}
return true
}
public func modificationDate(for name: Infrastructure.Name) -> Date? {
let optBundleDate = bundleModificationDate(for: name)
guard let cacheDate = cacheModificationDate(for: name) else {
return optBundleDate
}
guard let bundleDate = optBundleDate else {
return cacheDate
}
return max(cacheDate, bundleDate)
}
private func save(_ name: Infrastructure.Name, with infrastructure: Infrastructure, lastModified: Date) {
cache[name] = infrastructure
let fm = FileManager.default
let url = cacheURL(for: name)
do {
let parent = url.deletingLastPathComponent()
try fm.createDirectory(at: parent, withIntermediateDirectories: true, attributes: nil)
let data = try JSONEncoder().encode(infrastructure)
try data.write(to: url)
try fm.setAttributes([.modificationDate: lastModified], ofItemAtPath: url.path)
} catch let e {
log.error("Error saving cache: \(e)")
}
}
private func cacheURL(for name: Infrastructure.Name) -> URL {
return cachePath.appendingPathComponent(name.bundleRelativePath)
}
private func cacheModificationDate(for name: Infrastructure.Name) -> Date? {
let url = cacheURL(for: name)
return FileManager.default.modificationDate(of: url.path)
}
private func bundleModificationDate(for name: Infrastructure.Name) -> Date? {
guard let url = name.bundleURL else {
return nil
}
return FileManager.default.modificationDate(of: url.path)
}
}
private extension Infrastructure.Name {
var bundleRelativePath: String {
let endpoint = WebServices.Endpoint.network(self)
// e.g. "Web", PIA="net/pia" -> "Web/net/pia.json"
return "\(AppConstants.Store.webCacheDirectory)/\(endpoint.path).json"
}
var bundleURL: URL? {
let bundle = Bundle(for: InfrastructureFactory.self)
let endpoint = WebServices.Endpoint.network(self)
// e.g. "Web", PIA="net/pia" -> "[Bundle]:Web/net/pia.json"
return bundle.url(forResource: "\(AppConstants.Store.webCacheDirectory)/\(endpoint.path)", withExtension: "json")
}
}

View File

@ -1,232 +0,0 @@
//
// InfrastructurePreset.swift
// Passepartout
//
// Created by Davide De Rosa on 8/30/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
// supports a subset of OpenVPNTunnelProvider.Configuration
// ignores new JSON keys
public struct InfrastructurePreset: Codable {
public enum ExternalKey: String, Codable {
case ca
case client
case key
case wrapKeyData = "wrap.key.data"
case hostname
}
public enum PresetKeys: String, CodingKey {
case id
case name
case comment
case configuration = "cfg"
case external
}
public enum ConfigurationKeys: String, CodingKey {
case endpointProtocols = "ep"
case cipher
case digest = "auth"
case ca
case clientCertificate = "client"
case clientKey = "key"
case compressionFraming = "frame"
case compressionAlgorithm = "compression"
case keepAliveSeconds = "ping"
case renegotiatesAfterSeconds = "reneg"
case tlsWrap = "wrap"
case checksEKU = "eku"
case randomizeEndpoint = "random"
case usesPIAPatches = "pia"
}
public let id: String
public let name: String
public let comment: String
public let configuration: OpenVPNTunnelProvider.Configuration
public let external: [ExternalKey: String]?
public func hasProtocol(_ proto: EndpointProtocol) -> Bool {
return configuration.sessionConfiguration.endpointProtocols?.firstIndex(of: proto) != nil
}
public func externalConfiguration(forKey key: ExternalKey, infrastructureName: Infrastructure.Name, pool: Pool) throws -> Any? {
guard let pattern = external?[key] else {
return nil
}
let baseURL = infrastructureName.externalURL
switch key {
case .ca:
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
let caURL = baseURL.appendingPathComponent(filename)
return OpenVPN.CryptoContainer(pem: try String(contentsOf: caURL))
case .wrapKeyData:
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
let tlsKeyURL = baseURL.appendingPathComponent(filename)
let file = try String(contentsOf: tlsKeyURL)
return OpenVPN.StaticKey(file: file, direction: .client)
case .hostname:
return pattern.replacingOccurrences(of: "${id}", with: pool.id)
default:
break
}
return nil
}
public func injectExternalConfiguration(_ configuration: inout OpenVPNTunnelProvider.ConfigurationBuilder, with infrastructureName: Infrastructure.Name, pool: Pool) throws {
guard let external = external, !external.isEmpty else {
return
}
var sessionBuilder = configuration.sessionConfiguration.builder()
if let _ = external[.ca] {
sessionBuilder.ca = try externalConfiguration(forKey: .ca, infrastructureName: infrastructureName, pool: pool) as? OpenVPN.CryptoContainer
}
if let _ = external[.wrapKeyData] {
if let dummyWrap = sessionBuilder.tlsWrap {
if let staticKey = try externalConfiguration(forKey: .wrapKeyData, infrastructureName: infrastructureName, pool: pool) as? OpenVPN.StaticKey {
sessionBuilder.tlsWrap = OpenVPN.TLSWrap(strategy: dummyWrap.strategy, key: staticKey)
}
}
}
if let _ = external[.hostname] {
sessionBuilder.hostname = try externalConfiguration(forKey: .hostname, infrastructureName: infrastructureName, pool: pool) as? String
}
configuration.sessionConfiguration = sessionBuilder.build()
}
// MARK: Codable
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: PresetKeys.self)
id = try container.decode(String.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
comment = try container.decode(String.self, forKey: .comment)
if let rawExternal = try container.decodeIfPresent([String: String].self, forKey: .external) {
var remapped: [ExternalKey: String] = [:]
for entry in rawExternal {
guard let key = ExternalKey(rawValue: entry.key) else {
continue
}
remapped[key] = entry.value
}
external = remapped
} else {
external = nil
}
let cfgContainer = try container.nestedContainer(keyedBy: ConfigurationKeys.self, forKey: .configuration)
var sessionBuilder = OpenVPN.ConfigurationBuilder()
sessionBuilder.cipher = try cfgContainer.decode(OpenVPN.Cipher.self, forKey: .cipher)
if let digest = try cfgContainer.decodeIfPresent(OpenVPN.Digest.self, forKey: .digest) {
sessionBuilder.digest = digest
}
sessionBuilder.compressionFraming = try cfgContainer.decode(OpenVPN.CompressionFraming.self, forKey: .compressionFraming)
sessionBuilder.compressionAlgorithm = try cfgContainer.decodeIfPresent(OpenVPN.CompressionAlgorithm.self, forKey: .compressionAlgorithm) ?? .disabled
sessionBuilder.ca = try cfgContainer.decodeIfPresent(OpenVPN.CryptoContainer.self, forKey: .ca)
sessionBuilder.clientCertificate = try cfgContainer.decodeIfPresent(OpenVPN.CryptoContainer.self, forKey: .clientCertificate)
sessionBuilder.clientKey = try cfgContainer.decodeIfPresent(OpenVPN.CryptoContainer.self, forKey: .clientKey)
sessionBuilder.tlsWrap = try cfgContainer.decodeIfPresent(OpenVPN.TLSWrap.self, forKey: .tlsWrap)
sessionBuilder.keepAliveInterval = try cfgContainer.decodeIfPresent(TimeInterval.self, forKey: .keepAliveSeconds)
sessionBuilder.renegotiatesAfter = try cfgContainer.decodeIfPresent(TimeInterval.self, forKey: .renegotiatesAfterSeconds)
sessionBuilder.endpointProtocols = try cfgContainer.decode([EndpointProtocol].self, forKey: .endpointProtocols)
sessionBuilder.checksEKU = try cfgContainer.decodeIfPresent(Bool.self, forKey: .checksEKU) ?? false
sessionBuilder.randomizeEndpoint = try cfgContainer.decodeIfPresent(Bool.self, forKey: .randomizeEndpoint) ?? false
sessionBuilder.usesPIAPatches = try cfgContainer.decodeIfPresent(Bool.self, forKey: .usesPIAPatches) ?? false
// default to server settings
sessionBuilder.routingPolicies = nil
let builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
configuration = builder.build()
}
public func encode(to encoder: Encoder) throws {
guard let ca = configuration.sessionConfiguration.ca else {
fatalError("Could not encode nil ca")
}
guard let endpointProtocols = configuration.sessionConfiguration.endpointProtocols else {
fatalError("Could not encode nil endpointProtocols")
}
var container = encoder.container(keyedBy: PresetKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(comment, forKey: .comment)
if let external = external {
var rawExternal: [String: String] = [:]
for entry in external {
rawExternal[entry.key.rawValue] = entry.value
}
try container.encode(rawExternal, forKey: .external)
}
var cfgContainer = container.nestedContainer(keyedBy: ConfigurationKeys.self, forKey: .configuration)
try cfgContainer.encode(configuration.sessionConfiguration.cipher, forKey: .cipher)
try cfgContainer.encode(configuration.sessionConfiguration.digest, forKey: .digest)
try cfgContainer.encode(configuration.sessionConfiguration.compressionFraming, forKey: .compressionFraming)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.compressionAlgorithm, forKey: .compressionAlgorithm)
try cfgContainer.encodeIfPresent(ca, forKey: .ca)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.clientCertificate, forKey: .clientCertificate)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.clientKey, forKey: .clientKey)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.tlsWrap, forKey: .tlsWrap)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.keepAliveInterval, forKey: .keepAliveSeconds)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.renegotiatesAfter, forKey: .renegotiatesAfterSeconds)
try cfgContainer.encode(endpointProtocols, forKey: .endpointProtocols)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.checksEKU, forKey: .checksEKU)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.randomizeEndpoint, forKey: .randomizeEndpoint)
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.usesPIAPatches, forKey: .usesPIAPatches)
}
}

View File

@ -1,161 +0,0 @@
//
// Pool.swift
// Passepartout
//
// Created by Davide De Rosa on 6/11/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
public struct Pool: Codable, Hashable {
public enum CodingKeys: String, CodingKey {
case id
case country
case extraCountries = "extra_countries"
case area
case num
case tags
// case location
case hostname
case isResolved = "resolved"
case numericAddresses = "addrs"
}
public let id: String
public let country: String
public let extraCountries: [String]?
public let area: String?
public let num: Int?
public let tags: [String]?
// public let location: (Double, Double)
public let hostname: String?
public let isResolved: Bool?
public let numericAddresses: [UInt32]?
// XXX: inefficient but convenient field (not serialized)
public func category(in infrastructure: Infrastructure) -> PoolCategory? {
for category in infrastructure.categories {
for group in category.groups {
for pool in group.pools {
if pool.id == id {
return category
}
}
}
}
return nil
}
public func supportedPresetIds(in infrastructure: Infrastructure) -> [String] {
let poolCategory = category(in: infrastructure)
return poolCategory?.presets ?? infrastructure.presets.map { $0.id }
}
public func hasAddress(_ address: String) -> Bool {
guard let numericAddresses = numericAddresses else {
return false
}
guard let ipv4 = DNSResolver.ipv4(fromString: address) else {
return false
}
return numericAddresses.contains(ipv4)
}
// XXX: inefficient, can't easily use lazy on struct
public func addresses() -> [String] {
var addrs = numericAddresses?.map { DNSResolver.string(fromIPv4: $0) } ?? []
if let hostname = hostname {
addrs.insert(hostname, at: 0)
}
return addrs
}
// MARK: Equatable
public static func == (lhs: Pool, rhs: Pool) -> Bool {
return lhs.id == rhs.id
}
// MARK: Hashable
public func hash(into hasher: inout Hasher) {
id.hash(into: &hasher)
}
}
extension Pool {
public var localizedCountry: String {
return Utils.localizedCountry(country)
}
public var localizedId: String {
var comps: [String] = [localizedCountry]
if let secondaryId = optionalSecondaryId {
comps.append(secondaryId)
}
return comps.joined(separator: " - ")
}
public var secondaryId: String {
return optionalSecondaryId ?? ""
}
private var optionalSecondaryId: String? {
var comps: [String] = []
if let extraCountries = extraCountries {
comps.append(contentsOf: extraCountries.map { Utils.localizedCountry($0) })
}
if let area = area {
comps.append(area.uppercased())
}
if let num = num {
comps.append("#\(num)")
}
guard !comps.isEmpty else {
return nil
}
var str = comps.joined(separator: " ")
if let tags = tags {
let suffix = tags.map { $0.uppercased() }.joined(separator: ",")
str = "\(str) (\(suffix))"
}
return str
}
}

View File

@ -1,74 +0,0 @@
//
// PoolGroup.swift
// Passepartout
//
// Created by Davide De Rosa on 4/6/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public struct PoolGroup: Codable, Hashable, Comparable, CustomStringConvertible {
public let country: String
public let area: String?
public let pools: [Pool]
private var id: String {
var id = country
if let area = area {
id += area
}
return id
}
private var localizedId: String {
var localizedId = Utils.localizedCountry(country)
if let area = area {
localizedId += area
}
return localizedId
}
// MARK: Hashable
public func hash(into hasher: inout Hasher) {
id.hash(into: &hasher)
}
// MARK: Comparable
public static func <(lhs: PoolGroup, rhs: PoolGroup) -> Bool {
return lhs.localizedId < rhs.localizedId
}
// MARK: CustomStringConvertible
public var description: String {
return "{\(country), \(area ?? "--")}"
}
}
extension PoolGroup {
public var localizedCountry: String {
return Utils.localizedCountry(country)
}
}

View File

@ -1,130 +0,0 @@
//
// WebServices.swift
// Passepartout
//
// Created by Davide De Rosa on 9/14/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import SwiftyBeaver
private let log = SwiftyBeaver.self
public class WebServices {
public enum Group: String {
case network = "net"
}
public enum Endpoint {
case network(Infrastructure.Name)
var path: String {
switch self {
case .network(let name):
return "\(Group.network.rawValue)/\(name.webName)"
}
}
}
public struct Response<T> {
public let value: T?
public let lastModifiedString: String?
public var lastModified: Date? {
guard let string = lastModifiedString else {
return nil
}
return lmFormatter.date(from: string)
}
public let isCached: Bool
}
public static let shared = WebServices()
private static let lmFormatter: DateFormatter = {
let fmt = DateFormatter()
fmt.timeZone = TimeZone(abbreviation: "GMT")
fmt.dateFormat = "EEE, dd LLL yyyy HH:mm:ss zzz"
return fmt
}()
public func network(with name: Infrastructure.Name, ifModifiedSince lastModified: Date?, completionHandler: @escaping (Response<Infrastructure>?, Error?) -> Void) {
var request = get(.network(name))
if let lastModified = lastModified {
request.addValue(WebServices.lmFormatter.string(from: lastModified), forHTTPHeaderField: "If-Modified-Since")
}
parse(Infrastructure.self, request: request, completionHandler: completionHandler)
}
private func get(_ endpoint: Endpoint) -> URLRequest {
let url = AppConstants.Web.url(path: "\(endpoint.path).json")
return URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: AppConstants.Web.timeout)
}
private func parse<T: Decodable>(_ type: T.Type, request: URLRequest, completionHandler: @escaping (Response<T>?, Error?) -> Void) {
log.debug("GET \(request.url!)")
log.debug("Request headers: \(request.allHTTPHeaderFields?.description ?? "")")
let session = URLSession(configuration: .default)
session.dataTask(with: request) { (data, response, error) in
guard let httpResponse = response as? HTTPURLResponse else {
log.error("Error (response): \(error?.localizedDescription ?? "nil")")
completionHandler(nil, error)
return
}
let statusCode = httpResponse.statusCode
log.debug("Response status: \(statusCode)")
if let responseHeaders = httpResponse.allHeaderFields as? [String: String] {
log.debug("Response headers: \(responseHeaders)")
}
// 304: cache hit
if statusCode == 304 {
log.debug("Response is cached")
completionHandler(Response(value: nil, lastModifiedString: nil, isCached: true), nil)
return
}
// 200: cache miss
let value: T
let lastModifiedString: String?
guard statusCode == 200, let data = data else {
log.error("Error (network): \(error?.localizedDescription ?? "nil")")
completionHandler(nil, error)
return
}
do {
value = try JSONDecoder().decode(type, from: data)
} catch let e {
log.error("Error (parsing): \(e)")
completionHandler(nil, error)
return
}
lastModifiedString = httpResponse.allHeaderFields["Last-Modified"] as? String
let response = Response(value: value, lastModifiedString: lastModifiedString, isCached: false)
completionHandler(response, nil)
}.resume()
}
}

View File

@ -1,995 +0,0 @@
// swiftlint:disable all
// Generated using SwiftGen https://github.com/SwiftGen/SwiftGen
import Foundation
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - Strings
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:disable nesting type_body_length type_name
public enum L10n {
public enum About {
/// About
public static let title = L10n.tr("Localizable", "about.title")
public enum Cells {
public enum Credits {
/// Credits
public static let caption = L10n.tr("Localizable", "about.cells.credits.caption")
}
public enum Disclaimer {
/// Disclaimer
public static let caption = L10n.tr("Localizable", "about.cells.disclaimer.caption")
}
public enum Faq {
/// FAQ
public static let caption = L10n.tr("Localizable", "about.cells.faq.caption")
}
public enum PrivacyPolicy {
/// Privacy policy
public static let caption = L10n.tr("Localizable", "about.cells.privacy_policy.caption")
}
public enum ShareGeneric {
/// Invite a friend
public static let caption = L10n.tr("Localizable", "about.cells.share_generic.caption")
}
public enum ShareTwitter {
/// Tweet about it!
public static let caption = L10n.tr("Localizable", "about.cells.share_twitter.caption")
}
public enum Website {
/// Home page
public static let caption = L10n.tr("Localizable", "about.cells.website.caption")
}
}
public enum Sections {
public enum Share {
/// Share
public static let header = L10n.tr("Localizable", "about.sections.share.header")
}
public enum Web {
/// Web
public static let header = L10n.tr("Localizable", "about.sections.web.header")
}
}
}
public enum Account {
public enum Cells {
public enum OpenGuide {
/// See your credentials
public static let caption = L10n.tr("Localizable", "account.cells.open_guide.caption")
}
public enum Password {
/// Password
public static let caption = L10n.tr("Localizable", "account.cells.password.caption")
/// secret
public static let placeholder = L10n.tr("Localizable", "account.cells.password.placeholder")
}
public enum Signup {
/// Register with %@
public static func caption(_ p1: String) -> String {
return L10n.tr("Localizable", "account.cells.signup.caption", p1)
}
}
public enum Username {
/// Username
public static let caption = L10n.tr("Localizable", "account.cells.username.caption")
/// username
public static let placeholder = L10n.tr("Localizable", "account.cells.username.placeholder")
}
}
public enum Sections {
public enum Credentials {
/// Credentials
public static let header = L10n.tr("Localizable", "account.sections.credentials.header")
}
public enum Guidance {
public enum Footer {
public enum Infrastructure {
/// Use your %@ website credentials. Your username is usually numeric.
public static func mullvad(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.mullvad", p1)
}
/// Use your %@ website credentials. Your username is usually your e-mail.
public static func nordvpn(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.nordvpn", p1)
}
/// Use your %@ website credentials. Your username is usually numeric with a "p" prefix.
public static func pia(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.pia", p1)
}
/// Find your %@ credentials in the "Account > OpenVPN / IKEv2 Username" section of the website.
public static func protonvpn(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.protonvpn", p1)
}
/// Use your %@ website credentials. Your username is usually your e-mail.
public static func tunnelbear(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.tunnelbear", p1)
}
/// Use your %@ website credentials. Your username is usually your e-mail.
public static func vyprvpn(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.vyprvpn", p1)
}
/// Find your %@ credentials in the OpenVPN Config Generator on the website.
public static func windscribe(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.windscribe", p1)
}
}
}
}
public enum Registration {
/// Go get an account on the %@ website.
public static func footer(_ p1: String) -> String {
return L10n.tr("Localizable", "account.sections.registration.footer", p1)
}
}
}
}
public enum Configuration {
public enum Cells {
public enum Cipher {
/// Cipher
public static let caption = L10n.tr("Localizable", "configuration.cells.cipher.caption")
}
public enum Client {
/// Client certificate
public static let caption = L10n.tr("Localizable", "configuration.cells.client.caption")
public enum Value {
/// Not verified
public static let disabled = L10n.tr("Localizable", "configuration.cells.client.value.disabled")
/// Verified
public static let enabled = L10n.tr("Localizable", "configuration.cells.client.value.enabled")
}
}
public enum CompressionAlgorithm {
/// Algorithm
public static let caption = L10n.tr("Localizable", "configuration.cells.compression_algorithm.caption")
public enum Value {
/// LZO
public static let lzo = L10n.tr("Localizable", "configuration.cells.compression_algorithm.value.lzo")
/// Unsupported
public static let other = L10n.tr("Localizable", "configuration.cells.compression_algorithm.value.other")
}
}
public enum CompressionFraming {
/// Framing
public static let caption = L10n.tr("Localizable", "configuration.cells.compression_framing.caption")
public enum Value {
/// --compress
public static let compress = L10n.tr("Localizable", "configuration.cells.compression_framing.value.compress")
/// --comp-lzo
public static let lzo = L10n.tr("Localizable", "configuration.cells.compression_framing.value.lzo")
}
}
public enum DefaultGateway {
/// Default gateway
public static let caption = L10n.tr("Localizable", "configuration.cells.default_gateway.caption")
}
public enum Digest {
/// Authentication
public static let caption = L10n.tr("Localizable", "configuration.cells.digest.caption")
public enum Value {
/// Embedded
public static let embedded = L10n.tr("Localizable", "configuration.cells.digest.value.embedded")
}
}
public enum DnsDomain {
/// Domain
public static let caption = L10n.tr("Localizable", "configuration.cells.dns_domain.caption")
}
public enum DnsServer {
/// DNS
public static let caption = L10n.tr("Localizable", "configuration.cells.dns_server.caption")
}
public enum Eku {
/// Extended verification
public static let caption = L10n.tr("Localizable", "configuration.cells.eku.caption")
}
public enum KeepAlive {
/// Keep-alive
public static let caption = L10n.tr("Localizable", "configuration.cells.keep_alive.caption")
public enum Value {
/// %d seconds
public static func seconds(_ p1: Int) -> String {
return L10n.tr("Localizable", "configuration.cells.keep_alive.value.seconds", p1)
}
}
}
public enum ProxyHttp {
/// Proxy
public static let caption = L10n.tr("Localizable", "configuration.cells.proxy_http.caption")
}
public enum ProxyHttps {
/// Proxy (HTTPS)
public static let caption = L10n.tr("Localizable", "configuration.cells.proxy_https.caption")
}
public enum RandomEndpoint {
/// Randomize endpoint
public static let caption = L10n.tr("Localizable", "configuration.cells.random_endpoint.caption")
}
public enum RenegotiationSeconds {
/// Renegotiation
public static let caption = L10n.tr("Localizable", "configuration.cells.renegotiation_seconds.caption")
public enum Value {
/// after %@
public static func after(_ p1: String) -> String {
return L10n.tr("Localizable", "configuration.cells.renegotiation_seconds.value.after", p1)
}
}
}
public enum ResetOriginal {
/// Reset configuration
public static let caption = L10n.tr("Localizable", "configuration.cells.reset_original.caption")
}
public enum TlsWrapping {
/// Wrapping
public static let caption = L10n.tr("Localizable", "configuration.cells.tls_wrapping.caption")
public enum Value {
/// Authentication
public static let auth = L10n.tr("Localizable", "configuration.cells.tls_wrapping.value.auth")
/// Encryption
public static let crypt = L10n.tr("Localizable", "configuration.cells.tls_wrapping.value.crypt")
}
}
}
public enum Sections {
public enum Communication {
/// Communication
public static let header = L10n.tr("Localizable", "configuration.sections.communication.header")
}
public enum Compression {
/// Compression
public static let header = L10n.tr("Localizable", "configuration.sections.compression.header")
}
public enum Network {
/// Network
public static let header = L10n.tr("Localizable", "configuration.sections.network.header")
}
public enum Other {
/// Other
public static let header = L10n.tr("Localizable", "configuration.sections.other.header")
}
public enum Reset {
/// If you ended up with broken connectivity after changing the communication parameters, tap to revert to the original configuration.
public static let footer = L10n.tr("Localizable", "configuration.sections.reset.footer")
}
public enum Tls {
/// TLS
public static let header = L10n.tr("Localizable", "configuration.sections.tls.header")
}
}
}
public enum Credits {
/// Credits
public static let title = L10n.tr("Localizable", "credits.title")
public enum Sections {
public enum Licenses {
/// Licenses
public static let header = L10n.tr("Localizable", "credits.sections.licenses.header")
}
public enum Notices {
/// Notices
public static let header = L10n.tr("Localizable", "credits.sections.notices.header")
}
public enum Translations {
/// Translations
public static let header = L10n.tr("Localizable", "credits.sections.translations.header")
}
}
}
public enum DebugLog {
public enum Alerts {
public enum EmptyLog {
/// The debug log is empty.
public static let message = L10n.tr("Localizable", "debug_log.alerts.empty_log.message")
}
}
public enum Buttons {
/// Next
public static let next = L10n.tr("Localizable", "debug_log.buttons.next")
/// Previous
public static let previous = L10n.tr("Localizable", "debug_log.buttons.previous")
}
}
public enum Donation {
/// Donate
public static let title = L10n.tr("Localizable", "donation.title")
public enum Alerts {
public enum Purchase {
public enum Failure {
/// Unable to perform the donation. %@
public static func message(_ p1: String) -> String {
return L10n.tr("Localizable", "donation.alerts.purchase.failure.message", p1)
}
}
public enum Success {
/// This means a lot to me and I really hope you keep using and promoting this app.
public static let message = L10n.tr("Localizable", "donation.alerts.purchase.success.message")
/// Thank you
public static let title = L10n.tr("Localizable", "donation.alerts.purchase.success.title")
}
}
}
public enum Cells {
public enum Loading {
/// Loading donations
public static let caption = L10n.tr("Localizable", "donation.cells.loading.caption")
}
public enum Purchasing {
/// Performing donation
public static let caption = L10n.tr("Localizable", "donation.cells.purchasing.caption")
}
}
public enum Sections {
public enum OneTime {
/// If you want to display gratitude for my free work, here are a couple amounts you can donate instantly.\n\nYou will only be charged once per donation, and you can donate multiple times.
public static let footer = L10n.tr("Localizable", "donation.sections.one_time.footer")
/// One time
public static let header = L10n.tr("Localizable", "donation.sections.one_time.header")
}
}
}
public enum Endpoint {
public enum Cells {
public enum AnyAddress {
/// Automatic
public static let caption = L10n.tr("Localizable", "endpoint.cells.any_address.caption")
}
public enum AnyProtocol {
/// Automatic
public static let caption = L10n.tr("Localizable", "endpoint.cells.any_protocol.caption")
}
}
public enum Sections {
public enum LocationAddresses {
/// Addresses
public static let header = L10n.tr("Localizable", "endpoint.sections.location_addresses.header")
}
public enum LocationProtocols {
/// Protocols
public static let header = L10n.tr("Localizable", "endpoint.sections.location_protocols.header")
}
}
}
public enum Global {
/// Cancel
public static let cancel = L10n.tr("Localizable", "global.cancel")
/// Close
public static let close = L10n.tr("Localizable", "global.close")
/// No e-mail account is configured.
public static let emailNotConfigured = L10n.tr("Localizable", "global.email_not_configured")
/// Next
public static let next = L10n.tr("Localizable", "global.next")
/// OK
public static let ok = L10n.tr("Localizable", "global.ok")
public enum Cells {
/// Automatic
public static let automatic = L10n.tr("Localizable", "global.cells.automatic")
/// Disabled
public static let disabled = L10n.tr("Localizable", "global.cells.disabled")
/// Enabled
public static let enabled = L10n.tr("Localizable", "global.cells.enabled")
/// Manual
public static let manual = L10n.tr("Localizable", "global.cells.manual")
/// None
public static let `none` = L10n.tr("Localizable", "global.cells.none")
}
public enum Host {
public enum TitleInput {
/// Acceptable characters are alphanumerics plus dash "-", underscore "_" and dot ".".
public static let message = L10n.tr("Localizable", "global.host.title_input.message")
/// My profile
public static let placeholder = L10n.tr("Localizable", "global.host.title_input.placeholder")
}
}
}
public enum ImportedHosts {
/// Imported hosts
public static let title = L10n.tr("Localizable", "imported_hosts.title")
}
public enum IssueReporter {
/// The debug log of your latest connections is crucial to resolve your connectivity issues and is completely anonymous.\n\nThe .ovpn configuration file, if any, is attached stripped of any sensitive data.\n\nPlease double check the e-mail attachments if unsure.
public static let message = L10n.tr("Localizable", "issue_reporter.message")
/// Report issue
public static let title = L10n.tr("Localizable", "issue_reporter.title")
public enum Buttons {
/// I understand
public static let accept = L10n.tr("Localizable", "issue_reporter.buttons.accept")
}
}
public enum Label {
public enum License {
/// Unable to download full license content.
public static let error = L10n.tr("Localizable", "label.license.error")
}
}
public enum NetworkSettings {
public enum Cells {
public enum AddDnsServer {
/// Add address
public static let caption = L10n.tr("Localizable", "network_settings.cells.add_dns_server.caption")
}
public enum AddProxyBypass {
/// Add bypass domain
public static let caption = L10n.tr("Localizable", "network_settings.cells.add_proxy_bypass.caption")
}
public enum Address {
/// Address
public static let caption = L10n.tr("Localizable", "network_settings.cells.address.caption")
}
public enum Choice {
/// Read .ovpn
public static let client = L10n.tr("Localizable", "network_settings.cells.choice.client")
/// Pull from server
public static let server = L10n.tr("Localizable", "network_settings.cells.choice.server")
}
public enum Port {
/// Port
public static let caption = L10n.tr("Localizable", "network_settings.cells.port.caption")
}
public enum ProxyBypass {
/// Bypass domain
public static let caption = L10n.tr("Localizable", "network_settings.cells.proxy_bypass.caption")
}
}
}
public enum Organizer {
public enum Alerts {
public enum AddHost {
/// Open an URL to an .ovpn configuration file from Safari, Mail or another app to set up a host profile.\n\nYou can also import an .ovpn with iTunes File Sharing.
public static let message = L10n.tr("Localizable", "organizer.alerts.add_host.message")
}
public enum CannotDonate {
/// There is no payment method configured on this device.
public static let message = L10n.tr("Localizable", "organizer.alerts.cannot_donate.message")
}
public enum DeleteVpnProfile {
/// Do you really want to erase the VPN configuration from your device settings? This may fix some broken VPN states and will not affect your provider and host profiles.
public static let message = L10n.tr("Localizable", "organizer.alerts.delete_vpn_profile.message")
}
public enum ExhaustedProviders {
/// You have created profiles for any available provider.
public static let message = L10n.tr("Localizable", "organizer.alerts.exhausted_providers.message")
}
}
public enum Cells {
public enum About {
/// About %@
public static func caption(_ p1: String) -> String {
return L10n.tr("Localizable", "organizer.cells.about.caption", p1)
}
}
public enum AddHost {
/// Add new host
public static let caption = L10n.tr("Localizable", "organizer.cells.add_host.caption")
}
public enum AddProvider {
/// Add new provider
public static let caption = L10n.tr("Localizable", "organizer.cells.add_provider.caption")
}
public enum Donate {
/// Make a donation
public static let caption = L10n.tr("Localizable", "organizer.cells.donate.caption")
}
public enum JoinCommunity {
/// Join community
public static let caption = L10n.tr("Localizable", "organizer.cells.join_community.caption")
}
public enum Patreon {
/// Support me on Patreon
public static let caption = L10n.tr("Localizable", "organizer.cells.patreon.caption")
}
public enum Profile {
public enum Value {
/// In use
public static let current = L10n.tr("Localizable", "organizer.cells.profile.value.current")
}
}
public enum SiriShortcuts {
/// Manage shortcuts
public static let caption = L10n.tr("Localizable", "organizer.cells.siri_shortcuts.caption")
}
public enum Translate {
/// Offer to translate
public static let caption = L10n.tr("Localizable", "organizer.cells.translate.caption")
}
public enum Uninstall {
/// Remove VPN configuration
public static let caption = L10n.tr("Localizable", "organizer.cells.uninstall.caption")
}
public enum WriteReview {
/// Write a review
public static let caption = L10n.tr("Localizable", "organizer.cells.write_review.caption")
}
}
public enum Sections {
public enum Feedback {
/// Feedback
public static let header = L10n.tr("Localizable", "organizer.sections.feedback.header")
}
public enum Hosts {
/// Import hosts from raw .ovpn configuration files.
public static let footer = L10n.tr("Localizable", "organizer.sections.hosts.footer")
/// Hosts
public static let header = L10n.tr("Localizable", "organizer.sections.hosts.header")
}
public enum Providers {
/// Here you find a few providers with preset configuration profiles.
public static let footer = L10n.tr("Localizable", "organizer.sections.providers.footer")
/// Providers
public static let header = L10n.tr("Localizable", "organizer.sections.providers.header")
}
public enum Siri {
/// Get help from Siri to speed up your most common interactions with the app.
public static let footer = L10n.tr("Localizable", "organizer.sections.siri.footer")
/// Siri
public static let header = L10n.tr("Localizable", "organizer.sections.siri.header")
}
public enum Support {
/// Support
public static let header = L10n.tr("Localizable", "organizer.sections.support.header")
}
}
}
public enum ParsedFile {
public enum Alerts {
public enum Buttons {
/// Report an issue
public static let report = L10n.tr("Localizable", "parsed_file.alerts.buttons.report")
}
public enum Decryption {
/// The configuration contains an encrypted private key and it could not be decrypted. Double check your entered passphrase.
public static let message = L10n.tr("Localizable", "parsed_file.alerts.decryption.message")
}
public enum EncryptionPassphrase {
/// Please enter the encryption passphrase.
public static let message = L10n.tr("Localizable", "parsed_file.alerts.encryption_passphrase.message")
}
public enum Malformed {
/// The configuration file contains a malformed option (%@).
public static func message(_ p1: String) -> String {
return L10n.tr("Localizable", "parsed_file.alerts.malformed.message", p1)
}
}
public enum Missing {
/// The configuration file lacks a required option (%@).
public static func message(_ p1: String) -> String {
return L10n.tr("Localizable", "parsed_file.alerts.missing.message", p1)
}
}
public enum Parsing {
/// Unable to parse the provided configuration file (%@).
public static func message(_ p1: String) -> String {
return L10n.tr("Localizable", "parsed_file.alerts.parsing.message", p1)
}
}
public enum PotentiallyUnsupported {
/// The configuration file is correct but contains a potentially unsupported option (%@).\n\nConnectivity may break depending on server settings.
public static func message(_ p1: String) -> String {
return L10n.tr("Localizable", "parsed_file.alerts.potentially_unsupported.message", p1)
}
}
public enum Unsupported {
/// The configuration file contains an unsupported option (%@).
public static func message(_ p1: String) -> String {
return L10n.tr("Localizable", "parsed_file.alerts.unsupported.message", p1)
}
}
}
}
public enum Provider {
public enum Preset {
public enum Cells {
public enum TechDetails {
/// Technical details
public static let caption = L10n.tr("Localizable", "provider.preset.cells.tech_details.caption")
}
}
}
}
public enum Reddit {
/// Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project.
public static let message = L10n.tr("Localizable", "reddit.message")
/// Reddit
public static let title = L10n.tr("Localizable", "reddit.title")
public enum Buttons {
/// Don't ask again
public static let never = L10n.tr("Localizable", "reddit.buttons.never")
/// Remind me later
public static let remind = L10n.tr("Localizable", "reddit.buttons.remind")
/// Subscribe now!
public static let subscribe = L10n.tr("Localizable", "reddit.buttons.subscribe")
}
}
public enum Service {
public enum Alerts {
public enum Buttons {
/// Reconnect
public static let reconnect = L10n.tr("Localizable", "service.alerts.buttons.reconnect")
}
public enum CredentialsNeeded {
/// You need to enter account credentials first.
public static let message = L10n.tr("Localizable", "service.alerts.credentials_needed.message")
}
public enum Download {
/// Failed to download configuration files. %@
public static func failed(_ p1: String) -> String {
return L10n.tr("Localizable", "service.alerts.download.failed", p1)
}
/// %@ requires the download of additional configuration files.\n\nConfirm to start the download.
public static func message(_ p1: String) -> String {
return L10n.tr("Localizable", "service.alerts.download.message", p1)
}
/// Download required
public static let title = L10n.tr("Localizable", "service.alerts.download.title")
public enum Hud {
/// Extracting files, please be patient...
public static let extracting = L10n.tr("Localizable", "service.alerts.download.hud.extracting")
}
}
public enum MasksPrivateData {
public enum Messages {
/// In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.
public static let mustReconnect = L10n.tr("Localizable", "service.alerts.masks_private_data.messages.must_reconnect")
}
}
public enum ReconnectVpn {
/// Do you want to reconnect to the VPN?
public static let message = L10n.tr("Localizable", "service.alerts.reconnect_vpn.message")
}
public enum Rename {
/// Rename profile
public static let title = L10n.tr("Localizable", "service.alerts.rename.title")
}
public enum TestConnectivity {
/// Connectivity
public static let title = L10n.tr("Localizable", "service.alerts.test_connectivity.title")
public enum Messages {
/// Your device has no Internet connectivity, please review your profile parameters.
public static let failure = L10n.tr("Localizable", "service.alerts.test_connectivity.messages.failure")
/// Your device is connected to the Internet!
public static let success = L10n.tr("Localizable", "service.alerts.test_connectivity.messages.success")
}
}
public enum Trusted {
public enum NoNetwork {
/// You are not connected to any Wi-Fi network.
public static let message = L10n.tr("Localizable", "service.alerts.trusted.no_network.message")
}
public enum WillDisconnectPolicy {
/// By changing the trust policy, the VPN may be disconnected. Continue?
public static let message = L10n.tr("Localizable", "service.alerts.trusted.will_disconnect_policy.message")
}
public enum WillDisconnectTrusted {
/// By trusting this network, the VPN may be disconnected. Continue?
public static let message = L10n.tr("Localizable", "service.alerts.trusted.will_disconnect_trusted.message")
}
}
}
public enum Cells {
public enum Account {
/// Account
public static let caption = L10n.tr("Localizable", "service.cells.account.caption")
/// None configured
public static let `none` = L10n.tr("Localizable", "service.cells.account.none")
}
public enum ConnectionStatus {
/// Status
public static let caption = L10n.tr("Localizable", "service.cells.connection_status.caption")
}
public enum DataCount {
/// Exchanged data
public static let caption = L10n.tr("Localizable", "service.cells.data_count.caption")
/// Unavailable
public static let `none` = L10n.tr("Localizable", "service.cells.data_count.none")
}
public enum DebugLog {
/// Debug log
public static let caption = L10n.tr("Localizable", "service.cells.debug_log.caption")
}
public enum Endpoint {
/// Endpoint
public static let caption = L10n.tr("Localizable", "service.cells.endpoint.caption")
}
public enum Host {
public enum Parameters {
/// Parameters
public static let caption = L10n.tr("Localizable", "service.cells.host.parameters.caption")
}
}
public enum MasksPrivateData {
/// Mask network data
public static let caption = L10n.tr("Localizable", "service.cells.masks_private_data.caption")
}
public enum NetworkSettings {
/// Network settings
public static let caption = L10n.tr("Localizable", "service.cells.network_settings.caption")
}
public enum Provider {
public enum Pool {
/// Location
public static let caption = L10n.tr("Localizable", "service.cells.provider.pool.caption")
}
public enum Preset {
/// Preset
public static let caption = L10n.tr("Localizable", "service.cells.provider.preset.caption")
}
public enum Refresh {
/// Refresh infrastructure
public static let caption = L10n.tr("Localizable", "service.cells.provider.refresh.caption")
}
}
public enum Reconnect {
/// Reconnect
public static let caption = L10n.tr("Localizable", "service.cells.reconnect.caption")
}
public enum ReportIssue {
/// Report connectivity issue
public static let caption = L10n.tr("Localizable", "service.cells.report_issue.caption")
}
public enum TestConnectivity {
/// Test connectivity
public static let caption = L10n.tr("Localizable", "service.cells.test_connectivity.caption")
}
public enum TrustedAddWifi {
/// Add current Wi-Fi
public static let caption = L10n.tr("Localizable", "service.cells.trusted_add_wifi.caption")
}
public enum TrustedMobile {
/// Cellular network
public static let caption = L10n.tr("Localizable", "service.cells.trusted_mobile.caption")
}
public enum TrustedPolicy {
/// Trust disables VPN
public static let caption = L10n.tr("Localizable", "service.cells.trusted_policy.caption")
}
public enum UseProfile {
/// Use this profile
public static let caption = L10n.tr("Localizable", "service.cells.use_profile.caption")
}
public enum VpnResolvesHostname {
/// Resolve server hostname
public static let caption = L10n.tr("Localizable", "service.cells.vpn_resolves_hostname.caption")
}
public enum VpnService {
/// Enabled
public static let caption = L10n.tr("Localizable", "service.cells.vpn_service.caption")
}
public enum VpnSurvivesSleep {
/// Keep alive on sleep
public static let caption = L10n.tr("Localizable", "service.cells.vpn_survives_sleep.caption")
}
}
public enum Sections {
public enum Configuration {
/// Configuration
public static let header = L10n.tr("Localizable", "service.sections.configuration.header")
}
public enum Diagnostics {
/// Masking status will be effective after reconnecting. Network data are hostnames, IP addresses, routing, SSID. Credentials and private keys are not logged regardless.
public static let footer = L10n.tr("Localizable", "service.sections.diagnostics.footer")
/// Diagnostics
public static let header = L10n.tr("Localizable", "service.sections.diagnostics.header")
}
public enum General {
/// General
public static let header = L10n.tr("Localizable", "service.sections.general.header")
}
public enum ProviderInfrastructure {
/// Last updated on %@.
public static func footer(_ p1: String) -> String {
return L10n.tr("Localizable", "service.sections.provider_infrastructure.footer", p1)
}
}
public enum Status {
/// Connection
public static let header = L10n.tr("Localizable", "service.sections.status.header")
}
public enum Trusted {
/// When entering a trusted network, the VPN is normally shut down and kept disconnected. Disable this option to not enforce such behavior.
public static let footer = L10n.tr("Localizable", "service.sections.trusted.footer")
/// Trusted networks
public static let header = L10n.tr("Localizable", "service.sections.trusted.header")
}
public enum Vpn {
/// The connection will be established whenever necessary.
public static let footer = L10n.tr("Localizable", "service.sections.vpn.footer")
/// VPN
public static let header = L10n.tr("Localizable", "service.sections.vpn.header")
}
public enum VpnResolvesHostname {
/// Preferred in most networks and required in some IPv6 networks. Disable where DNS is blocked, or to speed up negotiation when DNS is slow to respond.
public static let footer = L10n.tr("Localizable", "service.sections.vpn_resolves_hostname.footer")
}
public enum VpnSurvivesSleep {
/// Disable to improve battery usage, at the expense of occasional slowdowns due to wake-up reconnections.
public static let footer = L10n.tr("Localizable", "service.sections.vpn_survives_sleep.footer")
}
}
public enum Welcome {
/// Welcome to Passepartout!\n\nUse the organizer to add a new profile.
public static let message = L10n.tr("Localizable", "service.welcome.message")
}
}
public enum Share {
/// Passepartout is an user-friendly, open source OpenVPN client for iOS and macOS
public static let message = L10n.tr("Localizable", "share.message")
}
public enum Shortcuts {
public enum Add {
/// Add shortcut
public static let title = L10n.tr("Localizable", "shortcuts.add.title")
public enum Alerts {
public enum NoProfiles {
/// There is no profile to connect to.
public static let message = L10n.tr("Localizable", "shortcuts.add.alerts.no_profiles.message")
}
}
public enum Cells {
public enum Connect {
/// Connect to
public static let caption = L10n.tr("Localizable", "shortcuts.add.cells.connect.caption")
}
public enum DisableVpn {
/// Disable VPN
public static let caption = L10n.tr("Localizable", "shortcuts.add.cells.disable_vpn.caption")
}
public enum EnableVpn {
/// Enable VPN
public static let caption = L10n.tr("Localizable", "shortcuts.add.cells.enable_vpn.caption")
}
public enum TrustCellular {
/// Trust cellular network
public static let caption = L10n.tr("Localizable", "shortcuts.add.cells.trust_cellular.caption")
}
public enum TrustCurrentWifi {
/// Trust current Wi-Fi
public static let caption = L10n.tr("Localizable", "shortcuts.add.cells.trust_current_wifi.caption")
}
public enum UntrustCellular {
/// Untrust cellular network
public static let caption = L10n.tr("Localizable", "shortcuts.add.cells.untrust_cellular.caption")
}
public enum UntrustCurrentWifi {
/// Untrust current Wi-Fi
public static let caption = L10n.tr("Localizable", "shortcuts.add.cells.untrust_current_wifi.caption")
}
}
public enum Sections {
public enum Cellular {
/// Cellular
public static let header = L10n.tr("Localizable", "shortcuts.add.sections.cellular.header")
}
public enum Vpn {
/// VPN
public static let header = L10n.tr("Localizable", "shortcuts.add.sections.vpn.header")
}
public enum Wifi {
/// Wi-Fi
public static let header = L10n.tr("Localizable", "shortcuts.add.sections.wifi.header")
}
}
}
public enum Edit {
/// Manage shortcuts
public static let title = L10n.tr("Localizable", "shortcuts.edit.title")
public enum Cells {
public enum AddShortcut {
/// Add shortcut
public static let caption = L10n.tr("Localizable", "shortcuts.edit.cells.add_shortcut.caption")
}
}
public enum Sections {
public enum All {
/// Existing shortcuts
public static let header = L10n.tr("Localizable", "shortcuts.edit.sections.all.header")
}
}
}
}
public enum Translations {
/// Translations
public static let title = L10n.tr("Localizable", "translations.title")
}
public enum Version {
/// Version
public static let title = L10n.tr("Localizable", "version.title")
public enum Labels {
/// Passepartout and TunnelKit are written and maintained by Davide De Rosa (keeshux).\n\nSource code for Passepartout and TunnelKit is publicly available on GitHub under the GPLv3, you can find links in the home page.\n\nPassepartout is a non-official client and is in no way affiliated with OpenVPN Inc.
public static let intro = L10n.tr("Localizable", "version.labels.intro")
}
}
public enum Vpn {
/// Active
public static let active = L10n.tr("Localizable", "vpn.active")
/// Connecting
public static let connecting = L10n.tr("Localizable", "vpn.connecting")
/// Disabled
public static let disabled = L10n.tr("Localizable", "vpn.disabled")
/// Disconnecting
public static let disconnecting = L10n.tr("Localizable", "vpn.disconnecting")
/// Inactive
public static let inactive = L10n.tr("Localizable", "vpn.inactive")
public enum Errors {
/// Auth failed
public static let auth = L10n.tr("Localizable", "vpn.errors.auth")
/// Compression unsupported
public static let compression = L10n.tr("Localizable", "vpn.errors.compression")
/// DNS failed
public static let dns = L10n.tr("Localizable", "vpn.errors.dns")
/// Encryption failed
public static let encryption = L10n.tr("Localizable", "vpn.errors.encryption")
/// No gateway
public static let gateway = L10n.tr("Localizable", "vpn.errors.gateway")
/// Network changed
public static let network = L10n.tr("Localizable", "vpn.errors.network")
/// Missing routing
public static let routing = L10n.tr("Localizable", "vpn.errors.routing")
/// Timeout
public static let timeout = L10n.tr("Localizable", "vpn.errors.timeout")
/// TLS failed
public static let tls = L10n.tr("Localizable", "vpn.errors.tls")
}
}
public enum Wizards {
public enum Host {
public enum Alerts {
public enum Existing {
/// A host profile with the same title already exists. Replace it?
public static let message = L10n.tr("Localizable", "wizards.host.alerts.existing.message")
}
}
public enum Cells {
public enum TitleInput {
/// Title
public static let caption = L10n.tr("Localizable", "wizards.host.cells.title_input.caption")
}
}
public enum Sections {
public enum Existing {
/// Existing profiles
public static let header = L10n.tr("Localizable", "wizards.host.sections.existing.header")
}
}
}
}
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:enable nesting type_body_length type_name
// MARK: - Implementation Details
extension L10n {
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
// swiftlint:disable:next nslocalizedstring_key
let format = NSLocalizedString(key, tableName: table, bundle: Bundle(for: BundleToken.self), comment: "")
return String(format: format, locale: Locale.current, arguments: args)
}
}
private final class BundleToken {}

View File

@ -1,242 +0,0 @@
//
// Utils.swift
// Passepartout
//
// Created by Davide De Rosa on 6/16/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
#if os(iOS)
import SystemConfiguration.CaptiveNetwork
#else
import CoreWLAN
#endif
import SwiftyBeaver
private let log = SwiftyBeaver.self
public class Utils {
fileprivate static let timestampFormatter: DateFormatter = {
let fmt = DateFormatter()
fmt.dateStyle = .medium
fmt.timeStyle = .medium
return fmt
}()
fileprivate static let componentsFormatter: DateComponentsFormatter = {
let fmt = DateComponentsFormatter()
fmt.unitsStyle = .full
return fmt
}()
public static func versionString() -> String {
let info = Bundle.main.infoDictionary
guard let version = info?["CFBundleShortVersionString"] else {
fatalError("No bundle version?")
}
guard let build = info?["CFBundleVersion"] else {
fatalError("No bundle build number?")
}
return "\(version) (\(build))"
}
#if targetEnvironment(simulator)
public static func hasCellularData() -> Bool {
return true
}
#else
public static func hasCellularData() -> Bool {
var addrs: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&addrs) == 0 else {
return false
}
var isFound = false
var cursor = addrs?.pointee
while let ifa = cursor {
let name = String(cString: ifa.ifa_name)
if name == "pdp_ip0" {
isFound = true
break
}
cursor = ifa.ifa_next?.pointee
}
freeifaddrs(addrs)
return isFound
}
#endif
#if targetEnvironment(simulator)
public static func currentWifiNetworkName() -> String? {
// return nil
return ["FOO", "BAR", "WIFI"].customRandomElement()
}
#else
public static func currentWifiNetworkName() -> String? {
#if os(iOS)
guard let interfaceNames = CNCopySupportedInterfaces() as? [CFString] else {
return nil
}
for name in interfaceNames {
guard let iface = CNCopyCurrentNetworkInfo(name) as? [String: Any] else {
continue
}
if let ssid = iface["SSID"] as? String {
return ssid
}
}
return nil
#else
return CWWiFiClient.shared().interface()?.ssid()
#endif
}
#endif
public static func regex(_ pattern: String) -> NSRegularExpression {
return try! NSRegularExpression(pattern: pattern, options: [])
}
public static func checkConnectivityURL(_ url: URL, timeout: TimeInterval, completionHandler: @escaping (Bool) -> Void) {
let session = URLSession(configuration: .ephemeral)
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: timeout)
log.info("Check connectivity via \(url)")
session.dataTask(with: request) { (_, response, error) in
if let response = response as? HTTPURLResponse {
log.debug("Response code: \(response.statusCode)")
}
if let error = error {
log.error("Connectivity failed: \(error)")
} else {
log.info("Connectivity succeeded!")
}
DispatchQueue.main.async {
completionHandler(error == nil)
}
}.resume()
}
public static func localizedCountry(_ code: String) -> String {
return Locale.current.localizedString(forRegionCode: code) ?? code
}
public static func localizedLanguage(_ code: String) -> String {
return Locale.current.localizedString(forLanguageCode: code)?.capitalized ?? code
}
private init() {
}
}
public extension FileManager {
func userURL(for searchPath: SearchPathDirectory, appending: String?) -> URL {
let paths = urls(for: .documentDirectory, in: .userDomainMask)
var directory = paths[0]
if let appending = appending {
directory.appendPathComponent(appending)
}
return directory
}
func modificationDate(of path: String) -> Date? {
guard let attrs = try? attributesOfItem(atPath: path) else {
return nil
}
return attrs[.modificationDate] as? Date
}
}
public extension Date {
var timestamp: String {
return Utils.timestampFormatter.string(from: self)
}
}
public extension TimeInterval {
var localized: String {
guard let str = Utils.componentsFormatter.string(from: self) else {
fatalError("Could not format a TimeInterval?")
}
return str
}
}
public extension Sequence {
func stableSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] {
return try enumerated().sorted {
return try areInIncreasingOrder($0.element, $1.element) ||
($0.offset < $1.offset && !areInIncreasingOrder($1.element, $0.element))
}.map { $0.element }
}
}
public extension Array {
func customRandomElement() -> Element {
let i = Int(arc4random() % UInt32(count))
return self[i]
}
}
public extension StringProtocol where Index == String.Index {
func nsRange(from range: Range<Index>) -> NSRange {
return NSRange(range, in: self)
}
}
public extension CharacterSet {
static let filename: CharacterSet = {
var chars: CharacterSet = .decimalDigits
let english = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let symbols = "-_."
chars.formUnion(CharacterSet(charactersIn: english))
chars.formUnion(CharacterSet(charactersIn: english.lowercased()))
chars.formUnion(CharacterSet(charactersIn: symbols))
return chars
}()
}
public extension URL {
private static let illegalCharacterFallback = "_"
var normalizedFilename: String {
let filename = deletingPathExtension().lastPathComponent
return filename.components(separatedBy: CharacterSet.filename.inverted).joined(separator: URL.illegalCharacterFallback)
}
}
public extension Array where Element: CustomStringConvertible {
func sortedCaseInsensitive() -> [Element] {
return sorted { $0.description.lowercased() < $1.description.lowercased() }
}
}
public extension UITableView {
func scrollToRowAsync(at indexPath: IndexPath) {
DispatchQueue.main.async { [weak self] in
self?.scrollToRow(at: indexPath, at: .middle, animated: false)
}
}
func selectRowAsync(at indexPath: IndexPath) {
DispatchQueue.main.async { [weak self] in
self?.selectRow(at: indexPath, animated: false, scrollPosition: .middle)
}
}
}

View File

@ -1,139 +0,0 @@
//
// GracefulVPN.swift
// Passepartout
//
// Created by Davide De Rosa on 9/18/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import SwiftyBeaver
private let log = SwiftyBeaver.self
public class GracefulVPN {
private let service: ConnectionService
public var profile: ConnectionProfile?
private var vpn: VPNProvider? {
guard let profile = profile else {
return nil
}
guard service.isActiveProfile(profile) else {
return nil
}
return VPN.shared
}
public var isEnabled: Bool {
return vpn?.isEnabled ?? false
}
public var status: VPNStatus? {
return vpn?.status
}
public init(service: ConnectionService) {
self.service = service
}
public func prepare(completionHandler: (() -> Void)?) {
service.clearVpnLastError()
guard let vpn = vpn else {
completionHandler?()
return
}
log.info("Preparing...")
vpn.prepare(completionHandler: completionHandler)
}
public func reconnect(completionHandler: ((Error?) -> Void)?) {
service.clearVpnLastError()
guard let vpn = vpn else {
completionHandler?(ApplicationError.inactiveProfile)
return
}
do {
log.info("Reconnecting...")
try vpn.reconnect(configuration: service.vpnConfiguration(), completionHandler: completionHandler)
} catch let e {
guard e as? ApplicationError != .externalResources else {
completionHandler?(e)
return
}
log.error("Could not reconnect: \(e)")
}
}
public func reinstall(completionHandler: ((Error?) -> Void)?) {
service.clearVpnLastError()
guard let vpn = vpn else {
completionHandler?(ApplicationError.inactiveProfile)
return
}
do {
log.info("Reinstalling...")
try vpn.install(configuration: service.vpnConfiguration(), completionHandler: completionHandler)
} catch let e {
guard e as? ApplicationError != .externalResources else {
completionHandler?(e)
return
}
log.error("Could not reinstall: \(e)")
}
}
public func reinstallIfEnabled() {
guard isEnabled else {
log.warning("Not reinstalling (VPN is disabled)")
return
}
if status != .disconnected {
reconnect(completionHandler: nil)
} else {
reinstall(completionHandler: nil)
}
}
public func disconnect(completionHandler: ((Error?) -> Void)?) {
guard let vpn = vpn else {
completionHandler?(ApplicationError.inactiveProfile)
return
}
vpn.disconnect(completionHandler: completionHandler)
}
public func uninstall(completionHandler: (() -> Void)?) {
guard let vpn = vpn else {
completionHandler?()
return
}
vpn.uninstall(completionHandler: completionHandler)
}
public func requestBytesCount(completionHandler: @escaping ((UInt, UInt)?) -> Void) {
guard let vpn = vpn else {
completionHandler(nil)
return
}
vpn.requestBytesCount(completionHandler: completionHandler)
}
}

View File

@ -1,81 +0,0 @@
//
// MockVPNProvider.swift
// Passepartout
//
// Created by Davide De Rosa on 6/15/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public class MockVPNProvider: VPNProvider {
public let isPrepared: Bool = true
public private(set) var isEnabled: Bool = false
public private(set) var status: VPNStatus = .disconnected
public func prepare(completionHandler: (() -> Void)?) {
NotificationCenter.default.post(name: .VPNDidPrepare, object: nil)
completionHandler?()
}
public func install(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
isEnabled = true
completionHandler?(nil)
}
public func connect(completionHandler: ((Error?) -> Void)?) {
isEnabled = true
status = .connected
NotificationCenter.default.post(name: .VPNDidChangeStatus, object: self)
completionHandler?(nil)
}
public func disconnect(completionHandler: ((Error?) -> Void)?) {
isEnabled = false
status = .disconnected
NotificationCenter.default.post(name: .VPNDidChangeStatus, object: self)
completionHandler?(nil)
}
public func reconnect(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
isEnabled = true
status = .connected
NotificationCenter.default.post(name: .VPNDidChangeStatus, object: self)
completionHandler?(nil)
}
public func uninstall(completionHandler: (() -> Void)?) {
isEnabled = false
status = .disconnected
NotificationCenter.default.post(name: .VPNDidChangeStatus, object: self)
completionHandler?()
}
public func requestDebugLog(fallback: (() -> String)?, completionHandler: @escaping (String) -> Void) {
let log = [String](repeating: "lorem ipsum", count: 1000).joined(separator: " ")
completionHandler(log)
}
public func requestBytesCount(completionHandler: @escaping ((UInt, UInt)?) -> Void) {
completionHandler((0, 0))
}
}

View File

@ -1,288 +0,0 @@
//
// StandardVPNProvider.swift
// Passepartout
//
// Created by Davide De Rosa on 6/15/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import NetworkExtension
import TunnelKit
public class StandardVPNProvider: VPNProvider {
private let bundleIdentifier: String
private var manager: NETunnelProviderManager?
private var lastNotifiedStatus: VPNStatus?
public init(bundleIdentifier: String) {
self.bundleIdentifier = bundleIdentifier
let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(vpnDidUpdate(_:)), name: .NEVPNStatusDidChange, object: nil)
nc.addObserver(self, selector: #selector(vpnDidReinstall(_:)), name: .NEVPNConfigurationChange, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: VPNProvider
public var isPrepared: Bool {
return manager != nil
}
public var isEnabled: Bool {
guard let manager = manager else {
return false
}
return manager.isEnabled && manager.isOnDemandEnabled
}
public var status: VPNStatus {
guard let neStatus = manager?.connection.status else {
return .disconnected
}
switch neStatus {
case .connected:
return .connected
case .connecting, .reasserting:
return .connecting
case .disconnecting:
return .disconnecting
case .disconnected, .invalid:
return .disconnected
@unknown default:
return .disconnected
}
}
public func prepare(completionHandler: (() -> Void)?) {
find(with: bundleIdentifier) {
self.manager = $0
NotificationCenter.default.post(name: .VPNDidPrepare, object: nil)
completionHandler?()
}
}
public func install(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
guard let configuration = configuration as? NetworkExtensionVPNConfiguration else {
fatalError("Not a NetworkExtensionVPNConfiguration")
}
find(with: bundleIdentifier) {
guard let manager = $0 else {
completionHandler?(nil)
return
}
self.manager = manager
manager.protocolConfiguration = configuration.protocolConfiguration
manager.onDemandRules = configuration.onDemandRules
manager.isOnDemandEnabled = true
manager.isEnabled = true
manager.saveToPreferences { (error) in
guard error == nil else {
manager.isOnDemandEnabled = false
manager.isEnabled = false
completionHandler?(error)
return
}
manager.loadFromPreferences { (error) in
completionHandler?(error)
}
}
}
}
public func connect(completionHandler: ((Error?) -> Void)?) {
do {
try manager?.connection.startVPNTunnel()
completionHandler?(nil)
} catch let e {
completionHandler?(e)
}
}
public func disconnect(completionHandler: ((Error?) -> Void)?) {
guard let manager = manager else {
completionHandler?(nil)
return
}
manager.connection.stopVPNTunnel()
manager.isOnDemandEnabled = false
manager.isEnabled = false
manager.saveToPreferences(completionHandler: completionHandler)
}
public func reconnect(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
guard let configuration = configuration as? NetworkExtensionVPNConfiguration else {
fatalError("Not a NetworkExtensionVPNConfiguration")
}
install(configuration: configuration) { (error) in
guard error == nil else {
completionHandler?(nil)
return
}
let connectBlock = {
self.connect(completionHandler: completionHandler)
}
if self.status != .disconnected {
self.manager?.connection.stopVPNTunnel()
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: connectBlock)
} else {
connectBlock()
}
}
}
public func uninstall(completionHandler: (() -> Void)?) {
find(with: bundleIdentifier) { (manager) in
guard let manager = manager else {
completionHandler?()
return
}
manager.connection.stopVPNTunnel()
manager.removeFromPreferences { (error) in
self.manager = nil
completionHandler?()
}
}
}
public func requestDebugLog(fallback: (() -> String)?, completionHandler: @escaping (String) -> Void) {
guard status != .disconnected else {
completionHandler(fallback?() ?? "")
return
}
findAndRequestDebugLog { (recent) in
DispatchQueue.main.async {
guard let recent = recent else {
completionHandler(fallback?() ?? "")
return
}
completionHandler(recent)
}
}
}
public func requestBytesCount(completionHandler: @escaping ((UInt, UInt)?) -> Void) {
find(with: bundleIdentifier) {
self.manager = $0
guard let session = self.manager?.connection as? NETunnelProviderSession else {
DispatchQueue.main.async {
completionHandler(nil)
}
return
}
do {
try session.sendProviderMessage(OpenVPNTunnelProvider.Message.dataCount.data) { (data) in
guard let data = data, data.count == 16 else {
DispatchQueue.main.async {
completionHandler(nil)
}
return
}
let bytesIn: UInt = data.subdata(in: 0..<8).withUnsafeBytes { $0.load(as: UInt.self) }
let bytesOut: UInt = data.subdata(in: 8..<16).withUnsafeBytes { $0.load(as: UInt.self) }
DispatchQueue.main.async {
completionHandler((bytesIn, bytesOut))
}
}
} catch {
DispatchQueue.main.async {
completionHandler(nil)
}
}
}
}
// MARK: Helpers
private func find(with bundleIdentifier: String, completionHandler: @escaping (NETunnelProviderManager?) -> Void) {
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
guard error == nil else {
completionHandler(nil)
return
}
let manager = managers?.first {
guard let ptm = $0.protocolConfiguration as? NETunnelProviderProtocol else {
return false
}
return (ptm.providerBundleIdentifier == bundleIdentifier)
}
completionHandler(manager ?? NETunnelProviderManager())
}
}
private func findAndRequestDebugLog(completionHandler: @escaping (String?) -> Void) {
find(with: bundleIdentifier) {
self.manager = $0
guard let session = self.manager?.connection as? NETunnelProviderSession else {
completionHandler(nil)
return
}
StandardVPNProvider.requestDebugLog(session: session, completionHandler: completionHandler)
}
}
private static func requestDebugLog(session: NETunnelProviderSession, completionHandler: @escaping (String?) -> Void) {
do {
try session.sendProviderMessage(OpenVPNTunnelProvider.Message.requestLog.data) { (data) in
guard let data = data, !data.isEmpty else {
completionHandler(nil)
return
}
let newestLog = String(data: data, encoding: .utf8)
completionHandler(newestLog)
}
} catch {
completionHandler(nil)
}
}
// MARK: Notifications
@objc private func vpnDidUpdate(_ notification: Notification) {
// guard let connection = notification.object as? NETunnelProviderSession else {
// return
// }
// log.debug("VPN status did change: \(connection.status.rawValue)")
let status = self.status
if let last = lastNotifiedStatus {
guard status != last else {
return
}
}
lastNotifiedStatus = status
NotificationCenter.default.post(name: .VPNDidChangeStatus, object: self)
}
@objc private func vpnDidReinstall(_ notification: Notification) {
NotificationCenter.default.post(name: .VPNDidReinstall, object: self)
}
}

View File

@ -1,34 +0,0 @@
//
// VPN.swift
// Passepartout
//
// Created by Davide De Rosa on 6/12/18.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public class VPN {
#if targetEnvironment(simulator)
public static let shared = MockVPNProvider()
#else
public static let shared = StandardVPNProvider(bundleIdentifier: AppConstants.App.tunnelBundleId)
#endif
}

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