mirror of
https://github.com/seemoo-lab/openhaystack.git
synced 2026-02-14 09:39:52 +00:00
Run autoformat
This commit is contained in:
@@ -9,15 +9,15 @@
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
|
||||
class AccessoryController: ObservableObject {
|
||||
@Published var accessories: [Accessory]
|
||||
var selfObserver: AnyCancellable?
|
||||
var listElementsObserver = [AnyCancellable]()
|
||||
let findMyController: FindMyController
|
||||
|
||||
|
||||
init(accessories: [Accessory], findMyController: FindMyController) {
|
||||
self.accessories = accessories
|
||||
self.findMyController = findMyController
|
||||
@@ -91,7 +91,7 @@ class AccessoryController: ObservableObject {
|
||||
}
|
||||
return accessory
|
||||
}
|
||||
|
||||
|
||||
/// Export the accessories property list so it can be imported at another location
|
||||
func export(accessories: [Accessory]) throws -> URL {
|
||||
let propertyList = try PropertyListEncoder().encode(accessories)
|
||||
@@ -109,7 +109,8 @@ class AccessoryController: ObservableObject {
|
||||
let result = savePanel.runModal()
|
||||
|
||||
if result == .OK,
|
||||
let url = savePanel.url {
|
||||
let url = savePanel.url
|
||||
{
|
||||
// Store the accessory file
|
||||
try propertyList.write(to: url)
|
||||
|
||||
@@ -130,47 +131,46 @@ class AccessoryController: ObservableObject {
|
||||
|
||||
let result = openPanel.runModal()
|
||||
if result == .OK,
|
||||
let url = openPanel.url {
|
||||
let url = openPanel.url
|
||||
{
|
||||
let propertyList = try Data(contentsOf: url)
|
||||
var importedAccessories = try PropertyListDecoder().decode([Accessory].self, from: propertyList)
|
||||
|
||||
var updatedAccessories = self.accessories
|
||||
// Filter out accessories with the same id (no duplicates)
|
||||
importedAccessories = importedAccessories.filter({acc in !self.accessories.contains(where: {acc.id == $0.id})})
|
||||
importedAccessories = importedAccessories.filter({ acc in !self.accessories.contains(where: { acc.id == $0.id }) })
|
||||
updatedAccessories.append(contentsOf: importedAccessories)
|
||||
updatedAccessories.sort(by: {$0.name < $1.name})
|
||||
|
||||
|
||||
updatedAccessories.sort(by: { $0.name < $1.name })
|
||||
|
||||
self.accessories = updatedAccessories
|
||||
|
||||
|
||||
//Update reports automatically. Do not report errors from here
|
||||
self.downloadLocationReports { result in}
|
||||
self.downloadLocationReports { result in }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum ImportError: Error {
|
||||
case cancelled
|
||||
}
|
||||
|
||||
|
||||
|
||||
//MARK: Location reports
|
||||
|
||||
|
||||
/// Download the location reports from
|
||||
/// - Parameter completion: called when the reports have been succesfully downloaded or the request has failed
|
||||
func downloadLocationReports(completion: @escaping (Result<Void,OpenHaystackMainView.AlertType>) -> Void) {
|
||||
|
||||
/// Download the location reports from
|
||||
/// - Parameter completion: called when the reports have been succesfully downloaded or the request has failed
|
||||
func downloadLocationReports(completion: @escaping (Result<Void, OpenHaystackMainView.AlertType>) -> Void) {
|
||||
AnisetteDataManager.shared.requestAnisetteData { result in
|
||||
switch result {
|
||||
case .failure(_):
|
||||
completion(.failure(.activatePlugin))
|
||||
case .success(let accountData):
|
||||
|
||||
|
||||
guard let token = accountData.searchPartyToken,
|
||||
token.isEmpty == false else {
|
||||
token.isEmpty == false
|
||||
else {
|
||||
completion(.failure(.searchPartyToken))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
self.findMyController.fetchReports(for: self.accessories, with: token) { result in
|
||||
switch result {
|
||||
case .failure(let error):
|
||||
@@ -180,17 +180,17 @@ class AccessoryController: ObservableObject {
|
||||
let reports = devices.compactMap({ $0.reports }).flatMap({ $0 })
|
||||
if reports.isEmpty {
|
||||
completion(.failure(.noReportsFound))
|
||||
}else {
|
||||
} else {
|
||||
self.updateWithDecryptedReports(devices: devices)
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AccessoryControllerPreview: AccessoryController {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//
|
||||
//
|
||||
// OpenHaystack – Tracking personal Bluetooth devices via Apple's Find My network
|
||||
//
|
||||
// Copyright © 2021 Secure Mobile Networking Lab (SEEMOO)
|
||||
@@ -21,7 +21,7 @@ struct ManageAccessoriesView: View {
|
||||
@Binding var focusedAccessory: Accessory?
|
||||
@Binding var accessoryToDeploy: Accessory?
|
||||
@Binding var showESP32DeploySheet: Bool
|
||||
|
||||
|
||||
@State var showMailPopup = false
|
||||
|
||||
var body: some View {
|
||||
@@ -74,23 +74,28 @@ struct ManageAccessoriesView: View {
|
||||
.listStyle(SidebarListStyle())
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// All toolbar buttons shown
|
||||
|
||||
/// All toolbar buttons shown
|
||||
var toolbarView: some View {
|
||||
Group {
|
||||
Spacer()
|
||||
|
||||
Button(action: self.importAccessories, label: {
|
||||
Label("Import accessories", systemImage: "square.and.arrow.down")
|
||||
})
|
||||
|
||||
Button(
|
||||
action: self.importAccessories,
|
||||
label: {
|
||||
Label("Import accessories", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
)
|
||||
.help("Import accessories from a file")
|
||||
|
||||
Button(action: self.exportAccessories, label: {
|
||||
Label("Export accessories", systemImage: "square.and.arrow.up")
|
||||
})
|
||||
|
||||
Button(
|
||||
action: self.exportAccessories,
|
||||
label: {
|
||||
Label("Export accessories", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
)
|
||||
.help("Export all accessories to a file")
|
||||
|
||||
|
||||
Button(action: self.addAccessory) {
|
||||
Label("Add accessory", systemImage: "plus")
|
||||
}
|
||||
@@ -120,25 +125,26 @@ struct ManageAccessoriesView: View {
|
||||
self.alertType = .keyError
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func exportAccessories() {
|
||||
do {
|
||||
_ = try self.accessoryController.export(accessories: self.accessories)
|
||||
}catch {
|
||||
} catch {
|
||||
self.alertType = .exportFailed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func importAccessories() {
|
||||
do {
|
||||
try self.accessoryController.importAccessories()
|
||||
}catch {
|
||||
} catch {
|
||||
if let importError = error as? AccessoryController.ImportError,
|
||||
importError == .cancelled {
|
||||
importError == .cancelled
|
||||
{
|
||||
//User cancelled the import. No error
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
self.alertType = .importFailed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ struct OpenHaystackMainView: View {
|
||||
|
||||
@State var loading = false
|
||||
@EnvironmentObject var accessoryController: AccessoryController
|
||||
|
||||
|
||||
var accessories: [Accessory] {
|
||||
return self.accessoryController.accessories
|
||||
}
|
||||
@@ -30,9 +30,9 @@ struct OpenHaystackMainView: View {
|
||||
@State var focusedAccessory: Accessory?
|
||||
@State var accessoryToDeploy: Accessory?
|
||||
@State var showMailPlugInPopover = false
|
||||
|
||||
|
||||
@State var mailPluginIsActive = false
|
||||
|
||||
|
||||
@State var showESP32DeploySheet = false
|
||||
|
||||
var body: some View {
|
||||
@@ -104,39 +104,43 @@ struct OpenHaystackMainView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// All toolbar items shown
|
||||
var toolbarView: some View {
|
||||
Group {
|
||||
|
||||
|
||||
Picker("", selection: self.$mapType) {
|
||||
Text("Satellite").tag(MKMapType.hybrid)
|
||||
Text("Standard").tag(MKMapType.standard)
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
|
||||
|
||||
Button(action: {
|
||||
if !self.mailPluginIsActive {
|
||||
self.showMailPlugInPopover.toggle()
|
||||
}else {
|
||||
self.downloadLocationReports()
|
||||
|
||||
Button(
|
||||
action: {
|
||||
if !self.mailPluginIsActive {
|
||||
self.showMailPlugInPopover.toggle()
|
||||
} else {
|
||||
self.downloadLocationReports()
|
||||
}
|
||||
|
||||
},
|
||||
label: {
|
||||
HStack {
|
||||
Circle()
|
||||
.fill(self.mailPluginIsActive ? Color.green : Color.orange)
|
||||
.frame(width: 8, height: 8)
|
||||
Label("Reload", systemImage: "arrow.clockwise")
|
||||
.disabled(!self.mailPluginIsActive)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},label: {
|
||||
HStack {
|
||||
Circle()
|
||||
.fill(self.mailPluginIsActive ? Color.green : Color.orange)
|
||||
.frame(width: 8, height: 8)
|
||||
Label("Reload", systemImage: "arrow.clockwise")
|
||||
.disabled(!self.mailPluginIsActive)
|
||||
}
|
||||
|
||||
})
|
||||
)
|
||||
.disabled(self.accessories.isEmpty)
|
||||
.popover(isPresented: $showMailPlugInPopover, content: {
|
||||
self.mailStatePopover
|
||||
})
|
||||
.popover(
|
||||
isPresented: $showMailPlugInPopover,
|
||||
content: {
|
||||
self.mailStatePopover
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +175,7 @@ struct OpenHaystackMainView: View {
|
||||
case .failure(let alert):
|
||||
if alert == .noReportsFound {
|
||||
self.popUpAlertType = .noReportsFound
|
||||
}else {
|
||||
} else {
|
||||
self.alertType = alert
|
||||
}
|
||||
case .success(_):
|
||||
@@ -179,15 +183,15 @@ struct OpenHaystackMainView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var mailStatePopover: some View {
|
||||
HStack {
|
||||
Image(systemName: "envelope")
|
||||
.foregroundColor(self.mailPluginIsActive ? .green : .red)
|
||||
|
||||
|
||||
if self.mailPluginIsActive {
|
||||
Text("The mail plug-in is up and running")
|
||||
}else {
|
||||
} else {
|
||||
Text("Cannot connect to the mail plug-in. Open Apple Mail and make sure the plug-in is enabled")
|
||||
.lineLimit(10)
|
||||
.multilineTextAlignment(.leading)
|
||||
@@ -234,7 +238,7 @@ struct OpenHaystackMainView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func checkPluginIsRunning(silent: Bool=false, _ completion: ((Bool) -> Void)?) {
|
||||
func checkPluginIsRunning(silent: Bool = false, _ completion: ((Bool) -> Void)?) {
|
||||
// Check if Mail plugin is active
|
||||
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
||||
DispatchQueue.main.async {
|
||||
@@ -242,7 +246,7 @@ struct OpenHaystackMainView: View {
|
||||
case .success(let accountData):
|
||||
|
||||
withAnimation {
|
||||
if let token = accountData.searchPartyToken {
|
||||
if let token = accountData.searchPartyToken {
|
||||
self.searchPartyToken = String(data: token, encoding: .ascii) ?? ""
|
||||
if self.searchPartyToken.isEmpty == false {
|
||||
self.searchPartyTokenLoaded = true
|
||||
@@ -263,11 +267,13 @@ struct OpenHaystackMainView: View {
|
||||
}
|
||||
self.mailPluginIsActive = false
|
||||
completion?(false)
|
||||
|
||||
|
||||
//Check again in 5s
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: {
|
||||
self.checkPluginIsRunning(silent: true, nil)
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(
|
||||
deadline: .now() + 5,
|
||||
execute: {
|
||||
self.checkPluginIsRunning(silent: true, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,9 +368,10 @@ struct OpenHaystackMainView: View {
|
||||
primaryButton: microbitButton,
|
||||
secondaryButton: esp32Button)
|
||||
case .downloadingReportsFailed:
|
||||
return Alert(title: Text("Downloading locations failed"),
|
||||
message: Text("We could not download any locations from Apple. Please try again later"),
|
||||
dismissButton: Alert.Button.okay())
|
||||
return Alert(
|
||||
title: Text("Downloading locations failed"),
|
||||
message: Text("We could not download any locations from Apple. Please try again later"),
|
||||
dismissButton: Alert.Button.okay())
|
||||
case .exportFailed:
|
||||
return Alert(
|
||||
title: Text("Export failed"),
|
||||
|
||||
@@ -31,22 +31,5 @@ struct OpenHaystackApp: App {
|
||||
.commands {
|
||||
SidebarCommands()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//MARK: Environment objects
|
||||
private struct FindMyControllerEnvironmentKey: EnvironmentKey {
|
||||
static let defaultValue: FindMyController = FindMyController()
|
||||
}
|
||||
|
||||
private struct AccessoryControllerEnvironmentKey: EnvironmentKey {
|
||||
static let defaultValue: AccessoryController = {
|
||||
if ProcessInfo().arguments.contains("-preview") {
|
||||
return AccessoryControllerPreview(accessories: PreviewData.accessories, findMyController: FindMyController())
|
||||
} else {
|
||||
return AccessoryController()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property(nonatomic, copy) NSLocale *locale;
|
||||
@property(nonatomic, copy) NSTimeZone *timeZone;
|
||||
|
||||
@property(nonatomic, copy) NSData * _Nullable searchPartyToken;
|
||||
@property(nonatomic, copy) NSData *_Nullable searchPartyToken;
|
||||
|
||||
- (instancetype)initWithMachineID:(NSString *)machineID
|
||||
oneTimePassword:(NSString *)oneTimePassword
|
||||
|
||||
Reference in New Issue
Block a user