]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
iOS: Keep track of most-recently-activated tunnels
authorRoopesh Chander <roop@roopc.net>
Tue, 30 Apr 2019 10:08:38 +0000 (15:38 +0530)
committerJason A. Donenfeld <Jason@zx2c4.com>
Sat, 25 May 2019 11:24:01 +0000 (13:24 +0200)
Signed-off-by: Roopesh Chander <roop@roopc.net>
WireGuard/WireGuard.xcodeproj/project.pbxproj
WireGuard/WireGuard/Tunnel/TunnelsManager.swift
WireGuard/WireGuard/UI/iOS/RecentTunnelsTracker.swift [new file with mode: 0644]

index ead155671f35a4358d08e453e01c4499340e87f8..d499929456d32f10d6dfcae19fdd46aeaaf61755 100644 (file)
@@ -54,6 +54,7 @@
                6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; };
                6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
                6F3E02E9228000F6001FE7E3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3E02E8228000F6001FE7E3 /* MainMenu.swift */; };
+               6F29A9432278518D00DC6A6B /* RecentTunnelsTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */; };
                6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; };
                6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
                6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
                6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = "<group>"; };
                6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
                6F3E02E8228000F6001FE7E3 /* MainMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
+               6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentTunnelsTracker.swift; sourceTree = "<group>"; };
                6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
                6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
                6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
                                6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */,
                                6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */,
                                5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */,
+                               6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */,
                                6FF4AC23211EC472002C96EB /* Info.plist */,
                                6FF4AC482120B9E0002C96EB /* WireGuard.entitlements */,
                        );
                                6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */,
                                6F7774E82172020C006A79B3 /* TunnelConfiguration.swift in Sources */,
                                6FDEF7FB21863B6100D8FBF6 /* unzip.c in Sources */,
+                               6F29A9432278518D00DC6A6B /* RecentTunnelsTracker.swift in Sources */,
                                6F6899A8218044FC0012E523 /* Curve25519.swift in Sources */,
                                6F0F44C9222D55BB00B0FF04 /* TextCell.swift in Sources */,
                                5F4541A021C2D6B700994C13 /* TunnelListCell.swift in Sources */,
