diff --git a/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj b/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj index 3c2f1ff7..e6dd75bf 100644 --- a/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj +++ b/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -58,6 +58,7 @@ 78EC227225DBC8CE0042B775 /* Accessory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78EC227125DBC8CE0042B775 /* Accessory.swift */; }; 78EC227725DBDB7E0042B775 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78EC227625DBDB7E0042B775 /* KeychainController.swift */; }; 78F8BB4C261C50EB00D9F37F /* LargeButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F8BB4B261C50EB00D9F37F /* LargeButtonStyle.swift */; }; + 9ED440A02C1605EF002574D1 /* OpenHaystackSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ED4409F2C1605EF002574D1 /* OpenHaystackSettingsView.swift */; }; F126102F2600D1D80066A859 /* Slider+LogScale.swift in Sources */ = {isa = PBXBuildFile; fileRef = F126102E2600D1D80066A859 /* Slider+LogScale.swift */; }; F12D5A5A25FA4F3500CBBA09 /* BluetoothAccessoryScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12D5A5925FA4F3500CBBA09 /* BluetoothAccessoryScanner.swift */; }; F12D5A6025FA79FA00CBBA09 /* Advertisement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12D5A5F25FA79FA00CBBA09 /* Advertisement.swift */; }; @@ -169,6 +170,7 @@ 78EC227125DBC8CE0042B775 /* Accessory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessory.swift; sourceTree = ""; }; 78EC227625DBDB7E0042B775 /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = ""; }; 78F8BB4B261C50EB00D9F37F /* LargeButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeButtonStyle.swift; sourceTree = ""; }; + 9ED4409F2C1605EF002574D1 /* OpenHaystackSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHaystackSettingsView.swift; sourceTree = ""; }; F126102E2600D1D80066A859 /* Slider+LogScale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Slider+LogScale.swift"; sourceTree = ""; }; F12D5A5925FA4F3500CBBA09 /* BluetoothAccessoryScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothAccessoryScanner.swift; sourceTree = ""; }; F12D5A5F25FA79FA00CBBA09 /* Advertisement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advertisement.swift; sourceTree = ""; }; @@ -380,6 +382,7 @@ isa = PBXGroup; children = ( 78F8BB4A261C50D500D9F37F /* Styles */, + 9ED4409F2C1605EF002574D1 /* OpenHaystackSettingsView.swift */, 78286D7625E5114600F65511 /* ActivityIndicator.swift */, 78EC226B25DBC2E40042B775 /* OpenHaystackMainView.swift */, 78486BEE25DD711E0007ED87 /* PopUpAlertView.swift */, @@ -644,6 +647,7 @@ 7821DAD325F7C39A0054DC33 /* ESP32InstallSheet.swift in Sources */, 781EB3F125DAD7EA00FEAA19 /* FindMyKeyDecoder.swift in Sources */, 787D8AC125DECD3C00148766 /* AccessoryController.swift in Sources */, + 9ED440A02C1605EF002574D1 /* OpenHaystackSettingsView.swift in Sources */, 78023CAB25F7767000B083EF /* ESP32Controller.swift in Sources */, F12D5A6025FA79FA00CBBA09 /* Advertisement.swift in Sources */, 781EB3F225DAD7EA00FEAA19 /* OpenHaystackApp.swift in Sources */, diff --git a/OpenHaystack/OpenHaystack.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/OpenHaystack/OpenHaystack.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7b30c94a..e0c2cac5 100644 --- a/OpenHaystack/OpenHaystack.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/OpenHaystack/OpenHaystack.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,24 @@ { + "originHash" : "bfeb00ee66eb6db71ff8535b5ea7585725e9fe73d97f066170be55b745d346e9", "pins" : [ + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "cd142fd2f64be2100422d658e7411e39489da985", + "version" : "1.2.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "ee97538f5b81ae89698fd95938896dec5217b148", + "version" : "1.1.1" + } + }, { "identity" : "swift-crypto", "kind" : "remoteSourceControl", @@ -14,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "124119f0bb12384cef35aa041d7c3a686108722d", - "version" : "2.40.0" + "revision" : "9428f62793696d9a0cc1f26a63f63bb31da0516d", + "version" : "2.66.0" } }, { @@ -23,10 +42,19 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-ssl", "state" : { - "revision" : "c30c680c78c99afdabf84805a83c8745387c4ac7", - "version" : "2.20.2" + "revision" : "2b09805797f21c380f7dc9bedaab3157c5508efb", + "version" : "2.27.0" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "f9266c85189c2751589a50ea5aec72799797e471", + "version" : "1.3.0" } } ], - "version" : 2 + "version" : 3 } diff --git a/OpenHaystack/OpenHaystack/AnisetteDataManager.swift b/OpenHaystack/OpenHaystack/AnisetteDataManager.swift index e8a6d047..0bef93d2 100644 --- a/OpenHaystack/OpenHaystack/AnisetteDataManager.swift +++ b/OpenHaystack/OpenHaystack/AnisetteDataManager.swift @@ -118,8 +118,6 @@ public class AnisetteDataManager: NSObject { guard let anisetteData = AOSUtilities.retrieveOTPHeadersForDSID("-2") else { throw AnisetteDataError.aosKitFailure } - let dateFormatter = ISO8601DateFormatter() - guard let machineID = anisetteData["X-Apple-MD-M"] as? String, let otp = anisetteData["X-Apple-MD"] as? String, let deviceId = AOSUtilities.machineUDID, diff --git a/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift b/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift index 62f1a08c..63a3fb0f 100755 --- a/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift +++ b/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift @@ -148,6 +148,10 @@ class FindMyController: ObservableObject { } catch { print("Failed with error \(error)") + if jsonData.isEmpty { + print("Empty response, consider updating your Search Party Token") + completion(FindMyErrors.invalidSearchPartyToken) + } devices[deviceIndex].reports = [] } fetchReportGroup.leave() @@ -241,4 +245,5 @@ class FindMyController: ObservableObject { enum FindMyErrors: Error { case decodingPlistFailed(message: String) case objectReleased + case invalidSearchPartyToken } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift b/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift index 3c2adc16..e248c1b5 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift @@ -13,6 +13,7 @@ import OSLog import SwiftUI class AccessoryController: ObservableObject { + @AppStorage("searchPartyToken") private var searchPartyToken: String = "" @Published var accessories: [Accessory] var selfObserver: AnyCancellable? var listElementsObserver = [AnyCancellable]() @@ -244,10 +245,8 @@ class AccessoryController: ObservableObject { case .failure(_): completion(.failure(.activatePlugin)) case .success(let accountData): - - guard let token = accountData.searchPartyToken, - token.isEmpty == false - else { + let token = accountData.searchPartyToken ?? self.searchPartyToken.data(using: .utf8) ?? Data() + if token.isEmpty { completion(.failure(.searchPartyToken)) return } @@ -256,7 +255,12 @@ class AccessoryController: ObservableObject { switch result { case .failure(let error): os_log(.error, "Downloading reports failed %@", error.localizedDescription) - completion(.failure(.downloadingReportsFailed)) + switch error { + case FindMyErrors.invalidSearchPartyToken: + completion(.failure(.invalidSearchPartyToken)) + default: + completion(.failure(.downloadingReportsFailed)) + } case .success(let devices): let reports = devices.compactMap({ $0.reports }).flatMap({ $0 }) if reports.isEmpty { diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift index 0ef144e5..e864d3b9 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift @@ -38,6 +38,9 @@ struct OpenHaystackMainView: View { @State var showESP32DeploySheet = false + @AppStorage("searchPartyToken") private var settingsSPToken: String? + @AppStorage("useMailPlugin") private var settingsUseMailPlugin: Bool = false + var body: some View { NavigationView { @@ -135,7 +138,7 @@ struct OpenHaystackMainView: View { Button( action: { - if !self.mailPluginIsActive { + if self.settingsUseMailPlugin && !self.mailPluginIsActive { self.showMailPlugInPopover.toggle() self.checkPluginIsRunning(silent: true, nil) } else { @@ -174,17 +177,26 @@ struct OpenHaystackMainView: View { return } - let pluginManager = MailPluginManager() + /// Checks if the search party token was set in the settings. If true the plugin is also not needed + if let tokenString = self.settingsSPToken { + self.searchPartyToken = tokenString + return + } - // Check if the plugin is installed - if pluginManager.isMailPluginInstalled == false { - // Install the mail plugin - self.alertType = .activatePlugin - self.checkPluginIsRunning(silent: true, nil) - } else { - self.checkPluginIsRunning(nil) + /// Uses mail plugin if enabled in settings + if self.settingsUseMailPlugin { + let pluginManager = MailPluginManager() + // Check if the plugin is installed + if pluginManager.isMailPluginInstalled == false { + // Install the mail plugin + self.alertType = .activatePlugin + self.checkPluginIsRunning(silent: true, nil) + } else { + self.checkPluginIsRunning(nil) + } } + } /// Download the location reports for all current accessories. Shows an error if something fails, like plug-in is missing @@ -308,7 +320,19 @@ struct OpenHaystackMainView: View { title: Text("Add the search party token"), message: Text( """ - Please paste the search party token below after copying itfrom the macOS Keychain. + Please paste the search party token in the settings after copying it from the macOS Keychain. + The item that contains the key can be found by searching for: + com.apple.account.DeviceLocator.search-party-token + """ + ), + dismissButton: Alert.Button.okay()) + case .invalidSearchPartyToken: + return Alert( + title: Text("Invalid search party token"), + message: Text( + """ + The request returned an empty result, this is probably due to an invalid search party token. + Please consider updating your search party token in the settings after copying it from the macOS Keychain. The item that contains the key can be found by searching for: com.apple.account.DeviceLocator.search-party-token """ @@ -388,6 +412,7 @@ struct OpenHaystackMainView: View { case keyError case searchPartyToken + case invalidSearchPartyToken case deployFailed case nrfDeployFailed case deployedSuccessfully diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackSettingsView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackSettingsView.swift new file mode 100644 index 00000000..56917072 --- /dev/null +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackSettingsView.swift @@ -0,0 +1,36 @@ +// +// OpenHaystack – Tracking personal Bluetooth devices via Apple's Find My network +// +// Copyright © 2024 Secure Mobile Networking Lab (SEEMOO) +// Copyright © 2024 The Open Wireless Link Project +// +// SPDX-License-Identifier: AGPL-3.0-only +// + +import Foundation +import SwiftUI + +struct OpenHaystackSettingsView: View { + var body: some View { + TabView { + GeneralSettingsView() + .tabItem { + Label("General", systemImage: "gear") + } + } + } +} + +struct GeneralSettingsView: View { + @AppStorage("useMailPlugin") private var useMailPlugin = false + @AppStorage("searchPartyToken") private var searchPartyToken = "" + + var body: some View { + Form { + Toggle("Use Apple Mail Plugin (only works on macOS 13 and lower)", isOn: $useMailPlugin) + TextField("Search Party Token", text: $searchPartyToken) + } + .padding(20) + .frame(width: 600, height: 200) + } +} diff --git a/OpenHaystack/OpenHaystack/OpenHaystackApp.swift b/OpenHaystack/OpenHaystack/OpenHaystackApp.swift index f886380f..e22a6b64 100644 --- a/OpenHaystack/OpenHaystack/OpenHaystackApp.swift +++ b/OpenHaystack/OpenHaystack/OpenHaystackApp.swift @@ -44,6 +44,11 @@ struct OpenHaystackApp: App { .commands { SidebarCommands() } + #if os(macOS) + Settings { + OpenHaystackSettingsView() + } + #endif } func checkForUpdates() {