index edf769063a91a48f0eeae83849c23d6c1c3ba02a..460a9f6ccc9186d9234e11143de284cd684138ad 100644 (file)
@@ -46,7 +46,11 @@ class TunnelsManager {
 
             var tunnelManagers = managers ?? []
             var refs: Set<Data> = []
+            var tunnelNames: Set<String> = []
             for (index, tunnelManager) in tunnelManagers.enumerated().reversed() {
+                if let tunnelName = tunnelManager.localizedDescription {
+                    tunnelNames.insert(tunnelName)
+                }
                 guard let proto = tunnelManager.protocolConfiguration as? NETunnelProviderProtocol else { continue }
                 if proto.migrateConfigurationIfNeeded(called: tunnelManager.localizedDescription ?? "unknown") {
                     tunnelManager.saveToPreferences { _ in }
@@ -66,6 +70,7 @@ class TunnelsManager {
                 }
             }
             Keychain.deleteReferences(except: refs)
+            RecentTunnelsTracker.cleanupTunnels(except: tunnelNames)
             completionHandler(.success(TunnelsManager(tunnelProviders: tunnelManagers)))
         }
         #endif
@@ -188,7 +193,8 @@ class TunnelsManager {
         }
 
         let tunnelProviderManager = tunnel.tunnelProvider
-        let isNameChanged = tunnelName != tunnelProviderManager.localizedDescription
+        let oldName = tunnelProviderManager.localizedDescription ?? ""
+        let isNameChanged = tunnelName != oldName
         if isNameChanged {
             guard !tunnels.contains(where: { $0.name == tunnelName }) else {
                 completionHandler(TunnelsManagerError.tunnelAlreadyExistsWithThatName)
@@ -220,6 +226,9 @@ class TunnelsManager {
                 self.tunnels.sort { TunnelsManager.tunnelNameIsLessThan($0.name, $1.name) }
                 let newIndex = self.tunnels.firstIndex(of: tunnel)!
                 self.tunnelsListDelegate?.tunnelMoved(from: oldIndex, to: newIndex)
+                #if os(iOS)
+                RecentTunnelsTracker.handleTunnelRenamed(oldName: oldName, newName: tunnelName)
+                #endif
             }
             self.tunnelsListDelegate?.tunnelModified(at: self.tunnels.firstIndex(of: tunnel)!)
 
@@ -266,6 +275,10 @@ class TunnelsManager {
                 self.tunnelsListDelegate?.tunnelRemoved(at: index, tunnel: tunnel)
             }
             completionHandler(nil)
+
+            #if os(iOS)
+            RecentTunnelsTracker.handleTunnelRemoved(tunnelName: tunnel.name)
+            #endif
         }
     }
 
@@ -343,6 +356,10 @@ class TunnelsManager {
         #else
         tunnel.startActivation(activationDelegate: activationDelegate)
         #endif
+
+        #if os(iOS)
+        RecentTunnelsTracker.handleTunnelActivated(tunnelName: tunnel.name)
+        #endif
     }
 
     func startDeactivation(of tunnel: TunnelContainer) {
diff --git a/WireGuard/WireGuard/UI/iOS/RecentTunnelsTracker.swift b/WireGuard/WireGuard/UI/iOS/RecentTunnelsTracker.swift
new file mode 100644 (file)
index 0000000..787a624
--- /dev/null
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+class RecentTunnelsTracker {
+
+    private static let keyRecentlyActivatedTunnelNames = "recentlyActivatedTunnelNames"
+    private static let maxNumberOfTunnels = 10
+
+    private static var userDefaults: UserDefaults? {
+        guard let appGroupId = FileManager.appGroupId else {
+            wg_log(.error, staticMessage: "Cannot obtain app group ID from bundle for tracking recently used tunnels")
+            return nil
+        }
+        guard let userDefaults = UserDefaults(suiteName: appGroupId) else {
+            wg_log(.error, staticMessage: "Cannot obtain shared user defaults for tracking recently used tunnels")
+            return nil
+        }
+        return userDefaults
+    }
+
+    static func handleTunnelActivated(tunnelName: String) {
+        guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
+        var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
+        if let existingIndex = recentTunnels.firstIndex(of: tunnelName) {
+            recentTunnels.remove(at: existingIndex)
+        }
+        recentTunnels.insert(tunnelName, at: 0)
+        if recentTunnels.count > maxNumberOfTunnels {
+            recentTunnels.removeLast(recentTunnels.count - maxNumberOfTunnels)
+        }
+        userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
+    }
+
+    static func handleTunnelRemoved(tunnelName: String) {
+        guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
+        var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
+        if let existingIndex = recentTunnels.firstIndex(of: tunnelName) {
+            recentTunnels.remove(at: existingIndex)
+            userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
+        }
+    }
+
+    static func handleTunnelRenamed(oldName: String, newName: String) {
+        guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
+        var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
+        if let existingIndex = recentTunnels.firstIndex(of: oldName) {
+            recentTunnels[existingIndex] = newName
+            userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
+        }
+    }
+
+    static func cleanupTunnels(except tunnelNamesToKeep: Set<String>) {
+        guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
+        var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
+        let oldCount = recentTunnels.count
+        recentTunnels.removeAll { !tunnelNamesToKeep.contains($0) }
+        if oldCount != recentTunnels.count {
+            userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
+        }
+    }
+
+    static func recentlyActivatedTunnelNames(limit: Int) -> [String] {
+        guard let userDefaults = RecentTunnelsTracker.userDefaults else { return [] }
+        var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
+        if limit < recentTunnels.count {
+            recentTunnels.removeLast(recentTunnels.count - limit)
+        }
+        return recentTunnels
+    }
+}