diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 3650ec29..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: rileytestut -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 289e4d62..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information if applicable):** - - OS: [Mac or Windows] - - Version: [e.g. Catalina] - -**iPhone (please complete the following information):** - - Device: [e.g. iPhone8] - - iOS: [e.g. 13.1] - -**Additional context and logs** -Add any error logs or any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 6665601b..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 675e77fc..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Post Commit to Discord - -on: - push: - branches: - - master - - develop - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - name: Discord notification - env: - DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} - DISCORD_USERNAME: AltBot - uses: Ilshidur/action-discord@c7b60ec diff --git a/AltBackup/AltBackup.entitlements b/AltBackup/AltBackup.entitlements new file mode 100644 index 00000000..099f1e39 --- /dev/null +++ b/AltBackup/AltBackup.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.rileytestut.AltStore + + + diff --git a/AltBackup/AppDelegate.swift b/AltBackup/AppDelegate.swift new file mode 100644 index 00000000..5d8a21a7 --- /dev/null +++ b/AltBackup/AppDelegate.swift @@ -0,0 +1,121 @@ +// +// AppDelegate.swift +// AltBackup +// +// Created by Riley Testut on 5/11/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import UIKit + +extension AppDelegate +{ + static let startBackupNotification = Notification.Name("io.altstore.StartBackup") + static let startRestoreNotification = Notification.Name("io.altstore.StartRestore") + + static let operationDidFinishNotification = Notification.Name("io.altstore.BackupOperationFinished") + + static let operationResultKey = "result" +} + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + private var currentBackupReturnURL: URL? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool + { + // Override point for customization after application launch. + + NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.operationDidFinish(_:)), name: AppDelegate.operationDidFinishNotification, object: nil) + + let viewController = ViewController() + + self.window = UIWindow(frame: UIScreen.main.bounds) + self.window?.rootViewController = viewController + self.window?.makeKeyAndVisible() + + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool + { + return self.open(url) + } +} + +private extension AppDelegate +{ + func open(_ url: URL) -> Bool + { + guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false } + guard let command = components.host?.lowercased() else { return false } + + switch command + { + case "backup": + guard let returnString = components.queryItems?.first(where: { $0.name == "returnURL" })?.value, let returnURL = URL(string: returnString) else { return false } + self.currentBackupReturnURL = returnURL + NotificationCenter.default.post(name: AppDelegate.startBackupNotification, object: nil) + + return true + + case "restore": + guard let returnString = components.queryItems?.first(where: { $0.name == "returnURL" })?.value, let returnURL = URL(string: returnString) else { return false } + self.currentBackupReturnURL = returnURL + NotificationCenter.default.post(name: AppDelegate.startRestoreNotification, object: nil) + + return true + + default: return false + } + } + + @objc func operationDidFinish(_ notification: Notification) + { + defer { self.currentBackupReturnURL = nil } + + guard + let returnURL = self.currentBackupReturnURL, + let result = notification.userInfo?[AppDelegate.operationResultKey] as? Result + else { return } + + guard var components = URLComponents(url: returnURL, resolvingAgainstBaseURL: false) else { return } + + switch result + { + case .success: + components.path = "/success" + + case .failure(let error as NSError): + components.path = "/failure" + components.queryItems = ["errorDomain": error.domain, + "errorCode": String(error.code), + "errorDescription": error.localizedDescription].map { URLQueryItem(name: $0, value: $1) } + } + + guard let responseURL = components.url else { return } + + DispatchQueue.main.async { + UIApplication.shared.open(responseURL, options: [:]) { (success) in + print("Sent response to app with success:", success) + } + } + } +} + diff --git a/AltBackup/Assets.xcassets/AppIcon.appiconset/Contents.json b/AltBackup/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/AltBackup/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltBackup/Assets.xcassets/Background.colorset/Contents.json b/AltBackup/Assets.xcassets/Background.colorset/Contents.json new file mode 100644 index 00000000..8251d696 --- /dev/null +++ b/AltBackup/Assets.xcassets/Background.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.518", + "green" : "0.502", + "red" : "0.004" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.404", + "green" : "0.322", + "red" : "0.008" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltBackup/Assets.xcassets/Contents.json b/AltBackup/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/AltBackup/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltBackup/Assets.xcassets/Text.colorset/Contents.json b/AltBackup/Assets.xcassets/Text.colorset/Contents.json new file mode 100644 index 00000000..a004a7f8 --- /dev/null +++ b/AltBackup/Assets.xcassets/Text.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.750", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltBackup/BackupController.swift b/AltBackup/BackupController.swift new file mode 100644 index 00000000..4b2379ac --- /dev/null +++ b/AltBackup/BackupController.swift @@ -0,0 +1,293 @@ +// +// BackupController.swift +// AltBackup +// +// Created by Riley Testut on 5/12/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation + +extension ErrorUserInfoKey +{ + static let sourceFile: String = "alt_sourceFile" + static let sourceFileLine: String = "alt_sourceFileLine" +} + +extension Error +{ + var sourceDescription: String? { + guard let sourceFile = (self as NSError).userInfo[ErrorUserInfoKey.sourceFile] as? String, let sourceFileLine = (self as NSError).userInfo[ErrorUserInfoKey.sourceFileLine] else { + return nil + } + return "(\((sourceFile as NSString).lastPathComponent), Line \(sourceFileLine))" + } +} + +struct BackupError: ALTLocalizedError +{ + enum Code + { + case invalidBundleID + case appGroupNotFound(String?) + case randomError // Used for debugging. + } + + let code: Code + + let sourceFile: String + let sourceFileLine: Int + + var errorFailure: String? + + var failureReason: String? { + switch self.code + { + case .invalidBundleID: return NSLocalizedString("The bundle identifier is invalid.", comment: "") + case .appGroupNotFound(let appGroup): + if let appGroup = appGroup + { + return String(format: NSLocalizedString("The app group “%@” could not be found.", comment: ""), appGroup) + } + else + { + return NSLocalizedString("The AltStore app group could not be found.", comment: "") + } + case .randomError: return NSLocalizedString("A random error occured.", comment: "") + } + } + + var errorUserInfo: [String : Any] { + let userInfo: [String: Any?] = [NSLocalizedDescriptionKey: self.errorDescription, + NSLocalizedFailureReasonErrorKey: self.failureReason, + NSLocalizedFailureErrorKey: self.errorFailure, + ErrorUserInfoKey.sourceFile: self.sourceFile, + ErrorUserInfoKey.sourceFileLine: self.sourceFileLine] + return userInfo.compactMapValues { $0 } + } + + init(_ code: Code, description: String? = nil, file: String = #file, line: Int = #line) + { + self.code = code + self.errorFailure = description + self.sourceFile = file + self.sourceFileLine = line + } +} + +class BackupController: NSObject +{ + private let fileCoordinator = NSFileCoordinator(filePresenter: nil) + private let operationQueue = OperationQueue() + + override init() + { + self.operationQueue.name = "AltBackup-BackupQueue" + } + + func performBackup(completionHandler: @escaping (Result) -> Void) + { + do + { + guard let bundleIdentifier = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.altBundleID) as? String else { + throw BackupError(.invalidBundleID, description: NSLocalizedString("Unable to create backup directory.", comment: "")) + } + + guard + let altstoreAppGroup = Bundle.main.altstoreAppGroup, + let sharedDirectoryURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: altstoreAppGroup) + else { throw BackupError(.appGroupNotFound(nil), description: NSLocalizedString("Unable to create backup directory.", comment: "")) } + + let backupsDirectory = sharedDirectoryURL.appendingPathComponent("Backups") + + // Use temporary directory to prevent messing up successful backup with incomplete one. + let temporaryAppBackupDirectory = backupsDirectory.appendingPathComponent("Temp", isDirectory: true).appendingPathComponent(UUID().uuidString) + let appBackupDirectory = backupsDirectory.appendingPathComponent(bundleIdentifier) + + let writingIntent = NSFileAccessIntent.writingIntent(with: temporaryAppBackupDirectory, options: []) + let replacementIntent = NSFileAccessIntent.writingIntent(with: appBackupDirectory, options: [.forReplacing]) + self.fileCoordinator.coordinate(with: [writingIntent, replacementIntent], queue: self.operationQueue) { (error) in + do + { + if let error = error + { + throw error + } + + do + { + let mainGroupBackupDirectory = temporaryAppBackupDirectory.appendingPathComponent("App") + try FileManager.default.createDirectory(at: mainGroupBackupDirectory, withIntermediateDirectories: true, attributes: nil) + + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + let backupDocumentsDirectory = mainGroupBackupDirectory.appendingPathComponent(documentsDirectory.lastPathComponent) + + if FileManager.default.fileExists(atPath: backupDocumentsDirectory.path) + { + try FileManager.default.removeItem(at: backupDocumentsDirectory) + } + + if FileManager.default.fileExists(atPath: documentsDirectory.path) + { + try FileManager.default.copyItem(at: documentsDirectory, to: backupDocumentsDirectory) + } + + print("Copied Documents directory from \(documentsDirectory) to \(backupDocumentsDirectory)") + + let libraryDirectory = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0] + let backupLibraryDirectory = mainGroupBackupDirectory.appendingPathComponent(libraryDirectory.lastPathComponent) + + if FileManager.default.fileExists(atPath: backupLibraryDirectory.path) + { + try FileManager.default.removeItem(at: backupLibraryDirectory) + } + + if FileManager.default.fileExists(atPath: libraryDirectory.path) + { + try FileManager.default.copyItem(at: libraryDirectory, to: backupLibraryDirectory) + } + + print("Copied Library directory from \(libraryDirectory) to \(backupLibraryDirectory)") + } + + for appGroup in Bundle.main.appGroups where appGroup != altstoreAppGroup + { + guard let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else { + throw BackupError(.appGroupNotFound(appGroup), description: NSLocalizedString("Unable to create app group backup directory.", comment: "")) + } + + let backupAppGroupURL = temporaryAppBackupDirectory.appendingPathComponent(appGroup) + + // There are several system hidden files that we don't have permission to read, so we just skip all hidden files in app group directories. + try self.copyDirectoryContents(at: appGroupURL, to: backupAppGroupURL, options: [.skipsHiddenFiles]) + } + + // Replace previous backup with new backup. + _ = try FileManager.default.replaceItemAt(appBackupDirectory, withItemAt: temporaryAppBackupDirectory) + + print("Replaced previous backup with new backup:", temporaryAppBackupDirectory) + + completionHandler(.success(())) + } + catch + { + do { try FileManager.default.removeItem(at: temporaryAppBackupDirectory) } + catch { print("Failed to remove temporary directory.", error) } + + completionHandler(.failure(error)) + } + } + } + catch + { + completionHandler(.failure(error)) + } + } + + func restoreBackup(completionHandler: @escaping (Result) -> Void) + { + do + { + guard let bundleIdentifier = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.altBundleID) as? String else { + throw BackupError(.invalidBundleID, description: NSLocalizedString("Unable to access backup.", comment: "")) + } + + guard + let altstoreAppGroup = Bundle.main.altstoreAppGroup, + let sharedDirectoryURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: altstoreAppGroup) + else { throw BackupError(.appGroupNotFound(nil), description: NSLocalizedString("Unable to access backup.", comment: "")) } + + let backupsDirectory = sharedDirectoryURL.appendingPathComponent("Backups") + let appBackupDirectory = backupsDirectory.appendingPathComponent(bundleIdentifier) + + let readingIntent = NSFileAccessIntent.readingIntent(with: appBackupDirectory, options: []) + self.fileCoordinator.coordinate(with: [readingIntent], queue: self.operationQueue) { (error) in + do + { + if let error = error + { + throw error + } + + let mainGroupBackupDirectory = appBackupDirectory.appendingPathComponent("App") + + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + let backupDocumentsDirectory = mainGroupBackupDirectory.appendingPathComponent(documentsDirectory.lastPathComponent) + + let libraryDirectory = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0] + let backupLibraryDirectory = mainGroupBackupDirectory.appendingPathComponent(libraryDirectory.lastPathComponent) + + try self.copyDirectoryContents(at: backupDocumentsDirectory, to: documentsDirectory) + try self.copyDirectoryContents(at: backupLibraryDirectory, to: libraryDirectory) + + for appGroup in Bundle.main.appGroups where appGroup != altstoreAppGroup + { + guard let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else { + throw BackupError(.appGroupNotFound(appGroup), description: NSLocalizedString("Unable to read app group backup.", comment: "")) + } + + let backupAppGroupURL = appBackupDirectory.appendingPathComponent(appGroup) + try self.copyDirectoryContents(at: backupAppGroupURL, to: appGroupURL) + } + + completionHandler(.success(())) + } + catch + { + completionHandler(.failure(error)) + } + } + } + catch + { + completionHandler(.failure(error)) + } + } +} + +private extension BackupController +{ + func copyDirectoryContents(at sourceDirectoryURL: URL, to destinationDirectoryURL: URL, options: FileManager.DirectoryEnumerationOptions = []) throws + { + guard FileManager.default.fileExists(atPath: sourceDirectoryURL.path) else { return } + + if !FileManager.default.fileExists(atPath: destinationDirectoryURL.path) + { + try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil) + } + + for fileURL in try FileManager.default.contentsOfDirectory(at: sourceDirectoryURL, includingPropertiesForKeys: [.isDirectoryKey], options: options) + { + let isDirectory = try fileURL.resourceValues(forKeys: [.isDirectoryKey]).isDirectory ?? false + let destinationURL = destinationDirectoryURL.appendingPathComponent(fileURL.lastPathComponent) + + if FileManager.default.fileExists(atPath: destinationURL.path) + { + do { + try FileManager.default.removeItem(at: destinationURL) + } + catch CocoaError.fileWriteNoPermission where isDirectory { + try self.copyDirectoryContents(at: fileURL, to: destinationURL, options: options) + continue + } + catch { + print(error) + throw error + } + } + + do { + try FileManager.default.copyItem(at: fileURL, to: destinationURL) + print("Copied item from \(fileURL) to \(destinationURL)") + } + catch let error where fileURL.lastPathComponent == "Inbox" && fileURL.deletingLastPathComponent().lastPathComponent == "Documents" { + // Ignore errors for /Documents/Inbox + print("Failed to copy Inbox directory:", error) + } + catch { + print(error) + throw error + } + } + } +} diff --git a/AltBackup/Base.lproj/LaunchScreen.storyboard b/AltBackup/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..61e8dd4c --- /dev/null +++ b/AltBackup/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AltBackup/Info.plist b/AltBackup/Info.plist new file mode 100644 index 00000000..63d01c06 --- /dev/null +++ b/AltBackup/Info.plist @@ -0,0 +1,66 @@ + + + + + ALTAppGroups + + group.com.rileytestut.AltStore + + ALTBundleIdentifier + com.rileytestut.AltBackup + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + AltBackup General + CFBundleURLSchemes + + altbackup + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarStyle + UIStatusBarStyleLightContent + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportsDocumentBrowser + + + diff --git a/AltBackup/UIColor+AltBackup.swift b/AltBackup/UIColor+AltBackup.swift new file mode 100644 index 00000000..73c7ceeb --- /dev/null +++ b/AltBackup/UIColor+AltBackup.swift @@ -0,0 +1,15 @@ +// +// UIColor+AltBackup.swift +// AltBackup +// +// Created by Riley Testut on 5/11/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import UIKit + +extension UIColor +{ + static let altstoreBackground = UIColor(named: "Background")! + static let altstoreText = UIColor(named: "Text")! +} diff --git a/AltBackup/ViewController.swift b/AltBackup/ViewController.swift new file mode 100644 index 00000000..0efb7112 --- /dev/null +++ b/AltBackup/ViewController.swift @@ -0,0 +1,206 @@ +// +// ViewController.swift +// AltBackup +// +// Created by Riley Testut on 5/11/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import UIKit + +extension Bundle +{ + var appName: String? { + let appName = + Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? + Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as? String + return appName + } +} + +extension ViewController +{ + enum BackupOperation + { + case backup + case restore + } +} + +class ViewController: UIViewController +{ + private let backupController = BackupController() + + private var currentOperation: BackupOperation? { + didSet { + DispatchQueue.main.async { + self.update() + } + } + } + + private var textLabel: UILabel! + private var detailTextLabel: UILabel! + private var activityIndicatorView: UIActivityIndicatorView! + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) + { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + + NotificationCenter.default.addObserver(self, selector: #selector(ViewController.backup), name: AppDelegate.startBackupNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(ViewController.restore), name: AppDelegate.startRestoreNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(ViewController.didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil) + } + + required init?(coder: NSCoder) { + fatalError() + } + + override func viewDidLoad() + { + super.viewDidLoad() + + self.view.backgroundColor = .altstoreBackground + + self.textLabel = UILabel(frame: .zero) + self.textLabel.font = UIFont.preferredFont(forTextStyle: .title2) + self.textLabel.textColor = .altstoreText + self.textLabel.textAlignment = .center + self.textLabel.numberOfLines = 0 + + self.detailTextLabel = UILabel(frame: .zero) + self.detailTextLabel.font = UIFont.preferredFont(forTextStyle: .body) + self.detailTextLabel.textColor = .altstoreText + self.detailTextLabel.textAlignment = .center + self.detailTextLabel.numberOfLines = 0 + + self.activityIndicatorView = UIActivityIndicatorView(style: .whiteLarge) + self.activityIndicatorView.color = .altstoreText + self.activityIndicatorView.startAnimating() + + #if DEBUG + let button1 = UIButton(type: .system) + button1.setTitle("Backup", for: .normal) + button1.setTitleColor(.white, for: .normal) + button1.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body) + button1.addTarget(self, action: #selector(ViewController.backup), for: .primaryActionTriggered) + + let button2 = UIButton(type: .system) + button2.setTitle("Restore", for: .normal) + button2.setTitleColor(.white, for: .normal) + button2.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body) + button2.addTarget(self, action: #selector(ViewController.restore), for: .primaryActionTriggered) + + let arrangedSubviews = [self.textLabel!, self.detailTextLabel!, self.activityIndicatorView!, button1, button2] + #else + let arrangedSubviews = [self.textLabel!, self.detailTextLabel!, self.activityIndicatorView!] + #endif + + let stackView = UIStackView(arrangedSubviews: arrangedSubviews) + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.spacing = 22 + stackView.axis = .vertical + stackView.alignment = .center + self.view.addSubview(stackView) + + NSLayoutConstraint.activate([stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), + stackView.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: self.view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1.0), + self.view.safeAreaLayoutGuide.trailingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: stackView.trailingAnchor, multiplier: 1.0)]) + + self.update() + } +} + +private extension ViewController +{ + @objc func backup() + { + self.currentOperation = .backup + + self.backupController.performBackup { (result) in + let appName = Bundle.main.appName ?? NSLocalizedString("App", comment: "") + + let title = String(format: NSLocalizedString("%@ could not be backed up.", comment: ""), appName) + self.process(result, errorTitle: title) + } + } + + @objc func restore() + { + self.currentOperation = .restore + + self.backupController.restoreBackup { (result) in + let appName = Bundle.main.appName ?? NSLocalizedString("App", comment: "") + + let title = String(format: NSLocalizedString("%@ could not be restored.", comment: ""), appName) + self.process(result, errorTitle: title) + } + } + + func update() + { + switch self.currentOperation + { + case .backup: + self.textLabel.text = NSLocalizedString("Backing up app data…", comment: "") + self.detailTextLabel.isHidden = true + self.activityIndicatorView.startAnimating() + + case .restore: + self.textLabel.text = NSLocalizedString("Restoring app data…", comment: "") + self.detailTextLabel.isHidden = true + self.activityIndicatorView.startAnimating() + + case .none: + self.textLabel.text = String(format: NSLocalizedString("%@ is inactive.", comment: ""), + Bundle.main.appName ?? NSLocalizedString("App", comment: "")) + + self.detailTextLabel.text = String(format: NSLocalizedString("Refresh %@ in AltStore to continue using it.", comment: ""), + Bundle.main.appName ?? NSLocalizedString("this app", comment: "")) + + self.detailTextLabel.isHidden = false + self.activityIndicatorView.stopAnimating() + } + } +} + +private extension ViewController +{ + func process(_ result: Result, errorTitle: String) + { + DispatchQueue.main.async { + switch result + { + case .success: break + case .failure(let error as NSError): + let message: String + + if let sourceDescription = error.sourceDescription + { + message = error.localizedDescription + "\n\n" + sourceDescription + } + else + { + message = error.localizedDescription + } + + let alertController = UIAlertController(title: errorTitle, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) + self.present(alertController, animated: true, completion: nil) + } + + NotificationCenter.default.post(name: AppDelegate.operationDidFinishNotification, object: nil, userInfo: [AppDelegate.operationResultKey: result]) + } + } + + @objc func didEnterBackground(_ notification: Notification) + { + // Reset UI once we've left app (but not before). + self.currentOperation = nil + } +} diff --git a/AltDaemon/AltDaemon-Bridging-Header.h b/AltDaemon/AltDaemon-Bridging-Header.h new file mode 100644 index 00000000..94f57276 --- /dev/null +++ b/AltDaemon/AltDaemon-Bridging-Header.h @@ -0,0 +1,59 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import + +// Shared +#import "ALTConstants.h" +#import "ALTConnection.h" +#import "NSError+ALTServerError.h" +#import "CFNotificationName+AltStore.h" + +// libproc +int proc_pidpath(int pid, void * buffer, uint32_t buffersize); + +// Security.framework +CF_ENUM(uint32_t) { + kSecCSInternalInformation = 1 << 0, + kSecCSSigningInformation = 1 << 1, + kSecCSRequirementInformation = 1 << 2, + kSecCSDynamicInformation = 1 << 3, + kSecCSContentInformation = 1 << 4, + kSecCSSkipResourceDirectory = 1 << 5, + kSecCSCalculateCMSDigest = 1 << 6, +}; + +OSStatus SecStaticCodeCreateWithPath(CFURLRef path, uint32_t flags, void ** __nonnull CF_RETURNS_RETAINED staticCode); +OSStatus SecCodeCopySigningInformation(void *code, uint32_t flags, CFDictionaryRef * __nonnull CF_RETURNS_RETAINED information); + +NS_ASSUME_NONNULL_BEGIN + +@interface AKDevice : NSObject + +@property (class, readonly) AKDevice *currentDevice; + +@property (strong, readonly) NSString *serialNumber; +@property (strong, readonly) NSString *uniqueDeviceIdentifier; +@property (strong, readonly) NSString *serverFriendlyDescription; + +@end + +@interface AKAppleIDSession : NSObject + +- (instancetype)initWithIdentifier:(NSString *)identifier; + +- (NSDictionary *)appleIDHeadersForRequest:(NSURLRequest *)request; + +@end + +@interface LSApplicationWorkspace : NSObject + +@property (class, readonly) LSApplicationWorkspace *defaultWorkspace; + +- (BOOL)installApplication:(NSURL *)fileURL withOptions:(nullable NSDictionary *)options error:(NSError *_Nullable *)error; +- (BOOL)uninstallApplication:(NSString *)bundleIdentifier withOptions:(nullable NSDictionary *)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AltDaemon/AltDaemon.entitlements b/AltDaemon/AltDaemon.entitlements new file mode 100644 index 00000000..3dfaef0b --- /dev/null +++ b/AltDaemon/AltDaemon.entitlements @@ -0,0 +1,22 @@ + + + + + application-identifier + 6XVY5G3U44.com.rileytestut.AltDaemon + get-task-allow + + platform-application + + com.apple.authkit.client.private + + com.apple.private.mobileinstall.allowedSPI + + Install + Uninstall + InstallForLaunchServices + UninstallForLaunchServices + InstallLocalProvisioned + + + diff --git a/AltDaemon/AnisetteDataManager.swift b/AltDaemon/AnisetteDataManager.swift new file mode 100644 index 00000000..daff25d6 --- /dev/null +++ b/AltDaemon/AnisetteDataManager.swift @@ -0,0 +1,65 @@ +// +// AnisetteDataManager.swift +// AltDaemon +// +// Created by Riley Testut on 6/1/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation + +import AltSign + +private extension UserDefaults +{ + @objc var localUserID: String? { + get { return self.string(forKey: #keyPath(UserDefaults.localUserID)) } + set { self.set(newValue, forKey: #keyPath(UserDefaults.localUserID)) } + } +} + +struct AnisetteDataManager +{ + static let shared = AnisetteDataManager() + + private let dateFormatter = ISO8601DateFormatter() + + private init() + { + dlopen("/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit", RTLD_NOW); + } + + func requestAnisetteData() throws -> ALTAnisetteData + { + var request = URLRequest(url: URL(string: "https://developerservices2.apple.com/services/QH65B2/listTeams.action?clientId=XABBG36SBA")!) + request.httpMethod = "POST" + + let akAppleIDSession = unsafeBitCast(NSClassFromString("AKAppleIDSession")!, to: AKAppleIDSession.Type.self) + let akDevice = unsafeBitCast(NSClassFromString("AKDevice")!, to: AKDevice.Type.self) + + let session = akAppleIDSession.init(identifier: "com.apple.gs.xcode.auth") + let headers = session.appleIDHeaders(for: request) + + let device = akDevice.current + let date = self.dateFormatter.date(from: headers["X-Apple-I-Client-Time"] ?? "") ?? Date() + + var localUserID = UserDefaults.standard.localUserID + if localUserID == nil + { + localUserID = UUID().uuidString + UserDefaults.standard.localUserID = localUserID + } + + let anisetteData = ALTAnisetteData(machineID: headers["X-Apple-I-MD-M"] ?? "", + oneTimePassword: headers["X-Apple-I-MD"] ?? "", + localUserID: headers["X-Apple-I-MD-LU"] ?? localUserID ?? "", + routingInfo: UInt64(headers["X-Apple-I-MD-RINFO"] ?? "") ?? 0, + deviceUniqueIdentifier: device.uniqueDeviceIdentifier, + deviceSerialNumber: device.serialNumber, + deviceDescription: " ", + date: date, + locale: .current, + timeZone: .current) + return anisetteData + } +} diff --git a/AltDaemon/AppManager.swift b/AltDaemon/AppManager.swift new file mode 100644 index 00000000..a0408ba2 --- /dev/null +++ b/AltDaemon/AppManager.swift @@ -0,0 +1,138 @@ +// +// AppManager.swift +// AltDaemon +// +// Created by Riley Testut on 6/1/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation + +import AltSign + +private extension URL +{ + static let profilesDirectoryURL = URL(fileURLWithPath: "/var/MobileDevice/ProvisioningProfiles", isDirectory: true) +} + +private extension CFNotificationName +{ + static let updatedProvisioningProfiles = CFNotificationName("MISProvisioningProfileRemoved" as CFString) +} + +struct AppManager +{ + static let shared = AppManager() + + private let appQueue = DispatchQueue(label: "com.rileytestut.AltDaemon.appQueue", qos: .userInitiated) + private let profilesQueue = OperationQueue() + + private let fileCoordinator = NSFileCoordinator() + + private init() + { + self.profilesQueue.name = "com.rileytestut.AltDaemon.profilesQueue" + self.profilesQueue.qualityOfService = .userInitiated + } + + func installApp(at fileURL: URL, bundleIdentifier: String, activeProfiles: Set?, completionHandler: @escaping (Result) -> Void) + { + self.appQueue.async { + let lsApplicationWorkspace = unsafeBitCast(NSClassFromString("LSApplicationWorkspace")!, to: LSApplicationWorkspace.Type.self) + + let options = ["CFBundleIdentifier": bundleIdentifier, "AllowInstallLocalProvisioned": NSNumber(value: true)] as [String : Any] + let result = Result { try lsApplicationWorkspace.default.installApplication(fileURL, withOptions: options) } + + completionHandler(result) + } + } + + func removeApp(forBundleIdentifier bundleIdentifier: String, completionHandler: @escaping (Result) -> Void) + { + self.appQueue.async { + let lsApplicationWorkspace = unsafeBitCast(NSClassFromString("LSApplicationWorkspace")!, to: LSApplicationWorkspace.Type.self) + lsApplicationWorkspace.default.uninstallApplication(bundleIdentifier, withOptions: nil) + + completionHandler(.success(())) + } + } + + func install(_ profiles: Set, activeProfiles: Set?, completionHandler: @escaping (Result) -> Void) + { + let intent = NSFileAccessIntent.writingIntent(with: .profilesDirectoryURL, options: []) + self.fileCoordinator.coordinate(with: [intent], queue: self.profilesQueue) { (error) in + do + { + if let error = error + { + throw error + } + + let installingBundleIDs = Set(profiles.map(\.bundleIdentifier)) + + let profileURLs = try FileManager.default.contentsOfDirectory(at: intent.url, includingPropertiesForKeys: nil, options: []) + + // Remove all inactive profiles (if active profiles are provided), and the previous profiles. + for fileURL in profileURLs + { + // Use memory mapping to reduce peak memory usage and stay within limit. + guard let profile = try? ALTProvisioningProfile(url: fileURL, options: [.mappedIfSafe]) else { continue } + + if installingBundleIDs.contains(profile.bundleIdentifier) || (activeProfiles?.contains(profile.bundleIdentifier) == false && profile.isFreeProvisioningProfile) + { + try FileManager.default.removeItem(at: fileURL) + } + else + { + print("Ignoring:", profile.bundleIdentifier, profile.uuid) + } + } + + for profile in profiles + { + let destinationURL = URL.profilesDirectoryURL.appendingPathComponent(profile.uuid.uuidString.lowercased()) + try profile.data.write(to: destinationURL, options: .atomic) + } + + completionHandler(.success(())) + } + catch + { + completionHandler(.failure(error)) + } + + // Notify system to prevent accidentally untrusting developer certificate. + CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), .updatedProvisioningProfiles, nil, nil, true) + } + } + + func removeProvisioningProfiles(forBundleIdentifiers bundleIdentifiers: Set, completionHandler: @escaping (Result) -> Void) + { + let intent = NSFileAccessIntent.writingIntent(with: .profilesDirectoryURL, options: []) + self.fileCoordinator.coordinate(with: [intent], queue: self.profilesQueue) { (error) in + do + { + let profileURLs = try FileManager.default.contentsOfDirectory(at: intent.url, includingPropertiesForKeys: nil, options: []) + + for fileURL in profileURLs + { + guard let profile = ALTProvisioningProfile(url: fileURL) else { continue } + + if bundleIdentifiers.contains(profile.bundleIdentifier) + { + try FileManager.default.removeItem(at: fileURL) + } + } + + completionHandler(.success(())) + } + catch + { + completionHandler(.failure(error)) + } + + // Notify system to prevent accidentally untrusting developer certificate. + CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), .updatedProvisioningProfiles, nil, nil, true) + } + } +} diff --git a/AltDaemon/DaemonRequestHandler.swift b/AltDaemon/DaemonRequestHandler.swift new file mode 100644 index 00000000..48b2d7c7 --- /dev/null +++ b/AltDaemon/DaemonRequestHandler.swift @@ -0,0 +1,123 @@ +// +// DaemonRequestHandler.swift +// AltDaemon +// +// Created by Riley Testut on 6/1/20. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +import Foundation + +typealias DaemonConnectionManager = ConnectionManager + +private let connectionManager = ConnectionManager(requestHandler: DaemonRequestHandler(), + connectionHandlers: [XPCConnectionHandler()]) + +extension DaemonConnectionManager +{ + static var shared: ConnectionManager { + return connectionManager + } +} + +struct DaemonRequestHandler: RequestHandler +{ + func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: Connection, completionHandler: @escaping (Result) -> Void) + { + do + { + let anisetteData = try AnisetteDataManager.shared.requestAnisetteData() + + let response = AnisetteDataResponse(anisetteData: anisetteData) + completionHandler(.success(response)) + } + catch + { + completionHandler(.failure(error)) + } + } + + func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: Connection, completionHandler: @escaping (Result) -> Void) + { + guard let fileURL = request.fileURL else { return completionHandler(.failure(ALTServerError(.invalidRequest))) } + + print("Awaiting begin installation request...") + + connection.receiveRequest() { (result) in + print("Received begin installation request with result:", result) + + do + { + guard case .beginInstallation(let request) = try result.get() else { throw ALTServerError(.unknownRequest) } + guard let bundleIdentifier = request.bundleIdentifier else { throw ALTServerError(.invalidRequest) } + + AppManager.shared.installApp(at: fileURL, bundleIdentifier: bundleIdentifier, activeProfiles: request.activeProfiles) { (result) in + let result = result.map { InstallationProgressResponse(progress: 1.0) } + print("Installed app with result:", result) + + completionHandler(result) + } + } + catch + { + completionHandler(.failure(error)) + } + } + } + + func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: Connection, + completionHandler: @escaping (Result) -> Void) + { + AppManager.shared.install(request.provisioningProfiles, activeProfiles: request.activeProfiles) { (result) in + switch result + { + case .failure(let error): + print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", error) + completionHandler(.failure(error)) + + case .success: + print("Installed profiles:", request.provisioningProfiles.map { $0.bundleIdentifier }) + + let response = InstallProvisioningProfilesResponse() + completionHandler(.success(response)) + } + } + } + + func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: Connection, + completionHandler: @escaping (Result) -> Void) + { + AppManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers) { (result) in + switch result + { + case .failure(let error): + print("Failed to remove profiles \(request.bundleIdentifiers):", error) + completionHandler(.failure(error)) + + case .success: + print("Removed profiles:", request.bundleIdentifiers) + + let response = RemoveProvisioningProfilesResponse() + completionHandler(.success(response)) + } + } + } + + func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: Connection, completionHandler: @escaping (Result) -> Void) + { + AppManager.shared.removeApp(forBundleIdentifier: request.bundleIdentifier) { (result) in + switch result + { + case .failure(let error): + print("Failed to remove app \(request.bundleIdentifier):", error) + completionHandler(.failure(error)) + + case .success: + print("Removed app:", request.bundleIdentifier) + + let response = RemoveAppResponse() + completionHandler(.success(response)) + } + } + } +} diff --git a/AltDaemon/XPCConnectionHandler.swift b/AltDaemon/XPCConnectionHandler.swift new file mode 100644 index 00000000..d6ee4202 --- /dev/null +++ b/AltDaemon/XPCConnectionHandler.swift @@ -0,0 +1,93 @@ +// +// XPCConnectionHandler.swift +// AltDaemon +// +// Created by Riley Testut on 9/14/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation +import Security + +class XPCConnectionHandler: NSObject, ConnectionHandler +{ + var connectionHandler: ((Connection) -> Void)? + var disconnectionHandler: ((Connection) -> Void)? + + private let dispatchQueue = DispatchQueue(label: "io.altstore.XPCConnectionListener", qos: .utility) + private let listeners = XPCConnection.machServiceNames.map { NSXPCListener.makeListener(machServiceName: $0) } + + deinit + { + self.stopListening() + } + + func startListening() + { + for listener in self.listeners + { + listener.delegate = self + listener.resume() + } + } + + func stopListening() + { + self.listeners.forEach { $0.suspend() } + } +} + +private extension XPCConnectionHandler +{ + func disconnect(_ connection: Connection) + { + connection.disconnect() + + self.disconnectionHandler?(connection) + } +} + +extension XPCConnectionHandler: NSXPCListenerDelegate +{ + func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool + { + let maximumPathLength = 4 * UInt32(MAXPATHLEN) + + let pathBuffer = UnsafeMutablePointer.allocate(capacity: Int(maximumPathLength)) + defer { pathBuffer.deallocate() } + + proc_pidpath(newConnection.processIdentifier, pathBuffer, maximumPathLength) + + let path = String(cString: pathBuffer) + let fileURL = URL(fileURLWithPath: path) + + var code: UnsafeMutableRawPointer? + defer { code.map { Unmanaged.fromOpaque($0).release() } } + + var status = SecStaticCodeCreateWithPath(fileURL as CFURL, 0, &code) + guard status == 0 else { return false } + + var signingInfo: CFDictionary? + defer { signingInfo.map { Unmanaged.passUnretained($0).release() } } + + status = SecCodeCopySigningInformation(code, kSecCSInternalInformation | kSecCSSigningInformation, &signingInfo) + guard status == 0 else { return false } + + // Only accept connections from AltStore. + guard + let codeSigningInfo = signingInfo as? [String: Any], + let bundleIdentifier = codeSigningInfo["identifier"] as? String, + bundleIdentifier.contains("com.rileytestut.AltStore") + else { return false } + + let connection = XPCConnection(newConnection) + newConnection.invalidationHandler = { [weak self, weak connection] in + guard let self = self, let connection = connection else { return } + self.disconnect(connection) + } + + self.connectionHandler?(connection) + + return true + } +} diff --git a/AltDaemon/main.swift b/AltDaemon/main.swift new file mode 100644 index 00000000..a4b69654 --- /dev/null +++ b/AltDaemon/main.swift @@ -0,0 +1,14 @@ +// +// main.swift +// AltDaemon +// +// Created by Riley Testut on 6/2/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation + +autoreleasepool { + DaemonConnectionManager.shared.start() + RunLoop.current.run() +} diff --git a/AltDaemon/package/DEBIAN/control b/AltDaemon/package/DEBIAN/control new file mode 100644 index 00000000..fa91ef73 --- /dev/null +++ b/AltDaemon/package/DEBIAN/control @@ -0,0 +1,10 @@ +Package: com.rileytestut.altdaemon +Name: AltDaemon +Depends: +Version: 1.0 +Architecture: iphoneos-arm +Description: AltDaemon allows AltStore to install and refresh apps without a computer. +Maintainer: Riley Testut +Author: Riley Testut +Homepage: https://altstore.io +Section: System diff --git a/AltDaemon/package/DEBIAN/postinst b/AltDaemon/package/DEBIAN/postinst new file mode 100755 index 00000000..e5b799be --- /dev/null +++ b/AltDaemon/package/DEBIAN/postinst @@ -0,0 +1,2 @@ +#!/bin/sh +launchctl load /Library/LaunchDaemons/com.rileytestut.altdaemon.plist diff --git a/AltDaemon/package/DEBIAN/preinst b/AltDaemon/package/DEBIAN/preinst new file mode 100755 index 00000000..cf29046c --- /dev/null +++ b/AltDaemon/package/DEBIAN/preinst @@ -0,0 +1,2 @@ +#!/bin/sh +launchctl unload /Library/LaunchDaemons/com.rileytestut.altdaemon.plist >> /dev/null 2>&1 diff --git a/AltDaemon/package/DEBIAN/prerm b/AltDaemon/package/DEBIAN/prerm new file mode 100755 index 00000000..e88bf33b --- /dev/null +++ b/AltDaemon/package/DEBIAN/prerm @@ -0,0 +1,2 @@ +#!/bin/sh +launchctl unload /Library/LaunchDaemons/com.rileytestut.altdaemon.plist diff --git a/AltDaemon/package/Library/LaunchDaemons/com.rileytestut.altdaemon.plist b/AltDaemon/package/Library/LaunchDaemons/com.rileytestut.altdaemon.plist new file mode 100644 index 00000000..7808ca12 --- /dev/null +++ b/AltDaemon/package/Library/LaunchDaemons/com.rileytestut.altdaemon.plist @@ -0,0 +1,28 @@ + + + + + Label + com.rileytestut.altdaemon + ProgramArguments + + /usr/bin/env + _MSSafeMode=1 + _SafeMode=1 + /usr/bin/AltDaemon + + UserName + mobile + KeepAlive + + RunAtLoad + + MachServices + + cy:io.altstore.altdaemon + + lh:io.altstore.altdaemon + + + + diff --git a/AltDaemon/package/usr/bin/AltDaemon b/AltDaemon/package/usr/bin/AltDaemon new file mode 100755 index 00000000..7f733693 Binary files /dev/null and b/AltDaemon/package/usr/bin/AltDaemon differ diff --git a/AltKit/CodableServerError.swift b/AltKit/CodableServerError.swift deleted file mode 100644 index 5dff7787..00000000 --- a/AltKit/CodableServerError.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// CodableServerError.swift -// AltKit -// -// Created by Riley Testut on 3/5/20. -// Copyright © 2020 Riley Testut. All rights reserved. -// - -import Foundation - -// Can only automatically conform ALTServerError.Code to Codable, not ALTServerError itself -extension ALTServerError.Code: Codable {} - -struct CodableServerError: Codable -{ - var error: ALTServerError { - return ALTServerError(self.errorCode, userInfo: self.userInfo ?? [:]) - } - - private var errorCode: ALTServerError.Code - private var userInfo: [String: String]? - - private enum CodingKeys: String, CodingKey - { - case errorCode - case userInfo - } - - init(error: ALTServerError) - { - self.errorCode = error.code - - let userInfo = error.userInfo.compactMapValues { $0 as? String } - if !userInfo.isEmpty - { - self.userInfo = userInfo - } - } - - init(from decoder: Decoder) throws - { - let container = try decoder.container(keyedBy: CodingKeys.self) - - let errorCode = try container.decode(Int.self, forKey: .errorCode) - self.errorCode = ALTServerError.Code(rawValue: errorCode) ?? .unknown - - let userInfo = try container.decodeIfPresent([String: String].self, forKey: .userInfo) - self.userInfo = userInfo - } - - func encode(to encoder: Encoder) throws - { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.error.code.rawValue, forKey: .errorCode) - try container.encodeIfPresent(self.userInfo, forKey: .userInfo) - } -} - diff --git a/AltPlugin/Info.plist b/AltPlugin/Info.plist index 6188d46b..e24386aa 100644 --- a/AltPlugin/Info.plist +++ b/AltPlugin/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion 1 NSHumanReadableCopyright @@ -58,5 +58,9 @@ # For mail version 13.0 (3594.4.2) on OS X Version 10.15 (build 19A558d) 6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053 + Supported11.0PluginCompatibilityUUIDs + + D985F0E4-3BBC-4B95-BBA1-12056AC4A531 + diff --git a/AltServer/AltPlugin.zip b/AltServer/AltPlugin.zip new file mode 100644 index 00000000..d6c5b41c Binary files /dev/null and b/AltServer/AltPlugin.zip differ diff --git a/AltServer/AltServer-Bridging-Header.h b/AltServer/AltServer-Bridging-Header.h index 05029051..db577fd9 100644 --- a/AltServer/AltServer-Bridging-Header.h +++ b/AltServer/AltServer-Bridging-Header.h @@ -5,4 +5,9 @@ #import "ALTDeviceManager.h" #import "ALTWiredConnection.h" #import "ALTNotificationConnection.h" -#import "AltKit.h" + +// Shared +#import "ALTConstants.h" +#import "ALTConnection.h" +#import "NSError+ALTServerError.h" +#import "CFNotificationName+AltStore.h" diff --git a/AltServer/AnisetteDataManager.swift b/AltServer/AnisetteDataManager.swift index e4338649..f28a589d 100644 --- a/AltServer/AnisetteDataManager.swift +++ b/AltServer/AnisetteDataManager.swift @@ -7,7 +7,6 @@ // import Foundation -import AltKit class AnisetteDataManager: NSObject { diff --git a/AltServer/AppDelegate.swift b/AltServer/AppDelegate.swift index 34a3790f..773d6305 100644 --- a/AltServer/AppDelegate.swift +++ b/AltServer/AppDelegate.swift @@ -12,32 +12,12 @@ import UserNotifications import AltSign import LaunchAtLogin -import STPrivilegedTask - -private let pluginDirectoryURL = URL(fileURLWithPath: "/Library/Mail/Bundles", isDirectory: true) -private let pluginURL = pluginDirectoryURL.appendingPathComponent("AltPlugin.mailbundle") - -enum PluginError: LocalizedError -{ - case cancelled - case unknown - case taskError(String) - case taskErrorCode(Int) - - var errorDescription: String? { - switch self - { - case .cancelled: return NSLocalizedString("Mail plug-in installation was cancelled.", comment: "") - case .unknown: return NSLocalizedString("Failed to install Mail plug-in.", comment: "") - case .taskError(let output): return output - case .taskErrorCode(let errorCode): return String(format: NSLocalizedString("There was an error installing the Mail plug-in. (Error Code: %@)", comment: ""), NSNumber(value: errorCode)) - } - } -} @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { + private let pluginManager = PluginManager() + private var statusItem: NSStatusItem? private var connectedDevices = [ALTDevice]() @@ -52,29 +32,21 @@ class AppDelegate: NSObject, NSApplicationDelegate { private weak var authenticationAppleIDTextField: NSTextField? private weak var authenticationPasswordTextField: NSSecureTextField? - private var isMailPluginInstalled: Bool { - let isMailPluginInstalled = FileManager.default.fileExists(atPath: pluginURL.path) - return isMailPluginInstalled - } - func applicationDidFinishLaunching(_ aNotification: Notification) { UserDefaults.standard.registerDefaults() UNUserNotificationCenter.current().delegate = self - ConnectionManager.shared.start() + ServerConnectionManager.shared.start() ALTDeviceManager.shared.start() let item = NSStatusBar.system.statusItem(withLength: -1) - guard let button = item.button else { return } - - button.image = NSImage(named: "MenuBarIcon") - button.target = self - button.action = #selector(AppDelegate.presentMenu) - + item.menu = self.appMenu + item.button?.image = NSImage(named: "MenuBarIcon") self.statusItem = item + self.appMenu.delegate = self self.connectedDevicesMenu.delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in @@ -92,6 +64,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { UserDefaults.standard.didPresentInitialNotification = true } } + + if self.pluginManager.isUpdateAvailable + { + self.installMailPlugin() + } } func applicationWillTerminate(_ aNotification: Notification) @@ -102,40 +79,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { private extension AppDelegate { - @objc func presentMenu() - { - guard let button = self.statusItem?.button, let superview = button.superview, let window = button.window else { return } - - self.connectedDevices = ALTDeviceManager.shared.availableDevices - - self.launchAtLoginMenuItem.state = LaunchAtLogin.isEnabled ? .on : .off - self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:)) - - if self.isMailPluginInstalled - { - self.installMailPluginMenuItem.title = NSLocalizedString("Uninstall Mail Plug-in", comment: "") - } - else - { - self.installMailPluginMenuItem.title = NSLocalizedString("Install Mail Plug-in", comment: "") - } - - self.installMailPluginMenuItem.target = self - self.installMailPluginMenuItem.action = #selector(AppDelegate.handleInstallMailPluginMenuItem(_:)) - - let x = button.frame.origin.x - let y = button.frame.origin.y - 5 - - let location = superview.convert(NSMakePoint(x, y), to: nil) - - guard let event = NSEvent.mouseEvent(with: .leftMouseUp, location: location, - modifierFlags: [], timestamp: 0, windowNumber: window.windowNumber, context: nil, - eventNumber: 0, clickCount: 1, pressure: 0) - else { return } - - NSMenu.popUpContextMenu(self.appMenu, with: event, for: button) - } - @objc func installAltStore(_ item: NSMenuItem) { guard case let index = self.connectedDevicesMenu.index(of: item), index != -1 else { return } @@ -212,6 +155,10 @@ private extension AppDelegate { alert.informativeText = underlyingError.localizedDescription } + else if let recoverySuggestion = error.localizedRecoverySuggestion + { + alert.informativeText = error.localizedDescription + "\n\n" + recoverySuggestion + } else { alert.informativeText = error.localizedDescription @@ -224,27 +171,13 @@ private extension AppDelegate } } - if !self.isMailPluginInstalled + if !self.pluginManager.isMailPluginInstalled || self.pluginManager.isUpdateAvailable { self.installMailPlugin { (result) in - DispatchQueue.main.async { - switch result - { - case .failure(PluginError.cancelled): break - case .failure(let error): - let alert = NSAlert() - alert.messageText = NSLocalizedString("Failed to Install Mail Plug-in", comment: "") - alert.informativeText = error.localizedDescription - alert.runModal() - - case .success: - let alert = NSAlert() - alert.messageText = NSLocalizedString("Mail Plug-in Installed", comment: "") - alert.informativeText = NSLocalizedString("Please restart Mail and enable AltPlugin in Mail's Preferences. Mail must be running when installing or refreshing apps with AltServer.", comment: "") - alert.runModal() - - install() - } + switch result + { + case .failure: break + case .success: install() } } } @@ -256,236 +189,109 @@ private extension AppDelegate @objc func toggleLaunchAtLogin(_ item: NSMenuItem) { - if item.state == .on - { - item.state = .off - } - else - { - item.state = .on - } - LaunchAtLogin.isEnabled.toggle() } @objc func handleInstallMailPluginMenuItem(_ item: NSMenuItem) { - if self.isMailPluginInstalled + if !self.pluginManager.isMailPluginInstalled || self.pluginManager.isUpdateAvailable { - self.uninstallMailPlugin { (result) in - DispatchQueue.main.async { - switch result - { - case .failure(PluginError.cancelled): break - case .failure(let error): - let alert = NSAlert() - alert.messageText = NSLocalizedString("Failed to Uninstall Mail Plug-in", comment: "") - alert.informativeText = error.localizedDescription - alert.runModal() - - case .success: - let alert = NSAlert() - alert.messageText = NSLocalizedString("Mail Plug-in Uninstalled", comment: "") - alert.informativeText = NSLocalizedString("Please restart Mail for changes to take effect. You will not be able to use AltServer until the plug-in is reinstalled.", comment: "") - alert.runModal() - } - } - } + self.installMailPlugin() } else { - self.installMailPlugin { (result) in - DispatchQueue.main.async { - switch result - { - case .failure(PluginError.cancelled): break - case .failure(let error): - let alert = NSAlert() - alert.messageText = NSLocalizedString("Failed to Install Mail Plug-in", comment: "") - alert.informativeText = error.localizedDescription - alert.runModal() - - case .success: - let alert = NSAlert() - alert.messageText = NSLocalizedString("Mail Plug-in Installed", comment: "") - alert.informativeText = NSLocalizedString("Please restart Mail and enable AltPlugin in Mail's Preferences. Mail must be running when installing or refreshing apps with AltServer.", comment: "") - alert.runModal() - } - } - } + self.uninstallMailPlugin() } } - func installMailPlugin(completionHandler: @escaping (Result) -> Void) + private func installMailPlugin(completion: ((Result) -> Void)? = nil) { - do - { - let alert = NSAlert() - alert.messageText = NSLocalizedString("Install Mail Plug-in", comment: "") - alert.informativeText = NSLocalizedString("AltServer requires a Mail plug-in in order to retrieve necessary information about your Apple ID. Would you like to install it now?", comment: "") - - alert.addButton(withTitle: NSLocalizedString("Install Plug-in", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) - - NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) - - let response = alert.runModal() - guard response == .alertFirstButtonReturn else { throw PluginError.cancelled } - - self.downloadPlugin { (result) in - do + self.pluginManager.installMailPlugin { (result) in + DispatchQueue.main.async { + switch result { - let fileURL = try result.get() - defer { try? FileManager.default.removeItem(at: fileURL) } + case .failure(PluginError.cancelled): break + case .failure(let error): + let alert = NSAlert() + alert.messageText = NSLocalizedString("Failed to Install Mail Plug-in", comment: "") + alert.informativeText = error.localizedDescription + alert.runModal() - // Ensure plug-in directory exists. - let authorization = try self.runAndKeepAuthorization("mkdir", arguments: ["-p", pluginDirectoryURL.path]) - - // Unzip AltPlugin to plug-ins directory. - try self.runAndKeepAuthorization("unzip", arguments: ["-o", fileURL.path, "-d", pluginDirectoryURL.path], authorization: authorization) - guard self.isMailPluginInstalled else { throw PluginError.unknown } - - // Enable Mail plug-in preferences. - try self.run("defaults", arguments: ["write", "/Library/Preferences/com.apple.mail", "EnableBundles", "-bool", "YES"], authorization: authorization) - - print("Finished installing Mail plug-in!") - - completionHandler(.success(())) - } - catch - { - completionHandler(.failure(error)) - } - } - } - catch - { - completionHandler(.failure(PluginError.cancelled)) - } - } - - func downloadPlugin(completionHandler: @escaping (Result) -> Void) - { - let pluginURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altserver/altplugin/1_0.zip")! - - let downloadTask = URLSession.shared.downloadTask(with: pluginURL) { (fileURL, response, error) in - if let fileURL = fileURL - { - print("Downloaded plugin to URL:", fileURL) - completionHandler(.success(fileURL)) - } - else - { - completionHandler(.failure(error ?? PluginError.unknown)) - } - } - - downloadTask.resume() - } - - func uninstallMailPlugin(completionHandler: @escaping (Result) -> Void) - { - let alert = NSAlert() - alert.messageText = NSLocalizedString("Uninstall Mail Plug-in", comment: "") - alert.informativeText = NSLocalizedString("Are you sure you want to uninstall the AltServer Mail plug-in? You will no longer be able to install or refresh apps with AltStore.", comment: "") - - alert.addButton(withTitle: NSLocalizedString("Uninstall Plug-in", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) - - NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) - - let response = alert.runModal() - guard response == .alertFirstButtonReturn else { return completionHandler(.failure(PluginError.cancelled)) } - - DispatchQueue.global().async { - do - { - if FileManager.default.fileExists(atPath: pluginURL.path) - { - // Delete Mail plug-in from privileged directory. - try self.run("rm", arguments: ["-rf", pluginURL.path]) + case .success: + let alert = NSAlert() + alert.messageText = NSLocalizedString("Mail Plug-in Installed", comment: "") + alert.informativeText = NSLocalizedString("Please restart Mail and enable AltPlugin in Mail's Preferences. Mail must be running when installing or refreshing apps with AltServer.", comment: "") + alert.runModal() } - completionHandler(.success(())) - } - catch - { - completionHandler(.failure(error)) + completion?(result) } } } -} - -private extension AppDelegate -{ - func run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws - { - _ = try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: true) - } - @discardableResult - func runAndKeepAuthorization(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws -> AuthorizationRef + private func uninstallMailPlugin() { - return try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: false) - } - - func _run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil, freeAuthorization: Bool) throws -> AuthorizationRef - { - var launchPath = "/usr/bin/" + program - if !FileManager.default.fileExists(atPath: launchPath) - { - launchPath = "/bin/" + program - } - - print("Running program:", launchPath) - - let task = STPrivilegedTask() - task.launchPath = launchPath - task.arguments = arguments - task.freeAuthorizationWhenDone = freeAuthorization - - let errorCode: OSStatus - - if let authorization = authorization - { - errorCode = task.launch(withAuthorization: authorization) - } - else - { - errorCode = task.launch() - } - - guard errorCode == 0 else { throw PluginError.taskErrorCode(Int(errorCode)) } - - task.waitUntilExit() - - print("Exit code:", task.terminationStatus) - - guard task.terminationStatus == 0 else { - let outputData = task.outputFileHandle.readDataToEndOfFile() - - if let outputString = String(data: outputData, encoding: .utf8), !outputString.isEmpty - { - throw PluginError.taskError(outputString) + self.pluginManager.uninstallMailPlugin { (result) in + DispatchQueue.main.async { + switch result + { + case .failure(PluginError.cancelled): break + case .failure(let error): + let alert = NSAlert() + alert.messageText = NSLocalizedString("Failed to Uninstall Mail Plug-in", comment: "") + alert.informativeText = error.localizedDescription + alert.runModal() + + case .success: + let alert = NSAlert() + alert.messageText = NSLocalizedString("Mail Plug-in Uninstalled", comment: "") + alert.informativeText = NSLocalizedString("Please restart Mail for changes to take effect. You will not be able to use AltServer until the plug-in is reinstalled.", comment: "") + alert.runModal() + } } - - throw PluginError.taskErrorCode(Int(task.terminationStatus)) } - - guard let authorization = task.authorization else { throw PluginError.unknown } - return authorization } } extension AppDelegate: NSMenuDelegate { + func menuWillOpen(_ menu: NSMenu) + { + guard menu == self.appMenu else { return } + + self.connectedDevices = ALTDeviceManager.shared.connectedDevices + + self.launchAtLoginMenuItem.target = self + self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:)) + self.launchAtLoginMenuItem.state = LaunchAtLogin.isEnabled ? .on : .off + + if self.pluginManager.isUpdateAvailable + { + self.installMailPluginMenuItem.title = NSLocalizedString("Update Mail Plug-in", comment: "") + } + else if self.pluginManager.isMailPluginInstalled + { + self.installMailPluginMenuItem.title = NSLocalizedString("Uninstall Mail Plug-in", comment: "") + } + else + { + self.installMailPluginMenuItem.title = NSLocalizedString("Install Mail Plug-in", comment: "") + } + self.installMailPluginMenuItem.target = self + self.installMailPluginMenuItem.action = #selector(AppDelegate.handleInstallMailPluginMenuItem(_:)) + } + func numberOfItems(in menu: NSMenu) -> Int { + guard menu == self.connectedDevicesMenu else { return -1 } + return self.connectedDevices.isEmpty ? 1 : self.connectedDevices.count } func menu(_ menu: NSMenu, update item: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool { + guard menu == self.connectedDevicesMenu else { return false } + if self.connectedDevices.isEmpty { item.title = NSLocalizedString("No Connected Devices", comment: "") diff --git a/AltServer/Base.lproj/Main.storyboard b/AltServer/Base.lproj/Main.storyboard index d2a97fe9..33f11093 100644 --- a/AltServer/Base.lproj/Main.storyboard +++ b/AltServer/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + diff --git a/AltServer/Connections/ALTNotificationConnection.h b/AltServer/Connections/ALTNotificationConnection.h index 50a0f59d..4654ab2e 100644 --- a/AltServer/Connections/ALTNotificationConnection.h +++ b/AltServer/Connections/ALTNotificationConnection.h @@ -6,7 +6,7 @@ // Copyright © 2020 Riley Testut. All rights reserved. // -#import +#import "AltSign.h" NS_ASSUME_NONNULL_BEGIN diff --git a/AltServer/Connections/ALTNotificationConnection.m b/AltServer/Connections/ALTNotificationConnection.mm similarity index 98% rename from AltServer/Connections/ALTNotificationConnection.m rename to AltServer/Connections/ALTNotificationConnection.mm index 6a3a895d..4c486d05 100644 --- a/AltServer/Connections/ALTNotificationConnection.m +++ b/AltServer/Connections/ALTNotificationConnection.mm @@ -7,7 +7,8 @@ // #import "ALTNotificationConnection+Private.h" -#import "AltKit.h" + +#import "NSError+ALTServerError.h" void ALTDeviceReceivedNotification(const char *notification, void *user_data); diff --git a/AltServer/Connections/ALTWiredConnection+Private.h b/AltServer/Connections/ALTWiredConnection+Private.h index 24dc2c85..a6c64d14 100644 --- a/AltServer/Connections/ALTWiredConnection+Private.h +++ b/AltServer/Connections/ALTWiredConnection+Private.h @@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN @interface ALTWiredConnection () +@property (nonatomic, readwrite, getter=isConnected) BOOL connected; + @property (nonatomic, readonly) idevice_connection_t connection; - (instancetype)initWithDevice:(ALTDevice *)device connection:(idevice_connection_t)connection; diff --git a/AltServer/Connections/ALTWiredConnection.h b/AltServer/Connections/ALTWiredConnection.h index 05801f98..d6fde691 100644 --- a/AltServer/Connections/ALTWiredConnection.h +++ b/AltServer/Connections/ALTWiredConnection.h @@ -6,12 +6,16 @@ // Copyright © 2020 Riley Testut. All rights reserved. // -#import +#import "AltSign.h" + +#import "ALTConnection.h" NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(WiredConnection) -@interface ALTWiredConnection : NSObject +@interface ALTWiredConnection : NSObject + +@property (nonatomic, readonly, getter=isConnected) BOOL connected; @property (nonatomic, copy, readonly) ALTDevice *device; diff --git a/AltServer/Connections/ALTWiredConnection.m b/AltServer/Connections/ALTWiredConnection.mm similarity index 89% rename from AltServer/Connections/ALTWiredConnection.m rename to AltServer/Connections/ALTWiredConnection.mm index 0b941730..aa674063 100644 --- a/AltServer/Connections/ALTWiredConnection.m +++ b/AltServer/Connections/ALTWiredConnection.mm @@ -7,7 +7,9 @@ // #import "ALTWiredConnection+Private.h" -#import "AltKit.h" + +#import "ALTConnection.h" +#import "NSError+ALTServerError.h" @implementation ALTWiredConnection @@ -30,8 +32,15 @@ - (void)disconnect { + if (![self isConnected]) + { + return; + } + idevice_disconnect(self.connection); _connection = nil; + + self.connected = NO; } - (void)sendData:(NSData *)data completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler @@ -85,7 +94,7 @@ uint32_t size = MIN(4096, (uint32_t)expectedSize - (uint32_t)receivedData.length); uint32_t receivedBytes = 0; - if (idevice_connection_receive_timeout(self.connection, bytes, size, &receivedBytes, 0) != IDEVICE_E_SUCCESS) + if (idevice_connection_receive_timeout(self.connection, bytes, size, &receivedBytes, 10000) != IDEVICE_E_SUCCESS) { return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]); } @@ -98,4 +107,11 @@ }); } +#pragma mark - NSObject - + +- (NSString *)description +{ + return [NSString stringWithFormat:@"%@ (Wired)", self.device.name]; +} + @end diff --git a/AltServer/Connections/ClientConnection.swift b/AltServer/Connections/ClientConnection.swift deleted file mode 100644 index 33b5562c..00000000 --- a/AltServer/Connections/ClientConnection.swift +++ /dev/null @@ -1,231 +0,0 @@ -// -// ClientConnection.swift -// AltServer -// -// Created by Riley Testut on 1/9/20. -// Copyright © 2020 Riley Testut. All rights reserved. -// - -import Foundation -import Network - -import AltKit -import AltSign - -extension ClientConnection -{ - enum Connection - { - case wireless(NWConnection) - case wired(WiredConnection) - } -} - -class ClientConnection -{ - let connection: Connection - - init(connection: Connection) - { - self.connection = connection - } - - func disconnect() - { - switch self.connection - { - case .wireless(let connection): - switch connection.state - { - case .cancelled, .failed: - print("Disconnecting from \(connection.endpoint)...") - - default: - // State update handler might call this method again. - connection.cancel() - } - - case .wired(let connection): - connection.disconnect() - } - } - - func send(_ response: T, shouldDisconnect: Bool = false, completionHandler: @escaping (Result) -> Void) - { - func finish(_ result: Result) - { - completionHandler(result) - - if shouldDisconnect - { - // Add short delay to prevent us from dropping connection too quickly. - DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) { - self.disconnect() - } - } - } - - do - { - let data = try JSONEncoder().encode(response) - let responseSize = withUnsafeBytes(of: Int32(data.count)) { Data($0) } - - self.send(responseSize) { (result) in - switch result - { - case .failure: finish(.failure(.init(.lostConnection))) - case .success: - - self.send(data) { (result) in - switch result - { - case .failure: finish(.failure(.init(.lostConnection))) - case .success: finish(.success(())) - } - } - } - } - } - catch - { - finish(.failure(.init(.invalidResponse))) - } - } - - func receiveRequest(completionHandler: @escaping (Result) -> Void) - { - let size = MemoryLayout.size - - print("Receiving request size") - self.receiveData(expectedBytes: size) { (result) in - do - { - let data = try result.get() - - print("Receiving request...") - let expectedBytes = Int(data.withUnsafeBytes { $0.load(as: Int32.self) }) - self.receiveData(expectedBytes: expectedBytes) { (result) in - do - { - let data = try result.get() - let request = try JSONDecoder().decode(ServerRequest.self, from: data) - - print("Received installation request:", request) - completionHandler(.success(request)) - } - catch - { - completionHandler(.failure(ALTServerError(error))) - } - } - } - catch - { - completionHandler(.failure(ALTServerError(error))) - } - } - } - - func send(_ data: Data, completionHandler: @escaping (Result) -> Void) - { - switch self.connection - { - case .wireless(let connection): - connection.send(content: data, completion: .contentProcessed { (error) in - if let error = error - { - completionHandler(.failure(error)) - } - else - { - completionHandler(.success(())) - } - }) - - case .wired(let connection): - connection.send(data) { (success, error) in - if !success - { - completionHandler(.failure(ALTServerError(.lostConnection))) - } - else - { - completionHandler(.success(())) - } - } - } - } - - func receiveData(expectedBytes: Int, completionHandler: @escaping (Result) -> Void) - { - func finish(data: Data?, error: Error?) - { - do - { - let data = try self.process(data: data, error: error) - completionHandler(.success(data)) - } - catch - { - completionHandler(.failure(ALTServerError(error))) - } - } - - switch self.connection - { - case .wireless(let connection): - connection.receive(minimumIncompleteLength: expectedBytes, maximumLength: expectedBytes) { (data, _, _, error) in - finish(data: data, error: error) - } - - case .wired(let connection): - connection.receiveData(withExpectedSize: expectedBytes) { (data, error) in - finish(data: data, error: error) - } - } - } -} - -extension ClientConnection: CustomStringConvertible -{ - var description: String { - switch self.connection - { - case .wireless(let connection): return "\(connection.endpoint) (Wireless)" - case .wired(let connection): return "\(connection.device.name) (Wired)" - } - } -} - -private extension ClientConnection -{ - func process(data: Data?, error: Error?) throws -> Data - { - do - { - do - { - guard let data = data else { throw error ?? ALTServerError(.unknown) } - return data - } - catch let error as NWError - { - print("Error receiving data from connection \(connection)", error) - - throw ALTServerError(.lostConnection) - } - catch - { - throw error - } - } - catch let error as ALTServerError - { - throw error - } - catch - { - preconditionFailure("A non-ALTServerError should never be thrown from this method.") - } - } -} diff --git a/AltServer/Connections/ConnectionManager.swift b/AltServer/Connections/ConnectionManager.swift deleted file mode 100644 index 6c5203a9..00000000 --- a/AltServer/Connections/ConnectionManager.swift +++ /dev/null @@ -1,508 +0,0 @@ -// -// ConnectionManager.swift -// AltServer -// -// Created by Riley Testut on 5/23/19. -// Copyright © 2019 Riley Testut. All rights reserved. -// - -import Foundation -import Network -import AppKit - -import AltKit - -extension ALTServerError -{ - init(_ error: E) - { - switch error - { - case let error as ALTServerError: self = error - case is DecodingError: self = ALTServerError(.invalidRequest) - case is EncodingError: self = ALTServerError(.invalidResponse) - case let error as NSError: - self = ALTServerError(.unknown, userInfo: error.userInfo) - } - } -} - -extension ConnectionManager -{ - enum State - { - case notRunning - case connecting - case running(NWListener.Service) - case failed(Swift.Error) - } -} - -class ConnectionManager -{ - static let shared = ConnectionManager() - - var stateUpdateHandler: ((State) -> Void)? - - private(set) var state: State = .notRunning { - didSet { - self.stateUpdateHandler?(self.state) - } - } - - private lazy var listener = self.makeListener() - private let dispatchQueue = DispatchQueue(label: "com.rileytestut.AltServer.connections", qos: .utility) - - private var connections = [ClientConnection]() - private var notificationConnections = [ALTDevice: NotificationConnection]() - - private init() - { - NotificationCenter.default.addObserver(self, selector: #selector(ConnectionManager.deviceDidConnect(_:)), name: .deviceManagerDeviceDidConnect, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(ConnectionManager.deviceDidDisconnect(_:)), name: .deviceManagerDeviceDidDisconnect, object: nil) - } - - func start() - { - switch self.state - { - case .notRunning, .failed: self.listener.start(queue: self.dispatchQueue) - default: break - } - } - - func stop() - { - switch self.state - { - case .running: self.listener.cancel() - default: break - } - } - - func disconnect(_ connection: ClientConnection) - { - connection.disconnect() - - if let index = self.connections.firstIndex(where: { $0 === connection }) - { - self.connections.remove(at: index) - } - } -} - -private extension ConnectionManager -{ - func makeListener() -> NWListener - { - let listener = try! NWListener(using: .tcp) - - let service: NWListener.Service - - if let serverID = UserDefaults.standard.serverID?.data(using: .utf8) - { - let txtDictionary = ["serverID": serverID] - let txtData = NetService.data(fromTXTRecord: txtDictionary) - - service = NWListener.Service(name: nil, type: ALTServerServiceType, domain: nil, txtRecord: txtData) - } - else - { - service = NWListener.Service(type: ALTServerServiceType) - } - - listener.service = service - - listener.serviceRegistrationUpdateHandler = { (serviceChange) in - switch serviceChange - { - case .add(.service(let name, let type, let domain, _)): - let service = NWListener.Service(name: name, type: type, domain: domain, txtRecord: nil) - self.state = .running(service) - - default: break - } - } - - listener.stateUpdateHandler = { (state) in - switch state - { - case .ready: break - case .waiting, .setup: self.state = .connecting - case .cancelled: self.state = .notRunning - case .failed(let error): - self.state = .failed(error) - self.start() - - @unknown default: break - } - } - - listener.newConnectionHandler = { [weak self] (connection) in - self?.prepare(connection) - } - - return listener - } - - func prepare(_ connection: NWConnection) - { - let clientConnection = ClientConnection(connection: .wireless(connection)) - - guard !self.connections.contains(where: { $0 === clientConnection }) else { return } - self.connections.append(clientConnection) - - connection.stateUpdateHandler = { [weak self] (state) in - switch state - { - case .setup, .preparing: break - - case .ready: - print("Connected to client:", connection.endpoint) - self?.handleRequest(for: clientConnection) - - case .waiting: - print("Waiting for connection...") - - case .failed(let error): - print("Failed to connect to service \(connection.endpoint).", error) - self?.disconnect(clientConnection) - - case .cancelled: - self?.disconnect(clientConnection) - - @unknown default: break - } - } - - connection.start(queue: self.dispatchQueue) - } -} - -private extension ConnectionManager -{ - func startNotificationConnection(to device: ALTDevice) - { - ALTDeviceManager.shared.startNotificationConnection(to: device) { (connection, error) in - guard let connection = connection else { return } - - let notifications: [CFNotificationName] = [.wiredServerConnectionAvailableRequest, .wiredServerConnectionStartRequest] - connection.startListening(forNotifications: notifications.map { String($0.rawValue) }) { (success, error) in - guard success else { return } - - connection.receivedNotificationHandler = { [weak self, weak connection] (notification) in - guard let self = self, let connection = connection else { return } - self.handle(notification, for: connection) - } - - self.notificationConnections[device] = connection - } - } - } - - func stopNotificationConnection(to device: ALTDevice) - { - guard let connection = self.notificationConnections[device] else { return } - connection.disconnect() - - self.notificationConnections[device] = nil - } - - func handle(_ notification: CFNotificationName, for connection: NotificationConnection) - { - switch notification - { - case .wiredServerConnectionAvailableRequest: - connection.sendNotification(.wiredServerConnectionAvailableResponse) { (success, error) in - if let error = error, !success - { - print("Error sending wired server connection response.", error) - } - else - { - print("Sent wired server connection available response!") - } - } - - case .wiredServerConnectionStartRequest: - ALTDeviceManager.shared.startWiredConnection(to: connection.device) { (wiredConnection, error) in - if let wiredConnection = wiredConnection - { - print("Started wired server connection!") - - let clientConnection = ClientConnection(connection: .wired(wiredConnection)) - self.handleRequest(for: clientConnection) - } - else if let error = error - { - print("Error starting wired server connection.", error) - } - } - - default: break - } - } -} - -private extension ConnectionManager -{ - func handleRequest(for connection: ClientConnection) - { - connection.receiveRequest() { (result) in - print("Received initial request with result:", result) - - switch result - { - case .failure(let error): - let response = ErrorResponse(error: ALTServerError(error)) - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent error response with result:", result) - } - - case .success(.anisetteData(let request)): - self.handleAnisetteDataRequest(request, for: connection) - - case .success(.prepareApp(let request)): - self.handlePrepareAppRequest(request, for: connection) - - case .success(.beginInstallation): break - - case .success(.installProvisioningProfiles(let request)): - self.handleInstallProvisioningProfilesRequest(request, for: connection) - - case .success(.removeProvisioningProfiles(let request)): - self.handleRemoveProvisioningProfilesRequest(request, for: connection) - - case .success(.unknown): - let response = ErrorResponse(error: ALTServerError(.unknownRequest)) - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent unknown request response with result:", result) - } - } - } - } - - func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: ClientConnection) - { - AnisetteDataManager.shared.requestAnisetteData { (result) in - switch result - { - case .failure(let error): - let errorResponse = ErrorResponse(error: ALTServerError(error)) - connection.send(errorResponse, shouldDisconnect: true) { (result) in - print("Sent anisette data error response with result:", result) - } - - case .success(let anisetteData): - let response = AnisetteDataResponse(anisetteData: anisetteData) - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent anisette data response with result:", result) - } - } - } - } - - func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: ClientConnection) - { - var temporaryURL: URL? - - func finish(_ result: Result) - { - if let temporaryURL = temporaryURL - { - do { try FileManager.default.removeItem(at: temporaryURL) } - catch { print("Failed to remove .ipa.", error) } - } - - switch result - { - case .failure(let error): - print("Failed to process request from \(connection).", error) - - let response = ErrorResponse(error: ALTServerError(error)) - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent install app error response to \(connection) with result:", result) - } - - case .success: - print("Processed request from \(connection).") - - let response = InstallationProgressResponse(progress: 1.0) - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent install app response to \(connection) with result:", result) - } - } - } - - self.receiveApp(for: request, from: connection) { (result) in - print("Received app with result:", result) - - switch result - { - case .failure(let error): finish(.failure(error)) - case .success(let fileURL): - temporaryURL = fileURL - - print("Awaiting begin installation request...") - - connection.receiveRequest() { (result) in - print("Received begin installation request with result:", result) - - switch result - { - case .failure(let error): finish(.failure(error)) - case .success(.beginInstallation(let installRequest)): - print("Installing to device \(request.udid)...") - - self.installApp(at: fileURL, toDeviceWithUDID: request.udid, activeProvisioningProfiles: installRequest.activeProfiles, connection: connection) { (result) in - print("Installed to device with result:", result) - switch result - { - case .failure(let error): finish(.failure(error)) - case .success: finish(.success(())) - } - } - - case .success: - let response = ErrorResponse(error: ALTServerError(.unknownRequest)) - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent unknown request error response to \(connection) with result:", result) - } - } - } - } - } - } - - func receiveApp(for request: PrepareAppRequest, from connection: ClientConnection, completionHandler: @escaping (Result) -> Void) - { - connection.receiveData(expectedBytes: request.contentSize) { (result) in - do - { - print("Received app data!") - - let data = try result.get() - - guard ALTDeviceManager.shared.availableDevices.contains(where: { $0.identifier == request.udid }) else { throw ALTServerError(.deviceNotFound) } - - print("Writing app data...") - - let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".ipa") - try data.write(to: temporaryURL, options: .atomic) - - print("Wrote app to URL:", temporaryURL) - - completionHandler(.success(temporaryURL)) - } - catch - { - print("Error processing app data:", error) - - completionHandler(.failure(ALTServerError(error))) - } - } - } - - func installApp(at fileURL: URL, toDeviceWithUDID udid: String, activeProvisioningProfiles: Set?, connection: ClientConnection, completionHandler: @escaping (Result) -> Void) - { - let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default) - var isSending = false - - var observation: NSKeyValueObservation? - - let progress = ALTDeviceManager.shared.installApp(at: fileURL, toDeviceWithUDID: udid, activeProvisioningProfiles: activeProvisioningProfiles) { (success, error) in - print("Installed app with result:", error == nil ? "Success" : error!.localizedDescription) - - if let error = error.map({ $0 as? ALTServerError ?? ALTServerError(.unknown) }) - { - completionHandler(.failure(error)) - } - else - { - completionHandler(.success(())) - } - - observation?.invalidate() - observation = nil - } - - observation = progress.observe(\.fractionCompleted, changeHandler: { (progress, change) in - serialQueue.async { - guard !isSending else { return } - isSending = true - - print("Progress:", progress.fractionCompleted) - let response = InstallationProgressResponse(progress: progress.fractionCompleted) - - connection.send(response) { (result) in - serialQueue.async { - isSending = false - } - } - } - }) - } - - func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: ClientConnection) - { - ALTDeviceManager.shared.installProvisioningProfiles(request.provisioningProfiles, toDeviceWithUDID: request.udid, activeProvisioningProfiles: request.activeProfiles) { (success, error) in - if let error = error, !success - { - print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", error) - - let errorResponse = ErrorResponse(error: ALTServerError(error)) - connection.send(errorResponse, shouldDisconnect: true) { (result) in - print("Sent install profiles error response with result:", result) - } - } - else - { - print("Installed profiles:", request.provisioningProfiles.map { $0.bundleIdentifier }) - - let response = InstallProvisioningProfilesResponse() - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent install profiles response to \(connection) with result:", result) - } - } - } - } - - func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: ClientConnection) - { - ALTDeviceManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers, fromDeviceWithUDID: request.udid) { (success, error) in - if let error = error, !success - { - print("Failed to remove profiles \(request.bundleIdentifiers):", error) - - let errorResponse = ErrorResponse(error: ALTServerError(error)) - connection.send(errorResponse, shouldDisconnect: true) { (result) in - print("Sent remove profiles error response with result:", result) - } - } - else - { - print("Removed profiles:", request.bundleIdentifiers) - - let response = RemoveProvisioningProfilesResponse() - connection.send(response, shouldDisconnect: true) { (result) in - print("Sent remove profiles error response to \(connection) with result:", result) - } - } - } - } -} - -private extension ConnectionManager -{ - @objc func deviceDidConnect(_ notification: Notification) - { - guard let device = notification.object as? ALTDevice else { return } - self.startNotificationConnection(to: device) - } - - @objc func deviceDidDisconnect(_ notification: Notification) - { - guard let device = notification.object as? ALTDevice else { return } - self.stopNotificationConnection(to: device) - } -} diff --git a/AltServer/Connections/RequestHandler.swift b/AltServer/Connections/RequestHandler.swift new file mode 100644 index 00000000..02326f54 --- /dev/null +++ b/AltServer/Connections/RequestHandler.swift @@ -0,0 +1,218 @@ +// +// RequestHandler.swift +// AltServer +// +// Created by Riley Testut on 5/23/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +import Foundation + +typealias ServerConnectionManager = ConnectionManager + +private let connectionManager = ConnectionManager(requestHandler: ServerRequestHandler(), + connectionHandlers: [WirelessConnectionHandler(), WiredConnectionHandler()]) + +extension ServerConnectionManager +{ + static var shared: ConnectionManager { + return connectionManager + } +} + +struct ServerRequestHandler: RequestHandler +{ + func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: Connection, completionHandler: @escaping (Result) -> Void) + { + AnisetteDataManager.shared.requestAnisetteData { (result) in + switch result + { + case .failure(let error): completionHandler(.failure(error)) + case .success(let anisetteData): + let response = AnisetteDataResponse(anisetteData: anisetteData) + completionHandler(.success(response)) + } + } + } + + func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: Connection, completionHandler: @escaping (Result) -> Void) + { + var temporaryURL: URL? + + func finish(_ result: Result) + { + if let temporaryURL = temporaryURL + { + do { try FileManager.default.removeItem(at: temporaryURL) } + catch { print("Failed to remove .ipa.", error) } + } + + completionHandler(result) + } + + self.receiveApp(for: request, from: connection) { (result) in + print("Received app with result:", result) + + switch result + { + case .failure(let error): finish(.failure(error)) + case .success(let fileURL): + temporaryURL = fileURL + + print("Awaiting begin installation request...") + + connection.receiveRequest() { (result) in + print("Received begin installation request with result:", result) + + switch result + { + case .failure(let error): finish(.failure(error)) + case .success(.beginInstallation(let installRequest)): + print("Installing app to device \(request.udid)...") + + self.installApp(at: fileURL, toDeviceWithUDID: request.udid, activeProvisioningProfiles: installRequest.activeProfiles, connection: connection) { (result) in + print("Installed app to device with result:", result) + switch result + { + case .failure(let error): finish(.failure(error)) + case .success: + let response = InstallationProgressResponse(progress: 1.0) + finish(.success(response)) + } + } + + case .success: finish(.failure(ALTServerError(.unknownRequest))) + } + } + } + } + } + + func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: Connection, + completionHandler: @escaping (Result) -> Void) + { + ALTDeviceManager.shared.installProvisioningProfiles(request.provisioningProfiles, toDeviceWithUDID: request.udid, activeProvisioningProfiles: request.activeProfiles) { (success, error) in + if let error = error, !success + { + print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", error) + completionHandler(.failure(ALTServerError(error))) + } + else + { + print("Installed profiles:", request.provisioningProfiles.map { $0.bundleIdentifier }) + + let response = InstallProvisioningProfilesResponse() + completionHandler(.success(response)) + } + } + } + + func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: Connection, + completionHandler: @escaping (Result) -> Void) + { + ALTDeviceManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers, fromDeviceWithUDID: request.udid) { (success, error) in + if let error = error, !success + { + print("Failed to remove profiles \(request.bundleIdentifiers):", error) + completionHandler(.failure(ALTServerError(error))) + } + else + { + print("Removed profiles:", request.bundleIdentifiers) + + let response = RemoveProvisioningProfilesResponse() + completionHandler(.success(response)) + } + } + } + + func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: Connection, completionHandler: @escaping (Result) -> Void) + { + ALTDeviceManager.shared.removeApp(forBundleIdentifier: request.bundleIdentifier, fromDeviceWithUDID: request.udid) { (success, error) in + if let error = error, !success + { + print("Failed to remove app \(request.bundleIdentifier):", error) + completionHandler(.failure(ALTServerError(error))) + } + else + { + print("Removed app:", request.bundleIdentifier) + + let response = RemoveAppResponse() + completionHandler(.success(response)) + } + } + } +} + +private extension RequestHandler +{ + func receiveApp(for request: PrepareAppRequest, from connection: Connection, completionHandler: @escaping (Result) -> Void) + { + connection.receiveData(expectedSize: request.contentSize) { (result) in + do + { + print("Received app data!") + + let data = try result.get() + + guard ALTDeviceManager.shared.availableDevices.contains(where: { $0.identifier == request.udid }) else { throw ALTServerError(.deviceNotFound) } + + print("Writing app data...") + + let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".ipa") + try data.write(to: temporaryURL, options: .atomic) + + print("Wrote app to URL:", temporaryURL) + + completionHandler(.success(temporaryURL)) + } + catch + { + print("Error processing app data:", error) + + completionHandler(.failure(ALTServerError(error))) + } + } + } + + func installApp(at fileURL: URL, toDeviceWithUDID udid: String, activeProvisioningProfiles: Set?, connection: Connection, completionHandler: @escaping (Result) -> Void) + { + let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default) + var isSending = false + + var observation: NSKeyValueObservation? + + let progress = ALTDeviceManager.shared.installApp(at: fileURL, toDeviceWithUDID: udid, activeProvisioningProfiles: activeProvisioningProfiles) { (success, error) in + print("Installed app with result:", error == nil ? "Success" : error!.localizedDescription) + + if let error = error.map({ ALTServerError($0) }) + { + completionHandler(.failure(error)) + } + else + { + completionHandler(.success(())) + } + + observation?.invalidate() + observation = nil + } + + observation = progress.observe(\.fractionCompleted, changeHandler: { (progress, change) in + serialQueue.async { + guard !isSending else { return } + isSending = true + + print("Progress:", progress.fractionCompleted) + let response = InstallationProgressResponse(progress: progress.fractionCompleted) + + connection.send(response) { (result) in + serialQueue.async { + isSending = false + } + } + } + }) + } +} diff --git a/AltServer/Connections/WiredConnectionHandler.swift b/AltServer/Connections/WiredConnectionHandler.swift new file mode 100644 index 00000000..0aede491 --- /dev/null +++ b/AltServer/Connections/WiredConnectionHandler.swift @@ -0,0 +1,115 @@ +// +// WiredConnectionHandler.swift +// AltServer +// +// Created by Riley Testut on 6/1/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation + +class WiredConnectionHandler: ConnectionHandler +{ + var connectionHandler: ((Connection) -> Void)? + var disconnectionHandler: ((Connection) -> Void)? + + private var notificationConnections = [ALTDevice: NotificationConnection]() + + func startListening() + { + NotificationCenter.default.addObserver(self, selector: #selector(WiredConnectionHandler.deviceDidConnect(_:)), name: .deviceManagerDeviceDidConnect, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(WiredConnectionHandler.deviceDidDisconnect(_:)), name: .deviceManagerDeviceDidDisconnect, object: nil) + } + + func stopListening() + { + NotificationCenter.default.removeObserver(self, name: .deviceManagerDeviceDidConnect, object: nil) + NotificationCenter.default.removeObserver(self, name: .deviceManagerDeviceDidDisconnect, object: nil) + } +} + +private extension WiredConnectionHandler +{ + func startNotificationConnection(to device: ALTDevice) + { + ALTDeviceManager.shared.startNotificationConnection(to: device) { (connection, error) in + guard let connection = connection else { return } + + let notifications: [CFNotificationName] = [.wiredServerConnectionAvailableRequest, .wiredServerConnectionStartRequest] + connection.startListening(forNotifications: notifications.map { String($0.rawValue) }) { (success, error) in + guard success else { return } + + connection.receivedNotificationHandler = { [weak self, weak connection] (notification) in + guard let self = self, let connection = connection else { return } + self.handle(notification, for: connection) + } + + self.notificationConnections[device] = connection + } + } + } + + func stopNotificationConnection(to device: ALTDevice) + { + guard let connection = self.notificationConnections[device] else { return } + connection.disconnect() + + self.notificationConnections[device] = nil + } + + func handle(_ notification: CFNotificationName, for connection: NotificationConnection) + { + switch notification + { + case .wiredServerConnectionAvailableRequest: + connection.sendNotification(.wiredServerConnectionAvailableResponse) { (success, error) in + if let error = error, !success + { + print("Error sending wired server connection response.", error) + } + else + { + print("Sent wired server connection available response!") + } + } + + case .wiredServerConnectionStartRequest: + ALTDeviceManager.shared.startWiredConnection(to: connection.device) { (wiredConnection, error) in + if let wiredConnection = wiredConnection + { + print("Started wired server connection!") + self.connectionHandler?(wiredConnection) + + var observation: NSKeyValueObservation? + observation = wiredConnection.observe(\.isConnected) { [weak self] (connection, change) in + guard !connection.isConnected else { return } + self?.disconnectionHandler?(connection) + + observation?.invalidate() + } + } + else if let error = error + { + print("Error starting wired server connection.", error) + } + } + + default: break + } + } +} + +private extension WiredConnectionHandler +{ + @objc func deviceDidConnect(_ notification: Notification) + { + guard let device = notification.object as? ALTDevice else { return } + self.startNotificationConnection(to: device) + } + + @objc func deviceDidDisconnect(_ notification: Notification) + { + guard let device = notification.object as? ALTDevice else { return } + self.stopNotificationConnection(to: device) + } +} diff --git a/AltServer/Connections/WirelessConnectionHandler.swift b/AltServer/Connections/WirelessConnectionHandler.swift new file mode 100644 index 00000000..9efb1d23 --- /dev/null +++ b/AltServer/Connections/WirelessConnectionHandler.swift @@ -0,0 +1,148 @@ +// +// WirelessConnectionHandler.swift +// AltKit +// +// Created by Riley Testut on 6/1/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation +import Network + +extension WirelessConnectionHandler +{ + public enum State + { + case notRunning + case connecting + case running(NWListener.Service) + case failed(Swift.Error) + } +} + +public class WirelessConnectionHandler: ConnectionHandler +{ + public var connectionHandler: ((Connection) -> Void)? + public var disconnectionHandler: ((Connection) -> Void)? + + public var stateUpdateHandler: ((State) -> Void)? + + public private(set) var state: State = .notRunning { + didSet { + self.stateUpdateHandler?(self.state) + } + } + + private lazy var listener = self.makeListener() + private let dispatchQueue = DispatchQueue(label: "io.altstore.WirelessConnectionListener", qos: .utility) + + public func startListening() + { + switch self.state + { + case .notRunning, .failed: self.listener.start(queue: self.dispatchQueue) + default: break + } + } + + public func stopListening() + { + switch self.state + { + case .running: self.listener.cancel() + default: break + } + } +} + +private extension WirelessConnectionHandler +{ + func makeListener() -> NWListener + { + let listener = try! NWListener(using: .tcp) + + let service: NWListener.Service + + if let serverID = UserDefaults.standard.serverID?.data(using: .utf8) + { + let txtDictionary = ["serverID": serverID] + let txtData = NetService.data(fromTXTRecord: txtDictionary) + + service = NWListener.Service(name: nil, type: ALTServerServiceType, domain: nil, txtRecord: txtData) + } + else + { + service = NWListener.Service(type: ALTServerServiceType) + } + + listener.service = service + + listener.serviceRegistrationUpdateHandler = { (serviceChange) in + switch serviceChange + { + case .add(.service(let name, let type, let domain, _)): + let service = NWListener.Service(name: name, type: type, domain: domain, txtRecord: nil) + self.state = .running(service) + + default: break + } + } + + listener.stateUpdateHandler = { (state) in + switch state + { + case .ready: break + case .waiting, .setup: self.state = .connecting + case .cancelled: self.state = .notRunning + case .failed(let error): self.state = .failed(error) + @unknown default: break + } + } + + listener.newConnectionHandler = { [weak self] (connection) in + self?.prepare(connection) + } + + return listener + } + + func prepare(_ nwConnection: NWConnection) + { + print("Preparing:", nwConnection) + + // Use same instance for all callbacks. + let connection = NetworkConnection(nwConnection) + + nwConnection.stateUpdateHandler = { [weak self] (state) in + switch state + { + case .setup, .preparing: break + + case .ready: + print("Connected to client:", connection) + self?.connectionHandler?(connection) + + case .waiting: + print("Waiting for connection...") + + case .failed(let error): + print("Failed to connect to service \(nwConnection.endpoint).", error) + self?.disconnect(connection) + + case .cancelled: + self?.disconnect(connection) + + @unknown default: break + } + } + + nwConnection.start(queue: self.dispatchQueue) + } + + func disconnect(_ connection: Connection) + { + connection.disconnect() + + self.disconnectionHandler?(connection) + } +} diff --git a/AltServer/Devices/ALTDeviceManager+Installation.swift b/AltServer/Devices/ALTDeviceManager+Installation.swift index a4dee469..63339ac0 100644 --- a/AltServer/Devices/ALTDeviceManager+Installation.swift +++ b/AltServer/Devices/ALTDeviceManager+Installation.swift @@ -16,6 +16,8 @@ private let appURL = URL(string: "https://f000.backblazeb2.com/file/altstore-sta private let appURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altstore.ipa")! #endif +private let appGroupsLock = NSLock() + enum InstallError: LocalizedError { case cancelled @@ -115,40 +117,18 @@ extension ALTDeviceManager let anisetteData = try result.get() session.anisetteData = anisetteData - self.registerAppID(name: "AltStore", identifier: "com.rileytestut.AltStore", team: team, session: session) { (result) in + self.prepareAllProvisioningProfiles(for: application, team: team, session: session) { (result) in do { - let appID = try result.get() + let profiles = try result.get() - self.updateFeatures(for: appID, app: application, team: team, session: session) { (result) in - do - { - let appID = try result.get() - - self.fetchProvisioningProfile(for: appID, team: team, session: session) { (result) in - do - { - let provisioningProfile = try result.get() - - self.install(application, to: device, team: team, appID: appID, certificate: certificate, profile: provisioningProfile) { (result) in - finish(result.error, title: "Failed to Install AltStore") - } - } - catch - { - finish(error, title: "Failed to Fetch Provisioning Profile") - } - } - } - catch - { - finish(error, title: "Failed to Update App ID") - } + self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in + finish(result.error, title: "Failed to Install AltStore") } } catch { - finish(error, title: "Failed to Register App") + finish(error, title: "Failed to Fetch Provisioning Profiles") } } } @@ -427,6 +407,92 @@ To prevent this from happening, feel free to try again with another Apple ID to } } + func prepareAllProvisioningProfiles(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, + completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void) + { + self.prepareProvisioningProfile(for: application, team: team, session: session) { (result) in + do + { + let profile = try result.get() + + var profiles = [application.bundleIdentifier: profile] + var error: Error? + + let dispatchGroup = DispatchGroup() + + for appExtension in application.appExtensions + { + dispatchGroup.enter() + + self.prepareProvisioningProfile(for: appExtension, team: team, session: session) { (result) in + switch result + { + case .failure(let e): error = e + case .success(let profile): profiles[appExtension.bundleIdentifier] = profile + } + + dispatchGroup.leave() + } + } + + dispatchGroup.notify(queue: .global()) { + if let error = error + { + completion(.failure(error)) + } + else + { + completion(.success(profiles)) + } + } + } + catch + { + completion(.failure(error)) + } + } + } + + func prepareProvisioningProfile(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) + { + self.registerAppID(name: application.name, identifier: application.bundleIdentifier, team: team, session: session) { (result) in + do + { + let appID = try result.get() + + self.updateFeatures(for: appID, app: application, team: team, session: session) { (result) in + do + { + let appID = try result.get() + + self.updateAppGroups(for: appID, app: application, team: team, session: session) { (result) in + do + { + let appID = try result.get() + + self.fetchProvisioningProfile(for: appID, team: team, session: session) { (result) in + completionHandler(result) + } + } + catch + { + completionHandler(.failure(error)) + } + } + } + catch + { + completionHandler(.failure(error)) + } + } + } + catch + { + completionHandler(.failure(error)) + } + } + } + func registerAppID(name appName: String, identifier: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { let bundleID = "com.\(team.identifier).\(identifier)" @@ -468,11 +534,119 @@ To prevent this from happening, feel free to try again with another Apple ID to features[.appGroups] = true } - let appID = appID.copy() as! ALTAppID - appID.features = features + var updateFeatures = false - ALTAppleAPI.shared.update(appID, team: team, session: session) { (appID, error) in - completionHandler(Result(appID, error)) + // Determine whether the required features are already enabled for the AppID. + for (feature, value) in features + { + if let appIDValue = appID.features[feature] as AnyObject?, (value as AnyObject).isEqual(appIDValue) + { + // AppID already has this feature enabled and the values are the same. + continue + } + else + { + // AppID either doesn't have this feature enabled or the value has changed, + // so we need to update it to reflect new values. + updateFeatures = true + break + } + } + + if updateFeatures + { + let appID = appID.copy() as! ALTAppID + appID.features = features + + ALTAppleAPI.shared.update(appID, team: team, session: session) { (appID, error) in + completionHandler(Result(appID, error)) + } + } + else + { + completionHandler(.success(appID)) + } + } + + func updateAppGroups(for appID: ALTAppID, app: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) + { + let applicationGroups = app.entitlements[.appGroups] as? [String] ?? [] + if applicationGroups.isEmpty + { + guard let isAppGroupsEnabled = appID.features[.appGroups] as? Bool, isAppGroupsEnabled else { + // No app groups, and we also haven't enabled the feature, so don't continue. + // For apps with no app groups but have had the feature enabled already + // we'll continue and assign the app ID to an empty array + // in case we need to explicitly remove them. + return completionHandler(.success(appID)) + } + } + + // Dispatch onto global queue to prevent appGroupsLock deadlock. + DispatchQueue.global().async { + + // Ensure we're not concurrently fetching and updating app groups, + // which can lead to race conditions such as adding an app group twice. + appGroupsLock.lock() + + func finish(_ result: Result) + { + appGroupsLock.unlock() + completionHandler(result) + } + + ALTAppleAPI.shared.fetchAppGroups(for: team, session: session) { (groups, error) in + switch Result(groups, error) + { + case .failure(let error): finish(.failure(error)) + case .success(let fetchedGroups): + let dispatchGroup = DispatchGroup() + + var groups = [ALTAppGroup]() + var errors = [Error]() + + for groupIdentifier in applicationGroups + { + let adjustedGroupIdentifier = groupIdentifier + "." + team.identifier + + if let group = fetchedGroups.first(where: { $0.groupIdentifier == adjustedGroupIdentifier }) + { + groups.append(group) + } + else + { + dispatchGroup.enter() + + // Not all characters are allowed in group names, so we replace periods with spaces (like Apple does). + let name = "AltStore " + groupIdentifier.replacingOccurrences(of: ".", with: " ") + + ALTAppleAPI.shared.addAppGroup(withName: name, groupIdentifier: adjustedGroupIdentifier, team: team, session: session) { (group, error) in + switch Result(group, error) + { + case .success(let group): groups.append(group) + case .failure(let error): errors.append(error) + } + + dispatchGroup.leave() + } + } + } + + dispatchGroup.notify(queue: .global()) { + if let error = errors.first + { + finish(.failure(error)) + } + else + { + ALTAppleAPI.shared.assign(appID, to: Array(groups), team: team, session: session) { (success, error) in + let result = Result(success, error) + finish(result.map { _ in appID }) + } + } + } + } + } } } @@ -508,35 +682,75 @@ To prevent this from happening, feel free to try again with another Apple ID to } } - func install(_ application: ALTApplication, to device: ALTDevice, team: ALTTeam, appID: ALTAppID, certificate: ALTCertificate, profile: ALTProvisioningProfile, completionHandler: @escaping (Result) -> Void) + func install(_ application: ALTApplication, to device: ALTDevice, team: ALTTeam, certificate: ALTCertificate, profiles: [String: ALTProvisioningProfile], completionHandler: @escaping (Result) -> Void) { + func prepare(_ bundle: Bundle, additionalInfoDictionaryValues: [String: Any] = [:]) throws + { + guard let identifier = bundle.bundleIdentifier else { throw ALTError(.missingAppBundle) } + guard let profile = profiles[identifier] else { throw ALTError(.missingProvisioningProfile) } + guard var infoDictionary = bundle.completeInfoDictionary else { throw ALTError(.missingInfoPlist) } + + infoDictionary[kCFBundleIdentifierKey as String] = profile.bundleIdentifier + infoDictionary[Bundle.Info.altBundleID] = identifier + + for (key, value) in additionalInfoDictionaryValues + { + infoDictionary[key] = value + } + + if let appGroups = profile.entitlements[.appGroups] as? [String] + { + infoDictionary[Bundle.Info.appGroups] = appGroups + } + + try (infoDictionary as NSDictionary).write(to: bundle.infoPlistURL) + } + DispatchQueue.global().async { do { - let infoPlistURL = application.fileURL.appendingPathComponent("Info.plist") + guard let appBundle = Bundle(url: application.fileURL) else { throw ALTError(.missingAppBundle) } + guard let infoDictionary = appBundle.completeInfoDictionary else { throw ALTError(.missingInfoPlist) } + + let openAppURL = URL(string: "altstore-" + application.bundleIdentifier + "://")! + + var allURLSchemes = infoDictionary[Bundle.Info.urlTypes] as? [[String: Any]] ?? [] + + // Embed open URL so AltBackup can return to AltStore. + let altstoreURLScheme = ["CFBundleTypeRole": "Editor", + "CFBundleURLName": application.bundleIdentifier, + "CFBundleURLSchemes": [openAppURL.scheme!]] as [String : Any] + allURLSchemes.append(altstoreURLScheme) + + var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes] + additionalValues[Bundle.Info.deviceID] = device.identifier + additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID - guard var infoDictionary = NSDictionary(contentsOf: infoPlistURL) as? [String: Any] else { throw ALTError(.missingInfoPlist) } - infoDictionary[kCFBundleIdentifierKey as String] = profile.bundleIdentifier - infoDictionary[Bundle.Info.deviceID] = device.identifier - infoDictionary[Bundle.Info.serverID] = UserDefaults.standard.serverID - infoDictionary[Bundle.Info.certificateID] = certificate.serialNumber - try (infoDictionary as NSDictionary).write(to: infoPlistURL) - if let machineIdentifier = certificate.machineIdentifier, let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier) { + additionalValues[Bundle.Info.certificateID] = certificate.serialNumber + let certificateURL = application.fileURL.appendingPathComponent("ALTCertificate.p12") try encryptedData.write(to: certificateURL, options: .atomic) } + try prepare(appBundle, additionalInfoDictionaryValues: additionalValues) + + for appExtension in application.appExtensions + { + guard let bundle = Bundle(url: appExtension.fileURL) else { throw ALTError(.missingAppBundle) } + try prepare(bundle) + } + let resigner = ALTSigner(team: team, certificate: certificate) - resigner.signApp(at: application.fileURL, provisioningProfiles: [profile]) { (success, error) in + resigner.signApp(at: application.fileURL, provisioningProfiles: Array(profiles.values)) { (success, error) in do { try Result(success, error).get() - let activeProfiles: Set? = (team.type == .free) ? [profile.bundleIdentifier] : nil + let activeProfiles: Set? = (team.type == .free) ? Set(profiles.values.map(\.bundleIdentifier)) : nil ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier, activeProvisioningProfiles: activeProfiles) { (success, error) in completionHandler(Result(success, error)) } diff --git a/AltServer/Devices/ALTDeviceManager.h b/AltServer/Devices/ALTDeviceManager.h index 8d27d9f3..03f78c97 100644 --- a/AltServer/Devices/ALTDeviceManager.h +++ b/AltServer/Devices/ALTDeviceManager.h @@ -7,7 +7,7 @@ // #import -#import +#import "AltSign.h" @class ALTWiredConnection; @class ALTNotificationConnection; @@ -28,6 +28,7 @@ extern NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification /* App Installation */ - (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; +- (void)removeAppForBundleIdentifier:(NSString *)bundleIdentifier fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; - (void)installProvisioningProfiles:(NSSet *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; - (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; diff --git a/AltServer/Devices/ALTDeviceManager.mm b/AltServer/Devices/ALTDeviceManager.mm index 3711fb11..219cbc46 100644 --- a/AltServer/Devices/ALTDeviceManager.mm +++ b/AltServer/Devices/ALTDeviceManager.mm @@ -8,10 +8,12 @@ #import "ALTDeviceManager.h" -#import "AltKit.h" #import "ALTWiredConnection+Private.h" #import "ALTNotificationConnection+Private.h" +#import "ALTConstants.h" +#import "NSError+ALTServerError.h" + #include #include #include @@ -20,6 +22,7 @@ #include void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *udid); +void ALTDeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void *uuid); void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data); NSNotificationName const ALTDeviceManagerDeviceDidConnectNotification = @"ALTDeviceManagerDeviceDidConnectNotification"; @@ -28,6 +31,8 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT @interface ALTDeviceManager () @property (nonatomic, readonly) NSMutableDictionary *installationCompletionHandlers; +@property (nonatomic, readonly) NSMutableDictionary *deletionCompletionHandlers; + @property (nonatomic, readonly) NSMutableDictionary *installationProgress; @property (nonatomic, readonly) dispatch_queue_t installationQueue; @@ -54,8 +59,9 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT if (self) { _installationCompletionHandlers = [NSMutableDictionary dictionary]; - _installationProgress = [NSMutableDictionary dictionary]; + _deletionCompletionHandlers = [NSMutableDictionary dictionary]; + _installationProgress = [NSMutableDictionary dictionary]; _installationQueue = dispatch_queue_create("com.rileytestut.AltServer.InstallationQueue", DISPATCH_QUEUE_SERIAL); _cachedDevices = [NSMutableSet set]; @@ -498,6 +504,87 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT return success; } +- (void)removeAppForBundleIdentifier:(NSString *)bundleIdentifier fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler +{ + __block idevice_t device = NULL; + __block lockdownd_client_t client = NULL; + __block instproxy_client_t ipc = NULL; + __block lockdownd_service_descriptor_t service = NULL; + + void (^finish)(NSError *error) = ^(NSError *e) { + __block NSError *error = e; + + lockdownd_service_descriptor_free(service); + instproxy_client_free(ipc); + lockdownd_client_free(client); + idevice_free(device); + + if (error != nil) + { + completionHandler(NO, error); + } + else + { + completionHandler(YES, nil); + } + }; + + /* Find Device */ + if (idevice_new(&device, udid.UTF8String) != IDEVICE_E_SUCCESS) + { + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]); + } + + /* Connect to Device */ + if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS) + { + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + } + + /* Connect to Installation Proxy */ + if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy", &service) != LOCKDOWN_E_SUCCESS) || service == NULL) + { + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + } + + if (instproxy_client_new(device, service, &ipc) != INSTPROXY_E_SUCCESS) + { + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + } + + if (service) + { + lockdownd_service_descriptor_free(service); + service = NULL; + } + + NSUUID *UUID = [NSUUID UUID]; + __block char *uuidString = (char *)malloc(UUID.UUIDString.length + 1); + strncpy(uuidString, (const char *)UUID.UUIDString.UTF8String, UUID.UUIDString.length); + uuidString[UUID.UUIDString.length] = '\0'; + + self.deletionCompletionHandlers[UUID] = ^(NSError *error) { + if (error != nil) + { + NSString *localizedFailure = [NSString stringWithFormat:NSLocalizedString(@"Could not remove “%@”.", @""), bundleIdentifier]; + + NSMutableDictionary *userInfo = [error.userInfo mutableCopy]; + userInfo[NSLocalizedFailureErrorKey] = localizedFailure; + + NSError *localizedError = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; + finish(localizedError); + } + else + { + finish(nil); + } + + free(uuidString); + }; + + instproxy_uninstall(ipc, bundleIdentifier.UTF8String, NULL, ALTDeviceManagerUpdateAppDeletionStatus, uuidString); +} + #pragma mark - Provisioning Profiles - - (void)installProvisioningProfiles:(NSSet *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *error))completionHandler @@ -1032,7 +1119,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT NSString *name = [NSString stringWithCString:device_name encoding:NSUTF8StringEncoding]; NSString *identifier = [NSString stringWithCString:udid encoding:NSUTF8StringEncoding]; - ALTDevice *altDevice = [[ALTDevice alloc] initWithName:name identifier:identifier]; + ALTDevice *altDevice = [[ALTDevice alloc] initWithName:name identifier:identifier type:ALTDeviceTypeiPhone]; [connectedDevices addObject:altDevice]; if (device_name != NULL) @@ -1075,7 +1162,7 @@ void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *uuid) { if (code != 0 || name != NULL) { - NSLog(@"Error installing app. %@ (%@). %@", @(code), @(name), @(description)); + NSLog(@"Error installing app. %@ (%@). %@", @(code), @(name ?: ""), @(description ?: "")); NSError *error = nil; @@ -1085,14 +1172,14 @@ void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *uuid) } else { - NSString *errorName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; + NSString *errorName = [NSString stringWithCString:name ?: "" encoding:NSUTF8StringEncoding]; if ([errorName isEqualToString:@"DeviceOSVersionTooLow"]) { error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorUnsupportediOSVersion userInfo:nil]; } else { - NSError *underlyingError = [NSError errorWithDomain:AltServerInstallationErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: @(description)}]; + NSError *underlyingError = [NSError errorWithDomain:AltServerInstallationErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: @(description ?: "")}]; error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorInstallationFailed userInfo:@{NSUnderlyingErrorKey: underlyingError}]; } } @@ -1117,6 +1204,43 @@ void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *uuid) } } +void ALTDeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void *uuid) +{ + NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:[NSString stringWithUTF8String:(const char *)uuid]]; + + char *statusName = NULL; + instproxy_status_get_name(status, &statusName); + + char *errorName = NULL; + char *errorDescription = NULL; + uint64_t code = 0; + instproxy_status_get_error(status, &errorName, &errorDescription, &code); + + if ([@(statusName) isEqualToString:@"Complete"] || code != 0 || errorName != NULL) + { + void (^completionHandler)(NSError *) = ALTDeviceManager.sharedManager.deletionCompletionHandlers[UUID]; + if (completionHandler != nil) + { + if (code != 0 || errorName != NULL) + { + NSLog(@"Error removing app. %@ (%@). %@", @(code), @(errorName ?: ""), @(errorDescription ?: "")); + + NSError *underlyingError = [NSError errorWithDomain:AltServerInstallationErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: @(errorDescription ?: "")}]; + NSError *error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorAppDeletionFailed userInfo:@{NSUnderlyingErrorKey: underlyingError}]; + + completionHandler(error); + } + else + { + NSLog(@"Finished removing app!"); + completionHandler(nil); + } + + ALTDeviceManager.sharedManager.deletionCompletionHandlers[UUID] = nil; + } + } +} + void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data) { ALTDevice * (^deviceForUDID)(NSString *, NSArray *) = ^ALTDevice *(NSString *udid, NSArray *devices) { diff --git a/AltServer/PluginManager.swift b/AltServer/PluginManager.swift new file mode 100644 index 00000000..d5b2e3d2 --- /dev/null +++ b/AltServer/PluginManager.swift @@ -0,0 +1,310 @@ +// +// PluginManager.swift +// AltServer +// +// Created by Riley Testut on 9/16/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import Foundation +import AppKit +import CryptoKit + +import STPrivilegedTask + +private let pluginDirectoryURL = URL(fileURLWithPath: "/Library/Mail/Bundles", isDirectory: true) +private let pluginURL = pluginDirectoryURL.appendingPathComponent("AltPlugin.mailbundle") + +enum PluginError: LocalizedError +{ + case cancelled + case unknown + case notFound + case mismatchedHash(hash: String, expectedHash: String) + case taskError(String) + case taskErrorCode(Int) + + var errorDescription: String? { + switch self + { + case .cancelled: return NSLocalizedString("Mail plug-in installation was cancelled.", comment: "") + case .unknown: return NSLocalizedString("Failed to install Mail plug-in.", comment: "") + case .notFound: return NSLocalizedString("The Mail plug-in does not exist at the requested URL.", comment: "") + case .mismatchedHash(let hash, let expectedHash): return String(format: NSLocalizedString("The hash of the downloaded Mail plug-in does not match the expected hash.\n\nHash:\n%@\n\nExpected Hash:\n%@", comment: ""), hash, expectedHash) + case .taskError(let output): return output + case .taskErrorCode(let errorCode): return String(format: NSLocalizedString("There was an error installing the Mail plug-in. (Error Code: %@)", comment: ""), NSNumber(value: errorCode)) + } + } +} + +struct PluginVersion +{ + var url: URL + var sha256Hash: String + var version: String + + static let v1_0 = PluginVersion(url: URL(string: "https://f000.backblazeb2.com/file/altstore/altserver/altplugin/1_0.zip")!, + sha256Hash: "070e9b7e1f74e7a6474d36253ab5a3623ff93892acc9e1043c3581f2ded12200", + version: "1.0") + + static let v1_1 = PluginVersion(url: Bundle.main.url(forResource: "AltPlugin", withExtension: "zip")!, + sha256Hash: "cd1e8c85cbb1935d2874376566671f3c5823101d4933fc6ee63bab8b2a37f800", + version: "1.1") +} + +class PluginManager +{ + var isMailPluginInstalled: Bool { + let isMailPluginInstalled = FileManager.default.fileExists(atPath: pluginURL.path) + return isMailPluginInstalled + } + + var isUpdateAvailable: Bool { + guard let bundle = Bundle(url: pluginURL) else { return false } + + // Load Info.plist from disk because Bundle.infoDictionary is cached by system. + let infoDictionaryURL = bundle.bundleURL.appendingPathComponent("Contents/Info.plist") + guard let infoDictionary = NSDictionary(contentsOf: infoDictionaryURL) as? [String: Any], + let version = infoDictionary["CFBundleShortVersionString"] as? String + else { return false } + + let isUpdateAvailable = (version != self.preferredVersion.version) + return isUpdateAvailable + } + + private var preferredVersion: PluginVersion { + if #available(macOS 11, *) + { + return .v1_1 + } + else + { + return .v1_0 + } + } +} + +extension PluginManager +{ + func installMailPlugin(completionHandler: @escaping (Result) -> Void) + { + do + { + let alert = NSAlert() + + if self.isUpdateAvailable + { + alert.messageText = NSLocalizedString("Update Mail Plug-in", comment: "") + alert.informativeText = NSLocalizedString("An update is available for AltServer's Mail plug-in. Please update the plug-in now in order to keep using AltStore.", comment: "") + + alert.addButton(withTitle: NSLocalizedString("Update Plug-in", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + } + else + { + alert.messageText = NSLocalizedString("Install Mail Plug-in", comment: "") + alert.informativeText = NSLocalizedString("AltServer requires a Mail plug-in in order to retrieve necessary information about your Apple ID. Would you like to install it now?", comment: "") + + alert.addButton(withTitle: NSLocalizedString("Install Plug-in", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + } + + NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) + + let response = alert.runModal() + guard response == .alertFirstButtonReturn else { throw PluginError.cancelled } + + self.downloadPlugin { (result) in + do + { + let fileURL = try result.get() + + // Ensure plug-in directory exists. + let authorization = try self.runAndKeepAuthorization("mkdir", arguments: ["-p", pluginDirectoryURL.path]) + + // Create temporary directory. + let temporaryDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) + try FileManager.default.createDirectory(at: temporaryDirectoryURL, withIntermediateDirectories: true, attributes: nil) + defer { try? FileManager.default.removeItem(at: temporaryDirectoryURL) } + + // Unzip AltPlugin to temporary directory. + try self.runAndKeepAuthorization("unzip", arguments: ["-o", fileURL.path, "-d", temporaryDirectoryURL.path], authorization: authorization) + + if FileManager.default.fileExists(atPath: pluginURL.path) + { + // Delete existing Mail plug-in. + try self.runAndKeepAuthorization("rm", arguments: ["-rf", pluginURL.path], authorization: authorization) + } + + // Copy AltPlugin to Mail plug-ins directory. + // Must be separate step than unzip to prevent macOS from considering plug-in corrupted. + let unzippedPluginURL = temporaryDirectoryURL.appendingPathComponent(pluginURL.lastPathComponent) + try self.runAndKeepAuthorization("cp", arguments: ["-R", unzippedPluginURL.path, pluginDirectoryURL.path], authorization: authorization) + + guard self.isMailPluginInstalled else { throw PluginError.unknown } + + // Enable Mail plug-in preferences. + try self.run("defaults", arguments: ["write", "/Library/Preferences/com.apple.mail", "EnableBundles", "-bool", "YES"], authorization: authorization) + + print("Finished installing Mail plug-in!") + + completionHandler(.success(())) + } + catch + { + completionHandler(.failure(error)) + } + } + } + catch + { + completionHandler(.failure(PluginError.cancelled)) + } + } + + func uninstallMailPlugin(completionHandler: @escaping (Result) -> Void) + { + let alert = NSAlert() + alert.messageText = NSLocalizedString("Uninstall Mail Plug-in", comment: "") + alert.informativeText = NSLocalizedString("Are you sure you want to uninstall the AltServer Mail plug-in? You will no longer be able to install or refresh apps with AltStore.", comment: "") + + alert.addButton(withTitle: NSLocalizedString("Uninstall Plug-in", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + + NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) + + let response = alert.runModal() + guard response == .alertFirstButtonReturn else { return completionHandler(.failure(PluginError.cancelled)) } + + DispatchQueue.global().async { + do + { + if FileManager.default.fileExists(atPath: pluginURL.path) + { + // Delete Mail plug-in from privileged directory. + try self.run("rm", arguments: ["-rf", pluginURL.path]) + } + + completionHandler(.success(())) + } + catch + { + completionHandler(.failure(error)) + } + } + } +} + +private extension PluginManager +{ + func downloadPlugin(completion: @escaping (Result) -> Void) + { + let pluginVersion = self.preferredVersion + + func finish(_ result: Result) + { + do + { + let fileURL = try result.get() + + if #available(OSX 10.15, *) + { + let data = try Data(contentsOf: fileURL) + let sha256Hash = SHA256.hash(data: data) + let hashString = sha256Hash.compactMap { String(format: "%02x", $0) }.joined() + + print("Comparing Mail plug-in hash (\(hashString)) against expected hash (\(pluginVersion.sha256Hash))...") + guard hashString == pluginVersion.sha256Hash else { throw PluginError.mismatchedHash(hash: hashString, expectedHash: pluginVersion.sha256Hash) } + } + + completion(.success(fileURL)) + } + catch + { + completion(.failure(error)) + } + } + + if pluginVersion.url.isFileURL + { + finish(.success(pluginVersion.url)) + } + else + { + let downloadTask = URLSession.shared.downloadTask(with: pluginVersion.url) { (fileURL, response, error) in + if let response = response as? HTTPURLResponse + { + guard response.statusCode != 404 else { return finish(.failure(PluginError.notFound)) } + } + + let result = Result(fileURL, error) + finish(result) + + if let fileURL = fileURL + { + try? FileManager.default.removeItem(at: fileURL) + } + } + + downloadTask.resume() + } + } + + func run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws + { + _ = try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: true) + } + + @discardableResult + func runAndKeepAuthorization(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws -> AuthorizationRef + { + return try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: false) + } + + func _run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil, freeAuthorization: Bool) throws -> AuthorizationRef + { + var launchPath = "/usr/bin/" + program + if !FileManager.default.fileExists(atPath: launchPath) + { + launchPath = "/bin/" + program + } + + print("Running program:", launchPath) + + let task = STPrivilegedTask() + task.launchPath = launchPath + task.arguments = arguments + task.freeAuthorizationWhenDone = freeAuthorization + + let errorCode: OSStatus + + if let authorization = authorization + { + errorCode = task.launch(withAuthorization: authorization) + } + else + { + errorCode = task.launch() + } + + guard errorCode == 0 else { throw PluginError.taskErrorCode(Int(errorCode)) } + + task.waitUntilExit() + + print("Exit code:", task.terminationStatus) + + guard task.terminationStatus == 0 else { + let outputData = task.outputFileHandle.readDataToEndOfFile() + + if let outputString = String(data: outputData, encoding: .utf8), !outputString.isEmpty + { + throw PluginError.taskError(outputString) + } + + throw PluginError.taskErrorCode(Int(task.terminationStatus)) + } + + guard let authorization = task.authorization else { throw PluginError.unknown } + return authorization + } +} diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index ea087e79..4db6af16 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -3,53 +3,54 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ - 01100C7036F0EBAC5B30984B /* libPods-AltStore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DE618FA97EA42C3F468D186 /* libPods-AltStore.a */; }; + 0E33F94B8D78AB969FD309A3 /* Pods_AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A08F67C18350C7990753F03F /* Pods_AltStoreCore.framework */; }; + 2A77E3D272F3D92436FAC272 /* Pods_AltStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9EEAA842DA87A88A870053B /* Pods_AltStore.framework */; }; A8BCEBEAC0620CF80A2FD26D /* Pods_AltServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC3822AB1C4CF1D4CDF7445D /* Pods_AltServer.framework */; }; - BF0201BA22C2EFA3000B93E4 /* AltSign.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; }; - BF0201BB22C2EFA3000B93E4 /* AltSign.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - BF0201BD22C2EFBC000B93E4 /* openssl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF4713A422976CFC00784A2F /* openssl.framework */; }; - BF0201BE22C2EFBC000B93E4 /* openssl.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF4713A422976CFC00784A2F /* openssl.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - BF02419422F2156E00129732 /* RefreshAttempt.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02419322F2156E00129732 /* RefreshAttempt.swift */; }; BF02419622F2199300129732 /* RefreshAttemptsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02419522F2199300129732 /* RefreshAttemptsViewController.swift */; }; BF0241AA22F29CCD00129732 /* UserDefaults+AltServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0241A922F29CCD00129732 /* UserDefaults+AltServer.swift */; }; BF08858322DE795100DE9F1E /* MyAppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF08858222DE795100DE9F1E /* MyAppsViewController.swift */; }; BF08858522DE7EC800DE9F1E /* UpdateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF08858422DE7EC800DE9F1E /* UpdateCollectionViewCell.swift */; }; + BF088D0F25019ABA008082D9 /* AltSign-Static in Frameworks */ = {isa = PBXBuildFile; productRef = BF088D0E25019ABA008082D9 /* AltSign-Static */; }; + BF088D332501A4FF008082D9 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF088D322501A4FF008082D9 /* OpenSSL.xcframework */; }; + BF088D342501A4FF008082D9 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF088D322501A4FF008082D9 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BF088D362501A821008082D9 /* AltSign-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = BF088D352501A821008082D9 /* AltSign-Dynamic */; }; + BF088D372501A821008082D9 /* AltSign-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = BF088D352501A821008082D9 /* AltSign-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + BF088D382501A833008082D9 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF088D322501A4FF008082D9 /* OpenSSL.xcframework */; }; + BF088D392501A833008082D9 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF088D322501A4FF008082D9 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */; }; BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0DCA652433BDF500E3A595 /* AnalyticsManager.swift */; }; - BF0F5FC723F394AD0080DB64 /* AltStore3ToAltStore4.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF0F5FC623F394AD0080DB64 /* AltStore3ToAltStore4.xcmappingmodel */; }; - BF100C50232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF100C4F232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel */; }; - BF100C54232D7DAE006A8926 /* StoreAppPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF100C53232D7DAE006A8926 /* StoreAppPolicy.swift */; }; + BF10EB34248730750055E6DB /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF10EB33248730750055E6DB /* main.swift */; }; + BF1614F1250822F100767AEA /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFD247862284BB3B00981D42 /* Roxas.framework */; }; + BF1614F2250822F100767AEA /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFD247862284BB3B00981D42 /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18B0F022E25DF9005C4CF5 /* ToastView.swift */; }; - BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* ConnectionManager.swift */; }; - BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; }; - BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; }; - BF1E315922A061FB00370A3C /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; }; - BF1E315A22A0620000370A3C /* NSError+ALTServerError.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314922A060F400370A3C /* NSError+ALTServerError.m */; }; - BF1E315F22A0635900370A3C /* libAltKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BF1E315022A0616100370A3C /* libAltKit.a */; }; - BF1E316022A0636400370A3C /* libAltKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BF1E315022A0616100370A3C /* libAltKit.a */; }; - BF258CE322EBAE2800023032 /* AppProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF258CE222EBAE2800023032 /* AppProtocol.swift */; }; - BF26A0E12370C5D400F53F9F /* ALTSourceUserInfoKey.m in Sources */ = {isa = PBXBuildFile; fileRef = BF26A0E02370C5D400F53F9F /* ALTSourceUserInfoKey.m */; }; + BF18BFFD2485A1E400DD5981 /* WiredConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFFC2485A1E400DD5981 /* WiredConnectionHandler.swift */; }; + BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* RequestHandler.swift */; }; + BF1FE358251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */; }; + BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */; }; BF29012F2318F6B100D88A45 /* AppBannerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF29012E2318F6B100D88A45 /* AppBannerView.xib */; }; BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2901302318F7A800D88A45 /* AppBannerView.swift */; }; + BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; }; + BF3432FB246B894F0052F4A1 /* BackupAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */; }; BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3BEFBE2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift */; }; BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3BEFC024086A1E00DE7D55 /* RefreshAppOperation.swift */; }; - BF3D648822E79A3700E9056B /* AppPermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3D648722E79A3700E9056B /* AppPermission.swift */; }; - BF3D648D22E79AC800E9056B /* ALTAppPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = BF3D648C22E79AC800E9056B /* ALTAppPermission.m */; }; BF3D649D22E7AC1B00E9056B /* PermissionPopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3D649C22E7AC1B00E9056B /* PermissionPopoverViewController.swift */; }; BF3D649F22E7B24C00E9056B /* CollapsingTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3D649E22E7B24C00E9056B /* CollapsingTextView.swift */; }; - BF3D64A222E8031100E9056B /* MergePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3D64A122E8031100E9056B /* MergePolicy.swift */; }; BF3D64B022E8D4B800E9056B /* AppContentViewControllerCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3D64AF22E8D4B800E9056B /* AppContentViewControllerCells.swift */; }; BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3F786322CAA41E008FBD20 /* ALTDeviceManager+Installation.swift */; }; BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF41B805233423AE00C593A3 /* TabBarController.swift */; }; BF41B808233433C100C593A3 /* LoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF41B807233433C100C593A3 /* LoadingState.swift */; }; - BF43002E22A714AF0051E2BC /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF43002D22A714AF0051E2BC /* Keychain.swift */; }; - BF43003022A71C960051E2BC /* UserDefaults+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF43002F22A71C960051E2BC /* UserDefaults+AltStore.swift */; }; + BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF42345825101C1D006D1EB2 /* WidgetView.swift */; }; + BF42345C251024B0006D1EB2 /* AltSign-Static in Frameworks */ = {isa = PBXBuildFile; productRef = BF42345B251024B0006D1EB2 /* AltSign-Static */; }; + BF42345D25102688006D1EB2 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF088D322501A4FF008082D9 /* OpenSSL.xcframework */; }; BF44CC6C232AEB90004DA9C3 /* LaunchAtLogin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */; }; BF44CC6D232AEB90004DA9C3 /* LaunchAtLogin.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BF44EEF0246B08BA002A52F2 /* BackupController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF44EEEF246B08BA002A52F2 /* BackupController.swift */; }; + BF44EEF3246B3A17002A52F2 /* AltBackup.ipa in Resources */ = {isa = PBXBuildFile; fileRef = BF44EEF2246B3A17002A52F2 /* AltBackup.ipa */; }; + BF44EEFC246B4550002A52F2 /* RemoveAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF44EEFB246B4550002A52F2 /* RemoveAppOperation.swift */; }; BF458690229872EA00BD7491 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF45868F229872EA00BD7491 /* AppDelegate.swift */; }; BF458694229872EA00BD7491 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF458693229872EA00BD7491 /* Assets.xcassets */; }; BF458697229872EA00BD7491 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF458695229872EA00BD7491 /* Main.storyboard */; }; @@ -117,23 +118,69 @@ BF45884A2298D55000BD7491 /* thread.c in Sources */ = {isa = PBXBuildFile; fileRef = BF4588482298D55000BD7491 /* thread.c */; }; BF45884B2298D55000BD7491 /* thread.h in Headers */ = {isa = PBXBuildFile; fileRef = BF4588492298D55000BD7491 /* thread.h */; }; BF4588882298DD3F00BD7491 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BF4588872298DD3F00BD7491 /* libxml2.tbd */; }; - BF4C7F2523801F0800B2556E /* AltSign.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF9B63C5229DD44D002F0A62 /* AltSign.framework */; }; - BF54E8212315EF0D000AE0D8 /* ALTPatreonBenefitType.m in Sources */ = {isa = PBXBuildFile; fileRef = BF54E8202315EF0D000AE0D8 /* ALTPatreonBenefitType.m */; }; - BF56D2AA23DF88310006506D /* AppID.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56D2A923DF88310006506D /* AppID.swift */; }; + BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4B78FD24B3D1DB008AB4AC /* SceneDelegate.swift */; }; BF56D2AC23DF8E170006506D /* FetchAppIDsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */; }; BF56D2AF23DF9E310006506D /* AppIDsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56D2AE23DF9E310006506D /* AppIDsViewController.swift */; }; + BF58047E246A28F7008AE704 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF58047D246A28F7008AE704 /* AppDelegate.swift */; }; + BF580482246A28F7008AE704 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF580481246A28F7008AE704 /* ViewController.swift */; }; + BF580487246A28F9008AE704 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF580486246A28F9008AE704 /* Assets.xcassets */; }; + BF58048A246A28F9008AE704 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF580488246A28F9008AE704 /* LaunchScreen.storyboard */; }; + BF580496246A3CB5008AE704 /* UIColor+AltBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF580495246A3CB5008AE704 /* UIColor+AltBackup.swift */; }; + BF580498246A3D19008AE704 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF580497246A3D19008AE704 /* UIKit.framework */; }; + BF58049B246A432D008AE704 /* NSError+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+AltStore.swift */; }; BF5C5FCF237DF69100EDD0C6 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; }; BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */; }; - BF6C336224197D700034FD24 /* NSError+LocalizedFailure.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */; }; - BF6C33652419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF6C33642419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel */; }; + BF66EE822501AE50007EE018 /* AltStoreCore.h in Headers */ = {isa = PBXBuildFile; fileRef = BF66EE802501AE50007EE018 /* AltStoreCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; }; + BF66EE862501AE50007EE018 /* AltStoreCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BF66EE8C2501AEB2007EE018 /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EE8B2501AEB1007EE018 /* Keychain.swift */; }; + BF66EE942501AEBC007EE018 /* ALTAppPermission.h in Headers */ = {isa = PBXBuildFile; fileRef = BF66EE8E2501AEBC007EE018 /* ALTAppPermission.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BF66EE952501AEBC007EE018 /* ALTSourceUserInfoKey.h in Headers */ = {isa = PBXBuildFile; fileRef = BF66EE8F2501AEBC007EE018 /* ALTSourceUserInfoKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BF66EE962501AEBC007EE018 /* ALTPatreonBenefitType.m in Sources */ = {isa = PBXBuildFile; fileRef = BF66EE902501AEBC007EE018 /* ALTPatreonBenefitType.m */; }; + BF66EE972501AEBC007EE018 /* ALTAppPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = BF66EE912501AEBC007EE018 /* ALTAppPermission.m */; }; + BF66EE982501AEBC007EE018 /* ALTPatreonBenefitType.h in Headers */ = {isa = PBXBuildFile; fileRef = BF66EE922501AEBC007EE018 /* ALTPatreonBenefitType.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BF66EE992501AEBC007EE018 /* ALTSourceUserInfoKey.m in Sources */ = {isa = PBXBuildFile; fileRef = BF66EE932501AEBC007EE018 /* ALTSourceUserInfoKey.m */; }; + BF66EE9D2501AEC1007EE018 /* AppProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EE9B2501AEC1007EE018 /* AppProtocol.swift */; }; + BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EE9C2501AEC1007EE018 /* Fetchable.swift */; }; + BF66EEA52501AEC5007EE018 /* Benefit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEA02501AEC5007EE018 /* Benefit.swift */; }; + BF66EEA62501AEC5007EE018 /* PatreonAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEA12501AEC5007EE018 /* PatreonAPI.swift */; }; + BF66EEA72501AEC5007EE018 /* Campaign.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEA22501AEC5007EE018 /* Campaign.swift */; }; + BF66EEA82501AEC5007EE018 /* Patron.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEA32501AEC5007EE018 /* Patron.swift */; }; + BF66EEA92501AEC5007EE018 /* Tier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEA42501AEC5007EE018 /* Tier.swift */; }; + BF66EECC2501AECA007EE018 /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEAB2501AECA007EE018 /* Source.swift */; }; + BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEAE2501AECA007EE018 /* StoreAppPolicy.swift */; }; + BF66EECE2501AECA007EE018 /* InstalledAppPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEAF2501AECA007EE018 /* InstalledAppPolicy.swift */; }; + BF66EECF2501AECA007EE018 /* AltStoreToAltStore2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEB12501AECA007EE018 /* AltStoreToAltStore2.xcmappingmodel */; }; + BF66EED02501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEB22501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel */; }; + BF66EED12501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEB32501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel */; }; + BF66EED22501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEB42501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel */; }; + BF66EED32501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEB52501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel */; }; + BF66EED42501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEB62501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel */; }; + BF66EED52501AECA007EE018 /* AltStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEB72501AECA007EE018 /* AltStore.xcdatamodeld */; }; + BF66EED62501AECA007EE018 /* NewsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEBF2501AECA007EE018 /* NewsItem.swift */; }; + BF66EED72501AECA007EE018 /* InstalledApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC02501AECA007EE018 /* InstalledApp.swift */; }; + BF66EED82501AECA007EE018 /* SecureValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC12501AECA007EE018 /* SecureValueTransformer.swift */; }; + BF66EED92501AECA007EE018 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC22501AECA007EE018 /* Team.swift */; }; + BF66EEDA2501AECA007EE018 /* RefreshAttempt.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC32501AECA007EE018 /* RefreshAttempt.swift */; }; + BF66EEDB2501AECA007EE018 /* StoreApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC42501AECA007EE018 /* StoreApp.swift */; }; + BF66EEDC2501AECA007EE018 /* MergePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC52501AECA007EE018 /* MergePolicy.swift */; }; + BF66EEDD2501AECA007EE018 /* AppPermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC62501AECA007EE018 /* AppPermission.swift */; }; + BF66EEDE2501AECA007EE018 /* AppID.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC72501AECA007EE018 /* AppID.swift */; }; + BF66EEDF2501AECA007EE018 /* PatreonAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC82501AECA007EE018 /* PatreonAccount.swift */; }; + BF66EEE02501AECA007EE018 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEC92501AECA007EE018 /* Account.swift */; }; + BF66EEE12501AECA007EE018 /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EECA2501AECA007EE018 /* DatabaseManager.swift */; }; + BF66EEE22501AECA007EE018 /* InstalledExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EECB2501AECA007EE018 /* InstalledExtension.swift */; }; + BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEE42501AED0007EE018 /* UserDefaults+AltStore.swift */; }; + BF66EEE92501AED0007EE018 /* JSONDecoder+Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEE52501AED0007EE018 /* JSONDecoder+Properties.swift */; }; + BF66EEEA2501AED0007EE018 /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEE62501AED0007EE018 /* UIColor+Hex.swift */; }; + BF66EEEB2501AED0007EE018 /* UIApplication+AppExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF66EEE72501AED0007EE018 /* UIApplication+AppExtension.swift */; }; + BF6C336224197D700034FD24 /* NSError+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+AltStore.swift */; }; BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */ = {isa = PBXBuildFile; fileRef = BF6C8FAA242935ED00125131 /* NSAttributedString+Markdown.m */; }; BF6C8FAE2429597900125131 /* BannerCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C8FAD2429597900125131 /* BannerCollectionViewCell.swift */; }; BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C8FAF2429599900125131 /* TextCollectionReusableView.swift */; }; BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6F439123644C6E00A0B879 /* RefreshAltStoreViewController.swift */; }; - BF718BC923C919E300A89F2D /* CFNotificationName+AltStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */; }; - BF718BD123C91BD300A89F2D /* ALTWiredConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD023C91BD300A89F2D /* ALTWiredConnection.m */; }; - BF718BD523C928A300A89F2D /* ALTNotificationConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD423C928A300A89F2D /* ALTNotificationConnection.m */; }; - BF718BD823C93DB700A89F2D /* AltKit.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD723C93DB700A89F2D /* AltKit.m */; }; + BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD023C91BD300A89F2D /* ALTWiredConnection.mm */; }; + BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD423C928A300A89F2D /* ALTNotificationConnection.mm */; }; BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF74989A23621C0700CED65F /* ForwardingNavigationController.swift */; }; BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */; }; BF770E5422BC044E002A40FE /* OperationContexts.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E5322BC044E002A40FE /* OperationContexts.swift */; }; @@ -141,46 +188,69 @@ BF770E5822BC3D0F002A40FE /* RefreshGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E5722BC3D0F002A40FE /* RefreshGroup.swift */; }; BF770E6722BD57C4002A40FE /* BackgroundTaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E6622BD57C3002A40FE /* BackgroundTaskManager.swift */; }; BF770E6922BD57DD002A40FE /* Silence.m4a in Resources */ = {isa = PBXBuildFile; fileRef = BF770E6822BD57DD002A40FE /* Silence.m4a */; }; - BF7C627223DBB3B400515A2D /* AltStore2ToAltStore3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF7C627123DBB3B400515A2D /* AltStore2ToAltStore3.xcmappingmodel */; }; - BF7C627423DBB78C00515A2D /* InstalledAppPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7C627323DBB78C00515A2D /* InstalledAppPolicy.swift */; }; + BF88F97224F8727D00BB75DF /* AppManagerErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF88F97124F8727D00BB75DF /* AppManagerErrors.swift */; }; + BF8B17EB250AC40000F8157F /* FileManager+SharedDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */; }; + BF8CAE452489E772004D6CCE /* AnisetteDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */; }; + BF8CAE462489E772004D6CCE /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE432489E772004D6CCE /* AppManager.swift */; }; + BF8CAE472489E772004D6CCE /* DaemonRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE442489E772004D6CCE /* DaemonRequestHandler.swift */; }; + BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */; }; BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C122E659F700049BA1 /* AppContentViewController.swift */; }; BF8F69C422E662D300049BA1 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C322E662D300049BA1 /* AppViewController.swift */; }; - BF9A03C623C7DD0D000D08DB /* ClientConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */; }; + BF989171250AABF4002ACF50 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF989170250AABF4002ACF50 /* Assets.xcassets */; }; + BF989177250AABF4002ACF50 /* AltWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF98917C250AAC4F002ACF50 /* Countdown.swift */; }; + BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF98917D250AAC4F002ACF50 /* AltWidget.swift */; }; + BF989184250AACFC002ACF50 /* Date+RelativeDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */; }; + BF989185250AAD1D002ACF50 /* UIColor+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */; }; BF9ABA4522DCFF43008935CF /* BrowseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */; }; BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */; }; BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */; }; BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4A22DD137F008935CF /* NavigationBar.swift */; }; BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4C22DD16DE008935CF /* PillButton.swift */; }; - BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */; }; - BF9F8D1A242AA6BC0024E48B /* AltStore5ToAltStore6.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF9F8D19242AA6BC0024E48B /* AltStore5ToAltStore6.xcmappingmodel */; }; BFA8172923C56042001B5953 /* ServerConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA8172823C56042001B5953 /* ServerConnection.swift */; }; BFA8172B23C5633D001B5953 /* FetchAnisetteDataOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */; }; - BFA8172D23C5823E001B5953 /* InstalledExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA8172C23C5823E001B5953 /* InstalledExtension.swift */; }; - BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB11691229322E400BB457C /* DatabaseManager.swift */; }; - BFB1169B2293274D00BB457C /* JSONDecoder+Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB1169A2293274D00BB457C /* JSONDecoder+Properties.swift */; }; + BFAECC522501B0A400528F27 /* CodableServerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD44605241188C300EAB90A /* CodableServerError.swift */; }; + BFAECC532501B0A400528F27 /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; }; + BFAECC542501B0A400528F27 /* NSError+ALTServerError.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314922A060F400370A3C /* NSError+ALTServerError.m */; }; + BFAECC552501B0A400528F27 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF624858BDE00DD5981 /* Connection.swift */; }; + BFAECC562501B0A400528F27 /* ALTServerError+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */; }; + BFAECC572501B0A400528F27 /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF22485828200DD5981 /* ConnectionManager.swift */; }; + BFAECC582501B0A400528F27 /* ALTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD723C93DB700A89F2D /* ALTConstants.m */; }; + BFAECC592501B0A400528F27 /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; }; + BFAECC5A2501B0A400528F27 /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CD2489ABE90097E58C /* NetworkConnection.swift */; }; + BFAECC5B2501B0A400528F27 /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; }; + BFAECC5C2501B0A400528F27 /* CFNotificationName+AltStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */; }; + BFAECC5D2501B0BF00528F27 /* ALTConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = BF18BFFE2485A42800DD5981 /* ALTConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BFAECC5E2501B0BF00528F27 /* CFNotificationName+AltStore.h in Headers */ = {isa = PBXBuildFile; fileRef = BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BFAECC5F2501B0BF00528F27 /* ALTConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = BFD52BD222A06EFB000B7ED1 /* ALTConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BFAECC602501B0BF00528F27 /* NSError+ALTServerError.h in Headers */ = {isa = PBXBuildFile; fileRef = BF1E314822A060F400370A3C /* NSError+ALTServerError.h */; settings = {ATTRIBUTES = (Public, ); }; }; BFB3645A2325985F00CD0EB1 /* FindServerOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB364592325985F00CD0EB1 /* FindServerOperation.swift */; }; + BFB39B5C252BC10E00D1BE50 /* Managed.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB39B5B252BC10E00D1BE50 /* Managed.swift */; }; BFB4323F22DE852000B7F8BC /* UpdateCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFB4323E22DE852000B7F8BC /* UpdateCollectionViewCell.xib */; }; BFB49AAA23834CF900D542D9 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; }; - BFB6B21B23186D640022A802 /* NewsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21A23186D640022A802 /* NewsItem.swift */; }; BFB6B21E231870160022A802 /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21D231870160022A802 /* NewsViewController.swift */; }; BFB6B220231870B00022A802 /* NewsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21F231870B00022A802 /* NewsCollectionViewCell.swift */; }; BFB6B22423187A3D0022A802 /* NewsCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFB6B22323187A3D0022A802 /* NewsCollectionViewCell.xib */; }; - BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */; }; - BFBBE2DF22931F73002097FA /* StoreApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DE22931F73002097FA /* StoreApp.swift */; }; - BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2E022931F81002097FA /* InstalledApp.swift */; }; + BFBE0004250ACFFB0080826E /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; }; + BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF989190250AAE86002ACF50 /* ViewAppIntentHandler.swift */; }; + BFBF331B2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */; }; BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */; }; BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */; }; BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */; }; BFC57A702416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */; }; + BFC712BB2512B9CF00AB5EBE /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */; }; + BFC712C32512D5F100AB5EBE /* XPCConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C12512D5F100AB5EBE /* XPCConnection.swift */; }; + BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C12512D5F100AB5EBE /* XPCConnection.swift */; }; + BFC712C52512D5F100AB5EBE /* XPCConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */; }; BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC84A4C2421A19100853474 /* SourcesViewController.swift */; }; + BFCB9207250AB2120057B44E /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFCB9206250AB2120057B44E /* Colors.xcassets */; }; + BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCB519245E3401001853EA /* VerifyAppOperation.swift */; }; BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476D2284B9A500981D42 /* AppDelegate.swift */; }; BFD247752284B9A500981D42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247732284B9A500981D42 /* Main.storyboard */; }; BFD247772284B9A700981D42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFD247762284B9A700981D42 /* Assets.xcassets */; }; BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247782284B9A700981D42 /* LaunchScreen.storyboard */; }; BFD2478C2284C4C300981D42 /* AppIconImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478B2284C4C300981D42 /* AppIconImageView.swift */; }; BFD2478F2284C8F900981D42 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478E2284C8F900981D42 /* Button.swift */; }; - BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */; }; - BFD44606241188C400EAB90A /* CodableServerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD44605241188C300EAB90A /* CodableServerError.swift */; }; BFD52BD422A0800A000B7ED1 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD52BD322A0800A000B7ED1 /* ServerManager.swift */; }; BFD52C0122A1A9CB000B7ED1 /* ptrarray.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52BE522A1A9CA000B7ED1 /* ptrarray.c */; }; BFD52C0222A1A9CB000B7ED1 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52BE622A1A9CA000B7ED1 /* base64.c */; }; @@ -213,33 +283,49 @@ BFD52C2022A1A9EC000B7ED1 /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1D22A1A9EC000B7ED1 /* node.c */; }; BFD52C2122A1A9EC000B7ED1 /* node_list.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1E22A1A9EC000B7ED1 /* node_list.c */; }; BFD52C2222A1A9EC000B7ED1 /* cnary.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1F22A1A9EC000B7ED1 /* cnary.c */; }; - BFD5D6E8230CC961007955AB /* PatreonAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD5D6E7230CC961007955AB /* PatreonAPI.swift */; }; - BFD5D6EA230CCAE5007955AB /* PatreonAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD5D6E9230CCAE5007955AB /* PatreonAccount.swift */; }; - BFD5D6EE230D8A86007955AB /* Patron.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD5D6ED230D8A86007955AB /* Patron.swift */; }; - BFD5D6F2230DD974007955AB /* Benefit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD5D6F1230DD974007955AB /* Benefit.swift */; }; - BFD5D6F4230DDB0A007955AB /* Campaign.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD5D6F3230DDB0A007955AB /* Campaign.swift */; }; - BFD5D6F6230DDB12007955AB /* Tier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD5D6F5230DDB12007955AB /* Tier.swift */; }; BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */; }; - BFDB5B1622EE90D300F74113 /* Date+RelativeDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */; }; BFDB5B2622EFBBEA00F74113 /* BrowseCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFDB5B2522EFBBEA00F74113 /* BrowseCollectionViewCell.xib */; }; - BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */; }; BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */; }; BFDB6A0B22AAEDB7007EA6D6 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0A22AAEDB7007EA6D6 /* Operation.swift */; }; BFDB6A0D22AAFC1A007EA6D6 /* OperationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0C22AAFC19007EA6D6 /* OperationError.swift */; }; BFDB6A0F22AB2776007EA6D6 /* SendAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0E22AB2776007EA6D6 /* SendAppOperation.swift */; }; - BFE338DD22F0E7F3002E24B9 /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE338DC22F0E7F3002E24B9 /* Source.swift */; }; + BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDBBD7F246CB84F004ED2F3 /* RemoveAppBackupOperation.swift */; }; + BFE00A202503097F00EB4D0C /* INInteraction+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */; }; BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */; }; BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE338E722F10E56002E24B9 /* LaunchViewController.swift */; }; BFE48975238007CE003239E0 /* AnisetteDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE48974238007CE003239E0 /* AnisetteDataManager.swift */; }; + BFE4FFB6251BF7BA0018CF9B /* AltPlugin.zip in Resources */ = {isa = PBXBuildFile; fileRef = BFE4FFB5251BF7BA0018CF9B /* AltPlugin.zip */; }; BFE60738231ADF49002B0E8E /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFE60737231ADF49002B0E8E /* Settings.storyboard */; }; BFE6073A231ADF82002B0E8E /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE60739231ADF82002B0E8E /* SettingsViewController.swift */; }; BFE6073C231AE1E7002B0E8E /* SettingsHeaderFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFE6073B231AE1E7002B0E8E /* SettingsHeaderFooterView.xib */; }; BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6073F231AFD2A002B0E8E /* InsetGroupTableViewCell.swift */; }; BFE60742231B07E6002B0E8E /* SettingsHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE60741231B07E6002B0E8E /* SettingsHeaderFooterView.swift */; }; BFE6325A22A83BEB00F30809 /* Authentication.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFE6325922A83BEB00F30809 /* Authentication.storyboard */; }; - BFE6326622A857C200F30809 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6326522A857C100F30809 /* Team.swift */; }; - BFE6326822A858F300F30809 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6326722A858F300F30809 /* Account.swift */; }; BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6326B22A86FF300F30809 /* AuthenticationOperation.swift */; }; + BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD44605241188C300EAB90A /* CodableServerError.swift */; }; + BFECAC8024FD950B0077C41F /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF22485828200DD5981 /* ConnectionManager.swift */; }; + BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */; }; + BFECAC8224FD950B0077C41F /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; }; + BFECAC8324FD950B0077C41F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CD2489ABE90097E58C /* NetworkConnection.swift */; }; + BFECAC8424FD950B0077C41F /* ALTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD723C93DB700A89F2D /* ALTConstants.m */; }; + BFECAC8524FD950B0077C41F /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF624858BDE00DD5981 /* Connection.swift */; }; + BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; }; + BFECAC8724FD950B0077C41F /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; }; + BFECAC8824FD950E0077C41F /* CodableServerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD44605241188C300EAB90A /* CodableServerError.swift */; }; + BFECAC8924FD950E0077C41F /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF22485828200DD5981 /* ConnectionManager.swift */; }; + BFECAC8A24FD950E0077C41F /* ALTServerError+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */; }; + BFECAC8B24FD950E0077C41F /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; }; + BFECAC8D24FD950E0077C41F /* ALTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD723C93DB700A89F2D /* ALTConstants.m */; }; + BFECAC8E24FD950E0077C41F /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF624858BDE00DD5981 /* Connection.swift */; }; + BFECAC8F24FD950E0077C41F /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; }; + BFECAC9024FD950E0077C41F /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; }; + BFECAC9324FD98BA0077C41F /* CFNotificationName+AltStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */; }; + BFECAC9424FD98BA0077C41F /* NSError+ALTServerError.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314922A060F400370A3C /* NSError+ALTServerError.m */; }; + BFECAC9524FD98BB0077C41F /* CFNotificationName+AltStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */; }; + BFECAC9624FD98BB0077C41F /* NSError+ALTServerError.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314922A060F400370A3C /* NSError+ALTServerError.m */; }; + BFF00D302501BD7D00746320 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */; }; + BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */; }; + BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D332501BDCF00746320 /* IntentHandler.swift */; }; BFF0B68E23219520007A79E1 /* PatreonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68D23219520007A79E1 /* PatreonViewController.swift */; }; BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */; }; BFF0B6922321A305007A79E1 /* AboutPatreonHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFF0B6912321A305007A79E1 /* AboutPatreonHeaderView.xib */; }; @@ -247,23 +333,11 @@ BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; }; BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */; }; BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */; }; + BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; }; + BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - BF1E315B22A0621900370A3C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFD247622284B9A500981D42 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BF1E314F22A0616100370A3C; - remoteInfo = AltKit; - }; - BF1E315D22A0621F00370A3C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFD247622284B9A500981D42 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BF1E314F22A0616100370A3C; - remoteInfo = AltKit; - }; BF4588442298D48B00BD7491 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFD247622284B9A500981D42 /* Project object */; @@ -271,6 +345,20 @@ remoteGlobalIDString = BF45872A2298D31600BD7491; remoteInfo = libimobiledevice; }; + BF66EE832501AE50007EE018 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFD247622284B9A500981D42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BF66EE7D2501AE50007EE018; + remoteInfo = AltStoreCore; + }; + BF989175250AABF4002ACF50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFD247622284B9A500981D42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BF989166250AABF3002ACF50; + remoteInfo = AltWidgetExtension; + }; BFBFFB262380C72F00993A4A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFD247622284B9A500981D42 /* Project object */; @@ -278,6 +366,13 @@ remoteGlobalIDString = BF5C5FC4237DF5AE00EDD0C6; remoteInfo = AltPlugin; }; + BFF615AA2510042B00484D3B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFD247622284B9A500981D42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BF66EE7D2501AE50007EE018; + remoteInfo = AltStoreCore; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -287,20 +382,24 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - BF0201BB22C2EFA3000B93E4 /* AltSign.framework in Embed Frameworks */, - BF0201BE22C2EFBC000B93E4 /* openssl.framework in Embed Frameworks */, + BF088D392501A833008082D9 /* OpenSSL.xcframework in Embed Frameworks */, BF44CC6D232AEB90004DA9C3 /* LaunchAtLogin.framework in Embed Frameworks */, + BF088D372501A821008082D9 /* AltSign-Dynamic in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - BF1E314E22A0616100370A3C /* CopyFiles */ = { + BF088D2B2501A087008082D9 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = "include/$(PRODUCT_NAME)"; - dstSubfolderSpec = 16; + dstPath = ""; + dstSubfolderSpec = 10; files = ( + BF1614F2250822F100767AEA /* Roxas.framework in Embed Frameworks */, + BF088D342501A4FF008082D9 /* OpenSSL.xcframework in Embed Frameworks */, + BF66EE862501AE50007EE018 /* AltStoreCore.framework in Embed Frameworks */, ); + name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; BF5C5FE9237E438C00EDD0C6 /* CopyFiles */ = { @@ -312,53 +411,65 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF98917B250AABF4002ACF50 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + BF989177250AABF4002ACF50 /* AltWidgetExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0DE618FA97EA42C3F468D186 /* libPods-AltStore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AltStore.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 11611D46F8A7C8B928E8156B /* Pods-AltServer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltServer.debug.xcconfig"; path = "Target Support Files/Pods-AltServer/Pods-AltServer.debug.xcconfig"; sourceTree = ""; }; 589BA531D903B28F292063E5 /* Pods-AltServer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltServer.release.xcconfig"; path = "Target Support Files/Pods-AltServer/Pods-AltServer.release.xcconfig"; sourceTree = ""; }; + A08F67C18350C7990753F03F /* Pods_AltStoreCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltStoreCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A136EE677716B80768E9F0A2 /* Pods-AltStore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStore.release.xcconfig"; path = "Target Support Files/Pods-AltStore/Pods-AltStore.release.xcconfig"; sourceTree = ""; }; - BF02419322F2156E00129732 /* RefreshAttempt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAttempt.swift; sourceTree = ""; }; + B39BC452F0753C2C33A2D43B /* Pods-AltStoreCore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStoreCore.debug.xcconfig"; path = "Target Support Files/Pods-AltStoreCore/Pods-AltStoreCore.debug.xcconfig"; sourceTree = ""; }; + BC373DB2C2B6CB739CCBFB5F /* Pods-AltStoreCore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStoreCore.release.xcconfig"; path = "Target Support Files/Pods-AltStoreCore/Pods-AltStoreCore.release.xcconfig"; sourceTree = ""; }; BF02419522F2199300129732 /* RefreshAttemptsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAttemptsViewController.swift; sourceTree = ""; }; BF0241A922F29CCD00129732 /* UserDefaults+AltServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+AltServer.swift"; sourceTree = ""; }; BF08858222DE795100DE9F1E /* MyAppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewController.swift; sourceTree = ""; }; BF08858422DE7EC800DE9F1E /* UpdateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCollectionViewCell.swift; sourceTree = ""; }; + BF088D322501A4FF008082D9 /* OpenSSL.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/OpenSSL.xcframework; sourceTree = ""; }; BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = ""; }; BF0DCA652433BDF500E3A595 /* AnalyticsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsManager.swift; sourceTree = ""; }; - BF0F5FC623F394AD0080DB64 /* AltStore3ToAltStore4.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore3ToAltStore4.xcmappingmodel; sourceTree = ""; }; - BF100C46232D7828006A8926 /* AltStore 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 2.xcdatamodel"; sourceTree = ""; }; - BF100C4F232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStoreToAltStore2.xcmappingmodel; sourceTree = ""; }; - BF100C53232D7DAE006A8926 /* StoreAppPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreAppPolicy.swift; sourceTree = ""; }; + BF10EB33248730750055E6DB /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; BF18B0F022E25DF9005C4CF5 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; + BF18BFE724857D7900DD5981 /* AltDaemon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = AltDaemon; sourceTree = BUILT_PRODUCTS_DIR; }; + BF18BFF22485828200DD5981 /* ConnectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = ""; }; + BF18BFF624858BDE00DD5981 /* Connection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; + BF18BFFC2485A1E400DD5981 /* WiredConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiredConnectionHandler.swift; sourceTree = ""; }; + BF18BFFE2485A42800DD5981 /* ALTConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTConnection.h; sourceTree = ""; }; + BF18C0032485B4DE00DD5981 /* AltDaemon-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AltDaemon-Bridging-Header.h"; sourceTree = ""; }; BF1E3128229F474900370A3C /* ServerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerProtocol.swift; sourceTree = ""; }; - BF1E3129229F474900370A3C /* ConnectionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = ""; }; + BF1E3129229F474900370A3C /* RequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestHandler.swift; sourceTree = ""; }; BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+AltStore.swift"; sourceTree = ""; }; BF1E314722A060F300370A3C /* AltStore-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AltStore-Bridging-Header.h"; sourceTree = ""; }; BF1E314822A060F400370A3C /* NSError+ALTServerError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSError+ALTServerError.h"; sourceTree = ""; }; BF1E314922A060F400370A3C /* NSError+ALTServerError.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSError+ALTServerError.m"; sourceTree = ""; }; - BF1E315022A0616100370A3C /* libAltKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAltKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; + BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSXPCConnection+MachServices.swift"; sourceTree = ""; }; BF219A7E22CAC431007676A6 /* AltStore.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltStore.entitlements; sourceTree = ""; }; - BF258CE222EBAE2800023032 /* AppProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppProtocol.swift; sourceTree = ""; }; - BF26A0DF2370C5D400F53F9F /* ALTSourceUserInfoKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTSourceUserInfoKey.h; sourceTree = ""; }; - BF26A0E02370C5D400F53F9F /* ALTSourceUserInfoKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTSourceUserInfoKey.m; sourceTree = ""; }; BF29012E2318F6B100D88A45 /* AppBannerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AppBannerView.xib; sourceTree = ""; }; BF2901302318F7A800D88A45 /* AppBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppBannerView.swift; sourceTree = ""; }; + BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupAppOperation.swift; sourceTree = ""; }; BF3BEFBE2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchProvisioningProfilesOperation.swift; sourceTree = ""; }; BF3BEFC024086A1E00DE7D55 /* RefreshAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAppOperation.swift; sourceTree = ""; }; - BF3D648722E79A3700E9056B /* AppPermission.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermission.swift; sourceTree = ""; }; - BF3D648B22E79AC800E9056B /* ALTAppPermission.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTAppPermission.h; sourceTree = ""; }; - BF3D648C22E79AC800E9056B /* ALTAppPermission.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTAppPermission.m; sourceTree = ""; }; BF3D649C22E7AC1B00E9056B /* PermissionPopoverViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionPopoverViewController.swift; sourceTree = ""; }; BF3D649E22E7B24C00E9056B /* CollapsingTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsingTextView.swift; sourceTree = ""; }; - BF3D64A122E8031100E9056B /* MergePolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MergePolicy.swift; sourceTree = ""; }; BF3D64AF22E8D4B800E9056B /* AppContentViewControllerCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentViewControllerCells.swift; sourceTree = ""; }; BF3F786322CAA41E008FBD20 /* ALTDeviceManager+Installation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTDeviceManager+Installation.swift"; sourceTree = ""; }; BF41B805233423AE00C593A3 /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; BF41B807233433C100C593A3 /* LoadingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingState.swift; sourceTree = ""; }; - BF43002D22A714AF0051E2BC /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; }; - BF43002F22A71C960051E2BC /* UserDefaults+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+AltStore.swift"; sourceTree = ""; }; + BF42345825101C1D006D1EB2 /* WidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetView.swift; sourceTree = ""; }; BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LaunchAtLogin.framework; path = Carthage/Build/Mac/LaunchAtLogin.framework; sourceTree = ""; }; + BF44EEEF246B08BA002A52F2 /* BackupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupController.swift; sourceTree = ""; }; + BF44EEF2246B3A17002A52F2 /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; path = AltBackup.ipa; sourceTree = ""; }; + BF44EEFB246B4550002A52F2 /* RemoveAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAppOperation.swift; sourceTree = ""; }; BF45868D229872EA00BD7491 /* AltServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AltServer.app; sourceTree = BUILT_PRODUCTS_DIR; }; BF45868F229872EA00BD7491 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; BF458693229872EA00BD7491 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -430,25 +541,76 @@ BF4588422298D40000BD7491 /* libusbmuxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = libusbmuxd.c; path = Dependencies/libusbmuxd/src/libusbmuxd.c; sourceTree = SOURCE_ROOT; }; BF4588482298D55000BD7491 /* thread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = thread.c; path = Dependencies/libusbmuxd/common/thread.c; sourceTree = SOURCE_ROOT; }; BF4588492298D55000BD7491 /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = thread.h; path = Dependencies/libusbmuxd/common/thread.h; sourceTree = SOURCE_ROOT; }; - BF4588542298DC5400BD7491 /* openssl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = openssl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BF4588872298DD3F00BD7491 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; }; - BF4588962298DE6E00BD7491 /* libzip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = libzip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BF4713A422976CFC00784A2F /* openssl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = openssl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BF54E81F2315EF0D000AE0D8 /* ALTPatreonBenefitType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTPatreonBenefitType.h; sourceTree = ""; }; - BF54E8202315EF0D000AE0D8 /* ALTPatreonBenefitType.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTPatreonBenefitType.m; sourceTree = ""; }; - BF56D2A823DF87570006506D /* AltStore 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 4.xcdatamodel"; sourceTree = ""; }; - BF56D2A923DF88310006506D /* AppID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppID.swift; sourceTree = ""; }; + BF4B78FD24B3D1DB008AB4AC /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAppIDsOperation.swift; sourceTree = ""; }; BF56D2AE23DF9E310006506D /* AppIDsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIDsViewController.swift; sourceTree = ""; }; - BF5AB3A72285FE6C00DC914B /* AltSign.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AltSign.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BF58047B246A28F7008AE704 /* AltBackup.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AltBackup.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BF58047D246A28F7008AE704 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + BF580481246A28F7008AE704 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + BF580486246A28F9008AE704 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BF580489246A28F9008AE704 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + BF58048B246A28F9008AE704 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BF580495246A3CB5008AE704 /* UIColor+AltBackup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+AltBackup.swift"; sourceTree = ""; }; + BF580497246A3D19008AE704 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + BF580499246A4153008AE704 /* AltBackup.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltBackup.entitlements; sourceTree = ""; }; BF5C5FC5237DF5AE00EDD0C6 /* AltPlugin.mailbundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AltPlugin.mailbundle; sourceTree = BUILT_PRODUCTS_DIR; }; BF5C5FC7237DF5AE00EDD0C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BF5C5FCD237DF69100EDD0C6 /* ALTPluginService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTPluginService.h; sourceTree = ""; }; BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTPluginService.m; sourceTree = ""; }; BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+DirectorySize.swift"; sourceTree = ""; }; - BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+LocalizedFailure.swift"; sourceTree = ""; }; - BF6C33632419ADEB0034FD24 /* AltStore 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 5.xcdatamodel"; sourceTree = ""; }; - BF6C33642419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore4ToAltStore5.xcmappingmodel; sourceTree = ""; }; + BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AltStoreCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BF66EE802501AE50007EE018 /* AltStoreCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AltStoreCore.h; sourceTree = ""; }; + BF66EE812501AE50007EE018 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BF66EE8B2501AEB1007EE018 /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; }; + BF66EE8E2501AEBC007EE018 /* ALTAppPermission.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALTAppPermission.h; sourceTree = ""; }; + BF66EE8F2501AEBC007EE018 /* ALTSourceUserInfoKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALTSourceUserInfoKey.h; sourceTree = ""; }; + BF66EE902501AEBC007EE018 /* ALTPatreonBenefitType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALTPatreonBenefitType.m; sourceTree = ""; }; + BF66EE912501AEBC007EE018 /* ALTAppPermission.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALTAppPermission.m; sourceTree = ""; }; + BF66EE922501AEBC007EE018 /* ALTPatreonBenefitType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALTPatreonBenefitType.h; sourceTree = ""; }; + BF66EE932501AEBC007EE018 /* ALTSourceUserInfoKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALTSourceUserInfoKey.m; sourceTree = ""; }; + BF66EE9B2501AEC1007EE018 /* AppProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppProtocol.swift; sourceTree = ""; }; + BF66EE9C2501AEC1007EE018 /* Fetchable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = ""; }; + BF66EEA02501AEC5007EE018 /* Benefit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Benefit.swift; sourceTree = ""; }; + BF66EEA12501AEC5007EE018 /* PatreonAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PatreonAPI.swift; sourceTree = ""; }; + BF66EEA22501AEC5007EE018 /* Campaign.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Campaign.swift; sourceTree = ""; }; + BF66EEA32501AEC5007EE018 /* Patron.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Patron.swift; sourceTree = ""; }; + BF66EEA42501AEC5007EE018 /* Tier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tier.swift; sourceTree = ""; }; + BF66EEAB2501AECA007EE018 /* Source.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Source.swift; sourceTree = ""; }; + BF66EEAE2501AECA007EE018 /* StoreAppPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreAppPolicy.swift; sourceTree = ""; }; + BF66EEAF2501AECA007EE018 /* InstalledAppPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstalledAppPolicy.swift; sourceTree = ""; }; + BF66EEB12501AECA007EE018 /* AltStoreToAltStore2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStoreToAltStore2.xcmappingmodel; sourceTree = ""; }; + BF66EEB22501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore6ToAltStore7.xcmappingmodel; sourceTree = ""; }; + BF66EEB32501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore3ToAltStore4.xcmappingmodel; sourceTree = ""; }; + BF66EEB42501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore4ToAltStore5.xcmappingmodel; sourceTree = ""; }; + BF66EEB52501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore2ToAltStore3.xcmappingmodel; sourceTree = ""; }; + BF66EEB62501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore5ToAltStore6.xcmappingmodel; sourceTree = ""; }; + BF66EEB82501AECA007EE018 /* AltStore 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 3.xcdatamodel"; sourceTree = ""; }; + BF66EEB92501AECA007EE018 /* AltStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AltStore.xcdatamodel; sourceTree = ""; }; + BF66EEBA2501AECA007EE018 /* AltStore 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 6.xcdatamodel"; sourceTree = ""; }; + BF66EEBB2501AECA007EE018 /* AltStore 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 5.xcdatamodel"; sourceTree = ""; }; + BF66EEBC2501AECA007EE018 /* AltStore 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 7.xcdatamodel"; sourceTree = ""; }; + BF66EEBD2501AECA007EE018 /* AltStore 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 2.xcdatamodel"; sourceTree = ""; }; + BF66EEBE2501AECA007EE018 /* AltStore 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 4.xcdatamodel"; sourceTree = ""; }; + BF66EEBF2501AECA007EE018 /* NewsItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsItem.swift; sourceTree = ""; }; + BF66EEC02501AECA007EE018 /* InstalledApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstalledApp.swift; sourceTree = ""; }; + BF66EEC12501AECA007EE018 /* SecureValueTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureValueTransformer.swift; sourceTree = ""; }; + BF66EEC22501AECA007EE018 /* Team.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Team.swift; sourceTree = ""; }; + BF66EEC32501AECA007EE018 /* RefreshAttempt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshAttempt.swift; sourceTree = ""; }; + BF66EEC42501AECA007EE018 /* StoreApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreApp.swift; sourceTree = ""; }; + BF66EEC52501AECA007EE018 /* MergePolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergePolicy.swift; sourceTree = ""; }; + BF66EEC62501AECA007EE018 /* AppPermission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppPermission.swift; sourceTree = ""; }; + BF66EEC72501AECA007EE018 /* AppID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppID.swift; sourceTree = ""; }; + BF66EEC82501AECA007EE018 /* PatreonAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PatreonAccount.swift; sourceTree = ""; }; + BF66EEC92501AECA007EE018 /* Account.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; + BF66EECA2501AECA007EE018 /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = ""; }; + BF66EECB2501AECA007EE018 /* InstalledExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstalledExtension.swift; sourceTree = ""; }; + BF66EEE42501AED0007EE018 /* UserDefaults+AltStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+AltStore.swift"; sourceTree = ""; }; + BF66EEE52501AED0007EE018 /* JSONDecoder+Properties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Properties.swift"; sourceTree = ""; }; + BF66EEE62501AED0007EE018 /* UIColor+Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = ""; }; + BF66EEE72501AED0007EE018 /* UIApplication+AppExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+AppExtension.swift"; sourceTree = ""; }; + BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+SharedDirectories.swift"; sourceTree = ""; }; + BF6C336124197D700034FD24 /* NSError+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+AltStore.swift"; sourceTree = ""; }; BF6C8FAA242935ED00125131 /* NSAttributedString+Markdown.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+Markdown.m"; path = "Dependencies/MarkdownAttributedString/NSAttributedString+Markdown.m"; sourceTree = SOURCE_ROOT; }; BF6C8FAB242935ED00125131 /* NSAttributedString+Markdown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+Markdown.h"; path = "Dependencies/MarkdownAttributedString/NSAttributedString+Markdown.h"; sourceTree = SOURCE_ROOT; }; BF6C8FAD2429597900125131 /* BannerCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerCollectionViewCell.swift; sourceTree = ""; }; @@ -457,12 +619,12 @@ BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CFNotificationName+AltStore.h"; sourceTree = ""; }; BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CFNotificationName+AltStore.m"; sourceTree = ""; }; BF718BCF23C91BD300A89F2D /* ALTWiredConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTWiredConnection.h; sourceTree = ""; }; - BF718BD023C91BD300A89F2D /* ALTWiredConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTWiredConnection.m; sourceTree = ""; }; + BF718BD023C91BD300A89F2D /* ALTWiredConnection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ALTWiredConnection.mm; sourceTree = ""; }; BF718BD223C91C7000A89F2D /* ALTWiredConnection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ALTWiredConnection+Private.h"; sourceTree = ""; }; BF718BD323C928A300A89F2D /* ALTNotificationConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTNotificationConnection.h; sourceTree = ""; }; - BF718BD423C928A300A89F2D /* ALTNotificationConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTNotificationConnection.m; sourceTree = ""; }; + BF718BD423C928A300A89F2D /* ALTNotificationConnection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ALTNotificationConnection.mm; sourceTree = ""; }; BF718BD623C92B3700A89F2D /* ALTNotificationConnection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ALTNotificationConnection+Private.h"; sourceTree = ""; }; - BF718BD723C93DB700A89F2D /* AltKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AltKit.m; sourceTree = ""; }; + BF718BD723C93DB700A89F2D /* ALTConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTConstants.m; sourceTree = ""; }; BF74989A23621C0700CED65F /* ForwardingNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardingNavigationController.swift; sourceTree = ""; }; BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallAppOperation.swift; sourceTree = ""; }; BF770E5322BC044E002A40FE /* OperationContexts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationContexts.swift; sourceTree = ""; }; @@ -470,44 +632,50 @@ BF770E5722BC3D0F002A40FE /* RefreshGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshGroup.swift; sourceTree = ""; }; BF770E6622BD57C3002A40FE /* BackgroundTaskManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundTaskManager.swift; sourceTree = ""; }; BF770E6822BD57DD002A40FE /* Silence.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Silence.m4a; sourceTree = ""; }; - BF7C627023DBB33300515A2D /* AltStore 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 3.xcdatamodel"; sourceTree = ""; }; - BF7C627123DBB3B400515A2D /* AltStore2ToAltStore3.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore2ToAltStore3.xcmappingmodel; sourceTree = ""; }; - BF7C627323DBB78C00515A2D /* InstalledAppPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledAppPolicy.swift; sourceTree = ""; }; + BF88F97124F8727D00BB75DF /* AppManagerErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManagerErrors.swift; sourceTree = ""; }; + BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltWidgetExtension.entitlements; sourceTree = ""; }; + BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnisetteDataManager.swift; sourceTree = ""; }; + BF8CAE432489E772004D6CCE /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = ""; }; + BF8CAE442489E772004D6CCE /* DaemonRequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DaemonRequestHandler.swift; sourceTree = ""; }; + BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Jailbreak.swift"; sourceTree = ""; }; BF8F69C122E659F700049BA1 /* AppContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentViewController.swift; sourceTree = ""; }; BF8F69C322E662D300049BA1 /* AppViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = ""; }; - BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientConnection.swift; sourceTree = ""; }; + BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AltWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + BF989170250AABF4002ACF50 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BF989172250AABF4002ACF50 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BF98917C250AAC4F002ACF50 /* Countdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Countdown.swift; sourceTree = ""; }; + BF98917D250AAC4F002ACF50 /* AltWidget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AltWidget.swift; sourceTree = ""; }; + BF989190250AAE86002ACF50 /* ViewAppIntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewAppIntentHandler.swift; sourceTree = ""; }; + BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = ViewApp.intentdefinition; sourceTree = ""; }; BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseViewController.swift; sourceTree = ""; }; BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseCollectionViewCell.swift; sourceTree = ""; }; BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotCollectionViewCell.swift; sourceTree = ""; }; BF9ABA4A22DD137F008935CF /* NavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = ""; }; BF9ABA4C22DD16DE008935CF /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = ""; }; - BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = ""; }; - BF9B63C5229DD44D002F0A62 /* AltSign.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AltSign.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BF9F8D18242AA6680024E48B /* AltStore 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 6.xcdatamodel"; sourceTree = ""; }; - BF9F8D19242AA6BC0024E48B /* AltStore5ToAltStore6.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore5ToAltStore6.xcmappingmodel; sourceTree = ""; }; BFA8172823C56042001B5953 /* ServerConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConnection.swift; sourceTree = ""; }; BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAnisetteDataOperation.swift; sourceTree = ""; }; - BFA8172C23C5823E001B5953 /* InstalledExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledExtension.swift; sourceTree = ""; }; - BFB11691229322E400BB457C /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = ""; }; - BFB1169A2293274D00BB457C /* JSONDecoder+Properties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Properties.swift"; sourceTree = ""; }; BFB1169C22932DB100BB457C /* apps.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = apps.json; sourceTree = ""; }; BFB364592325985F00CD0EB1 /* FindServerOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindServerOperation.swift; sourceTree = ""; }; + BFB39B5B252BC10E00D1BE50 /* Managed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Managed.swift; sourceTree = ""; }; BFB4323E22DE852000B7F8BC /* UpdateCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UpdateCollectionViewCell.xib; sourceTree = ""; }; BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ALTAnisetteData.m; path = "Dependencies/AltSign/AltSign/Model/Apple API/ALTAnisetteData.m"; sourceTree = SOURCE_ROOT; }; BFB49AA923834CF900D542D9 /* ALTAnisetteData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ALTAnisetteData.h; path = "Dependencies/AltSign/AltSign/Model/Apple API/ALTAnisetteData.h"; sourceTree = SOURCE_ROOT; }; - BFB6B21A23186D640022A802 /* NewsItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsItem.swift; sourceTree = ""; }; BFB6B21D231870160022A802 /* NewsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsViewController.swift; sourceTree = ""; }; BFB6B21F231870B00022A802 /* NewsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsCollectionViewCell.swift; sourceTree = ""; }; BFB6B22323187A3D0022A802 /* NewsCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NewsCollectionViewCell.xib; sourceTree = ""; }; BFBAC8852295C90300587369 /* Result+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Conveniences.swift"; sourceTree = ""; }; - BFBBE2DC22931B20002097FA /* AltStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AltStore.xcdatamodel; sourceTree = ""; }; - BFBBE2DE22931F73002097FA /* StoreApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp.swift; sourceTree = ""; }; - BFBBE2E022931F81002097FA /* InstalledApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledApp.swift; sourceTree = ""; }; + BFBF33142526754700B7B8C9 /* AltStore 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 9.xcdatamodel"; sourceTree = ""; }; + BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore8ToAltStore9.xcmappingmodel; sourceTree = ""; }; BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadAppOperation.swift; sourceTree = ""; }; BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeactivateAppOperation.swift; sourceTree = ""; }; BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledAppsCollectionHeaderView.swift; sourceTree = ""; }; BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InstalledAppsCollectionHeaderView.xib; sourceTree = ""; }; + BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = ""; }; + BFC712C12512D5F100AB5EBE /* XPCConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XPCConnection.swift; sourceTree = ""; }; + BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XPCConnectionHandler.swift; sourceTree = ""; }; BFC84A4C2421A19100853474 /* SourcesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesViewController.swift; sourceTree = ""; }; + BFCB9206250AB2120057B44E /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + BFCCB519245E3401001853EA /* VerifyAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifyAppOperation.swift; sourceTree = ""; }; BFD2476A2284B9A500981D42 /* AltStore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AltStore.app; sourceTree = BUILT_PRODUCTS_DIR; }; BFD2476D2284B9A500981D42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; BFD247742284B9A500981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -519,7 +687,7 @@ BFD2478E2284C8F900981D42 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+AltStore.swift"; sourceTree = ""; }; BFD44605241188C300EAB90A /* CodableServerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableServerError.swift; sourceTree = ""; }; - BFD52BD222A06EFB000B7ED1 /* AltKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AltKit.h; sourceTree = ""; }; + BFD52BD222A06EFB000B7ED1 /* ALTConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTConstants.h; sourceTree = ""; }; BFD52BD322A0800A000B7ED1 /* ServerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerManager.swift; sourceTree = ""; }; BFD52BE522A1A9CA000B7ED1 /* ptrarray.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ptrarray.c; path = Dependencies/libplist/src/ptrarray.c; sourceTree = SOURCE_ROOT; }; BFD52BE622A1A9CA000B7ED1 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = base64.c; path = Dependencies/libplist/src/base64.c; sourceTree = SOURCE_ROOT; }; @@ -552,33 +720,29 @@ BFD52C1D22A1A9EC000B7ED1 /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node.c; path = Dependencies/libplist/libcnary/node.c; sourceTree = SOURCE_ROOT; }; BFD52C1E22A1A9EC000B7ED1 /* node_list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node_list.c; path = Dependencies/libplist/libcnary/node_list.c; sourceTree = SOURCE_ROOT; }; BFD52C1F22A1A9EC000B7ED1 /* cnary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cnary.c; path = Dependencies/libplist/libcnary/cnary.c; sourceTree = SOURCE_ROOT; }; - BFD5D6E7230CC961007955AB /* PatreonAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonAPI.swift; sourceTree = ""; }; - BFD5D6E9230CCAE5007955AB /* PatreonAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonAccount.swift; sourceTree = ""; }; - BFD5D6ED230D8A86007955AB /* Patron.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Patron.swift; sourceTree = ""; }; - BFD5D6F1230DD974007955AB /* Benefit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Benefit.swift; sourceTree = ""; }; - BFD5D6F3230DDB0A007955AB /* Campaign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Campaign.swift; sourceTree = ""; }; - BFD5D6F5230DDB12007955AB /* Tier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tier.swift; sourceTree = ""; }; BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsComponents.swift; sourceTree = ""; }; BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+RelativeDate.swift"; sourceTree = ""; }; BFDB5B2522EFBBEA00F74113 /* BrowseCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BrowseCollectionViewCell.xib; sourceTree = ""; }; - BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = ""; }; BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignAppOperation.swift; sourceTree = ""; }; BFDB6A0A22AAEDB7007EA6D6 /* Operation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; BFDB6A0C22AAFC19007EA6D6 /* OperationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationError.swift; sourceTree = ""; }; BFDB6A0E22AB2776007EA6D6 /* SendAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendAppOperation.swift; sourceTree = ""; }; - BFE338DC22F0E7F3002E24B9 /* Source.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source.swift; sourceTree = ""; }; + BFDBBD7F246CB84F004ED2F3 /* RemoveAppBackupOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAppBackupOperation.swift; sourceTree = ""; }; + BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "INInteraction+AltStore.swift"; sourceTree = ""; }; BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchSourceOperation.swift; sourceTree = ""; }; BFE338E722F10E56002E24B9 /* LaunchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchViewController.swift; sourceTree = ""; }; BFE48974238007CE003239E0 /* AnisetteDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnisetteDataManager.swift; sourceTree = ""; }; + BFE4FFB5251BF7BA0018CF9B /* AltPlugin.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = AltPlugin.zip; sourceTree = ""; }; BFE60737231ADF49002B0E8E /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; BFE60739231ADF82002B0E8E /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; BFE6073B231AE1E7002B0E8E /* SettingsHeaderFooterView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsHeaderFooterView.xib; sourceTree = ""; }; BFE6073F231AFD2A002B0E8E /* InsetGroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsetGroupTableViewCell.swift; sourceTree = ""; }; BFE60741231B07E6002B0E8E /* SettingsHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHeaderFooterView.swift; sourceTree = ""; }; BFE6325922A83BEB00F30809 /* Authentication.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Authentication.storyboard; sourceTree = ""; }; - BFE6326522A857C100F30809 /* Team.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Team.swift; sourceTree = ""; }; - BFE6326722A858F300F30809 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; BFE6326B22A86FF300F30809 /* AuthenticationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationOperation.swift; sourceTree = ""; }; + BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = ""; }; + BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundRefreshAppsOperation.swift; sourceTree = ""; }; + BFF00D332501BDCF00746320 /* IntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; BFF0B68D23219520007A79E1 /* PatreonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonViewController.swift; sourceTree = ""; }; BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonComponents.swift; sourceTree = ""; }; BFF0B6912321A305007A79E1 /* AboutPatreonHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AboutPatreonHeaderView.xib; sourceTree = ""; }; @@ -586,6 +750,12 @@ BFF0B695232242D3007A79E1 /* LicensesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesViewController.swift; sourceTree = ""; }; BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsViewController.swift; sourceTree = ""; }; BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+CompactHeight.swift"; sourceTree = ""; }; + BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WirelessConnectionHandler.swift; sourceTree = ""; }; + BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTServerError+Conveniences.swift"; sourceTree = ""; }; + BFF767CD2489ABE90097E58C /* NetworkConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = ""; }; + BFF7EC4C25081E9300BDE521 /* AltStore 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 8.xcdatamodel"; sourceTree = ""; }; + BFFCFA45248835530077BFCE /* AltDaemon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltDaemon.entitlements; sourceTree = ""; }; + C9EEAA842DA87A88A870053B /* Pods_AltStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltStore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA79A60285C6AF5848AA16E9 /* Pods-AltStore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStore.debug.xcconfig"; path = "Target Support Files/Pods-AltStore/Pods-AltStore.debug.xcconfig"; sourceTree = ""; }; FC3822AB1C4CF1D4CDF7445D /* Pods_AltServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltServer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -598,11 +768,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BF1E314D22A0616100370A3C /* Frameworks */ = { + BF18BFE424857D7900DD5981 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BF4C7F2523801F0800B2556E /* AltSign.framework in Frameworks */, + BF088D0F25019ABA008082D9 /* AltSign-Static in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -610,16 +780,23 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BF1E315F22A0635900370A3C /* libAltKit.a in Frameworks */, + BF088D382501A833008082D9 /* OpenSSL.xcframework in Frameworks */, BF4588882298DD3F00BD7491 /* libxml2.tbd in Frameworks */, BF44CC6C232AEB90004DA9C3 /* LaunchAtLogin.framework in Frameworks */, BF4588472298D4B000BD7491 /* libimobiledevice.a in Frameworks */, - BF0201BD22C2EFBC000B93E4 /* openssl.framework in Frameworks */, - BF0201BA22C2EFA3000B93E4 /* AltSign.framework in Frameworks */, + BF088D362501A821008082D9 /* AltSign-Dynamic in Frameworks */, A8BCEBEAC0620CF80A2FD26D /* Pods_AltServer.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; + BF580478246A28F7008AE704 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BF580498246A3D19008AE704 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BF5C5FC2237DF5AE00EDD0C6 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -627,12 +804,32 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF66EE7B2501AE50007EE018 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BF42345D25102688006D1EB2 /* OpenSSL.xcframework in Frameworks */, + BF42345C251024B0006D1EB2 /* AltSign-Static in Frameworks */, + 0E33F94B8D78AB969FD309A3 /* Pods_AltStoreCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BF989164250AABF3002ACF50 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BFD247672284B9A500981D42 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BF1E316022A0636400370A3C /* libAltKit.a in Frameworks */, - 01100C7036F0EBAC5B30984B /* libPods-AltStore.a in Frameworks */, + BF1614F1250822F100767AEA /* Roxas.framework in Frameworks */, + BF088D332501A4FF008082D9 /* OpenSSL.xcframework in Frameworks */, + BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */, + 2A77E3D272F3D92436FAC272 /* Pods_AltStore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -646,6 +843,8 @@ A136EE677716B80768E9F0A2 /* Pods-AltStore.release.xcconfig */, 11611D46F8A7C8B928E8156B /* Pods-AltServer.debug.xcconfig */, 589BA531D903B28F292063E5 /* Pods-AltServer.release.xcconfig */, + B39BC452F0753C2C33A2D43B /* Pods-AltStoreCore.debug.xcconfig */, + BC373DB2C2B6CB739CCBFB5F /* Pods-AltStoreCore.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -666,63 +865,47 @@ path = Analytics; sourceTree = ""; }; - BF100C4E232D7C95006A8926 /* Migrations */ = { + BF18BFE824857D7900DD5981 /* AltDaemon */ = { isa = PBXGroup; children = ( - BF100C52232D7D9E006A8926 /* Policies */, - BF100C51232D7D91006A8926 /* Mapping Models */, + BFFCFA45248835530077BFCE /* AltDaemon.entitlements */, + BF10EB33248730750055E6DB /* main.swift */, + BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */, + BF8CAE442489E772004D6CCE /* DaemonRequestHandler.swift */, + BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */, + BF8CAE432489E772004D6CCE /* AppManager.swift */, + BF18C0032485B4DE00DD5981 /* AltDaemon-Bridging-Header.h */, ); - path = Migrations; + path = AltDaemon; sourceTree = ""; }; - BF100C51232D7D91006A8926 /* Mapping Models */ = { + BF18BFFF2485A75F00DD5981 /* Server Protocol */ = { isa = PBXGroup; children = ( - BF100C4F232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel */, - BF7C627123DBB3B400515A2D /* AltStore2ToAltStore3.xcmappingmodel */, - BF0F5FC623F394AD0080DB64 /* AltStore3ToAltStore4.xcmappingmodel */, - BF6C33642419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel */, - BF9F8D19242AA6BC0024E48B /* AltStore5ToAltStore6.xcmappingmodel */, - ); - path = "Mapping Models"; - sourceTree = ""; - }; - BF100C52232D7D9E006A8926 /* Policies */ = { - isa = PBXGroup; - children = ( - BF100C53232D7DAE006A8926 /* StoreAppPolicy.swift */, - BF7C627323DBB78C00515A2D /* InstalledAppPolicy.swift */, - ); - path = Policies; - sourceTree = ""; - }; - BF1E315122A0616100370A3C /* AltKit */ = { - isa = PBXGroup; - children = ( - BFD52BD222A06EFB000B7ED1 /* AltKit.h */, - BF718BD723C93DB700A89F2D /* AltKit.m */, - BFBAC8852295C90300587369 /* Result+Conveniences.swift */, - BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */, BF1E3128229F474900370A3C /* ServerProtocol.swift */, BFD44605241188C300EAB90A /* CodableServerError.swift */, - BF1E314822A060F400370A3C /* NSError+ALTServerError.h */, - BF1E314922A060F400370A3C /* NSError+ALTServerError.m */, - BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */, - BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */, ); - path = AltKit; + path = "Server Protocol"; + sourceTree = ""; + }; + BF1E315122A0616100370A3C /* Shared */ = { + isa = PBXGroup; + children = ( + BFD52BD222A06EFB000B7ED1 /* ALTConstants.h */, + BF718BD723C93DB700A89F2D /* ALTConstants.m */, + BF18BFFF2485A75F00DD5981 /* Server Protocol */, + BFF767CF2489AC240097E58C /* Connections */, + BFF767C32489A6800097E58C /* Extensions */, + BFF767C42489A6980097E58C /* Categories */, + ); + path = Shared; sourceTree = ""; }; BF3D648922E79A7700E9056B /* Types */ = { isa = PBXGroup; children = ( - BF3D648B22E79AC800E9056B /* ALTAppPermission.h */, - BF3D648C22E79AC800E9056B /* ALTAppPermission.m */, - BF54E81F2315EF0D000AE0D8 /* ALTPatreonBenefitType.h */, - BF54E8202315EF0D000AE0D8 /* ALTPatreonBenefitType.m */, - BF26A0DF2370C5D400F53F9F /* ALTSourceUserInfoKey.h */, - BF26A0E02370C5D400F53F9F /* ALTSourceUserInfoKey.m */, BF41B807233433C100C593A3 /* LoadingState.swift */, + BFB39B5B252BC10E00D1BE50 /* Managed.swift */, ); path = Types; sourceTree = ""; @@ -744,6 +927,7 @@ BF45868F229872EA00BD7491 /* AppDelegate.swift */, BF458695229872EA00BD7491 /* Main.storyboard */, BFE48974238007CE003239E0 /* AnisetteDataManager.swift */, + BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */, BF703195229F36FF006E110F /* Devices */, BFD52BDC22A0A659000B7ED1 /* Connections */, BF055B4A233B528B0086DEA9 /* Extensions */, @@ -900,6 +1084,21 @@ path = "App IDs"; sourceTree = ""; }; + BF58047C246A28F7008AE704 /* AltBackup */ = { + isa = PBXGroup; + children = ( + BF580499246A4153008AE704 /* AltBackup.entitlements */, + BF58047D246A28F7008AE704 /* AppDelegate.swift */, + BF580481246A28F7008AE704 /* ViewController.swift */, + BF44EEEF246B08BA002A52F2 /* BackupController.swift */, + BF580495246A3CB5008AE704 /* UIColor+AltBackup.swift */, + BF580486246A28F9008AE704 /* Assets.xcassets */, + BF580488246A28F9008AE704 /* LaunchScreen.storyboard */, + BF58048B246A28F9008AE704 /* Info.plist */, + ); + path = AltBackup; + sourceTree = ""; + }; BF5C5FC6237DF5AE00EDD0C6 /* AltPlugin */ = { isa = PBXGroup; children = ( @@ -912,6 +1111,134 @@ path = AltPlugin; sourceTree = ""; }; + BF66EE7F2501AE50007EE018 /* AltStoreCore */ = { + isa = PBXGroup; + children = ( + BF66EE802501AE50007EE018 /* AltStoreCore.h */, + BF66EE8A2501AEB1007EE018 /* Components */, + BF66EEE32501AED0007EE018 /* Extensions */, + BF66EEAA2501AECA007EE018 /* Model */, + BF66EE9F2501AEC5007EE018 /* Patreon */, + BF98918B250AAE18002ACF50 /* Intents */, + BF66EE9A2501AEC1007EE018 /* Protocols */, + BF66EE8D2501AEBC007EE018 /* Types */, + BF66EE812501AE50007EE018 /* Info.plist */, + BFCB9205250AB1FF0057B44E /* Resources */, + ); + path = AltStoreCore; + sourceTree = ""; + }; + BF66EE8A2501AEB1007EE018 /* Components */ = { + isa = PBXGroup; + children = ( + BF66EE8B2501AEB1007EE018 /* Keychain.swift */, + ); + path = Components; + sourceTree = ""; + }; + BF66EE8D2501AEBC007EE018 /* Types */ = { + isa = PBXGroup; + children = ( + BF66EE8E2501AEBC007EE018 /* ALTAppPermission.h */, + BF66EE912501AEBC007EE018 /* ALTAppPermission.m */, + BF66EE922501AEBC007EE018 /* ALTPatreonBenefitType.h */, + BF66EE902501AEBC007EE018 /* ALTPatreonBenefitType.m */, + BF66EE8F2501AEBC007EE018 /* ALTSourceUserInfoKey.h */, + BF66EE932501AEBC007EE018 /* ALTSourceUserInfoKey.m */, + ); + path = Types; + sourceTree = ""; + }; + BF66EE9A2501AEC1007EE018 /* Protocols */ = { + isa = PBXGroup; + children = ( + BF66EE9B2501AEC1007EE018 /* AppProtocol.swift */, + BF66EE9C2501AEC1007EE018 /* Fetchable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + BF66EE9F2501AEC5007EE018 /* Patreon */ = { + isa = PBXGroup; + children = ( + BF66EEA02501AEC5007EE018 /* Benefit.swift */, + BF66EEA22501AEC5007EE018 /* Campaign.swift */, + BF66EEA12501AEC5007EE018 /* PatreonAPI.swift */, + BF66EEA32501AEC5007EE018 /* Patron.swift */, + BF66EEA42501AEC5007EE018 /* Tier.swift */, + ); + path = Patreon; + sourceTree = ""; + }; + BF66EEAA2501AECA007EE018 /* Model */ = { + isa = PBXGroup; + children = ( + BF66EEB72501AECA007EE018 /* AltStore.xcdatamodeld */, + BF66EEC92501AECA007EE018 /* Account.swift */, + BF66EEC72501AECA007EE018 /* AppID.swift */, + BF66EEC62501AECA007EE018 /* AppPermission.swift */, + BF66EECA2501AECA007EE018 /* DatabaseManager.swift */, + BF66EEC02501AECA007EE018 /* InstalledApp.swift */, + BF66EECB2501AECA007EE018 /* InstalledExtension.swift */, + BF66EEC52501AECA007EE018 /* MergePolicy.swift */, + BF66EEBF2501AECA007EE018 /* NewsItem.swift */, + BF66EEC82501AECA007EE018 /* PatreonAccount.swift */, + BF66EEC32501AECA007EE018 /* RefreshAttempt.swift */, + BF66EEC12501AECA007EE018 /* SecureValueTransformer.swift */, + BF66EEAB2501AECA007EE018 /* Source.swift */, + BF66EEC42501AECA007EE018 /* StoreApp.swift */, + BF66EEC22501AECA007EE018 /* Team.swift */, + BF66EEAC2501AECA007EE018 /* Migrations */, + ); + path = Model; + sourceTree = ""; + }; + BF66EEAC2501AECA007EE018 /* Migrations */ = { + isa = PBXGroup; + children = ( + BF66EEAD2501AECA007EE018 /* Policies */, + BF66EEB02501AECA007EE018 /* Mapping Models */, + ); + path = Migrations; + sourceTree = ""; + }; + BF66EEAD2501AECA007EE018 /* Policies */ = { + isa = PBXGroup; + children = ( + BF66EEAE2501AECA007EE018 /* StoreAppPolicy.swift */, + BF66EEAF2501AECA007EE018 /* InstalledAppPolicy.swift */, + ); + path = Policies; + sourceTree = ""; + }; + BF66EEB02501AECA007EE018 /* Mapping Models */ = { + isa = PBXGroup; + children = ( + BF66EEB12501AECA007EE018 /* AltStoreToAltStore2.xcmappingmodel */, + BF66EEB22501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel */, + BF66EEB32501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel */, + BF66EEB42501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel */, + BF66EEB52501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel */, + BF66EEB62501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel */, + BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */, + ); + path = "Mapping Models"; + sourceTree = ""; + }; + BF66EEE32501AED0007EE018 /* Extensions */ = { + isa = PBXGroup; + children = ( + BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */, + BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */, + BF66EEE52501AED0007EE018 /* JSONDecoder+Properties.swift */, + BF66EEE72501AED0007EE018 /* UIApplication+AppExtension.swift */, + BF66EEE62501AED0007EE018 /* UIColor+Hex.swift */, + BF66EEE42501AED0007EE018 /* UserDefaults+AltStore.swift */, + BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */, + ); + path = Extensions; + sourceTree = ""; + }; BF6C8FA8242935CA00125131 /* Dependencies */ = { isa = PBXGroup; children = ( @@ -932,6 +1259,7 @@ BF703194229F36F6006E110F /* Resources */ = { isa = PBXGroup; children = ( + BFE4FFB5251BF7BA0018CF9B /* AltPlugin.zip */, BF458693229872EA00BD7491 /* Assets.xcassets */, ); name = Resources; @@ -957,6 +1285,28 @@ name = "Supporting Files"; sourceTree = ""; }; + BF98916C250AABF3002ACF50 /* AltWidget */ = { + isa = PBXGroup; + children = ( + BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */, + BF98917D250AAC4F002ACF50 /* AltWidget.swift */, + BF42345825101C1D006D1EB2 /* WidgetView.swift */, + BF98917C250AAC4F002ACF50 /* Countdown.swift */, + BF989170250AABF4002ACF50 /* Assets.xcassets */, + BF989172250AABF4002ACF50 /* Info.plist */, + ); + path = AltWidget; + sourceTree = ""; + }; + BF98918B250AAE18002ACF50 /* Intents */ = { + isa = PBXGroup; + children = ( + BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */, + BF989190250AAE86002ACF50 /* ViewAppIntentHandler.swift */, + ); + path = Intents; + sourceTree = ""; + }; BF9ABA4322DCFF33008935CF /* Browse */ = { isa = PBXGroup; children = ( @@ -1009,14 +1359,26 @@ path = Sources; sourceTree = ""; }; + BFCB9205250AB1FF0057B44E /* Resources */ = { + isa = PBXGroup; + children = ( + BFCB9206250AB2120057B44E /* Colors.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; BFD247612284B9A500981D42 = { isa = PBXGroup; children = ( BFD2476C2284B9A500981D42 /* AltStore */, + BF66EE7F2501AE50007EE018 /* AltStoreCore */, BF45868E229872EA00BD7491 /* AltServer */, - BF1E315122A0616100370A3C /* AltKit */, + BF1E315122A0616100370A3C /* Shared */, BF45872C2298D31600BD7491 /* libimobiledevice */, BF5C5FC6237DF5AE00EDD0C6 /* AltPlugin */, + BF58047C246A28F7008AE704 /* AltBackup */, + BF18BFE824857D7900DD5981 /* AltDaemon */, + BF98916C250AABF3002ACF50 /* AltWidget */, BFD247852284BB3300981D42 /* Frameworks */, BFD2476B2284B9A500981D42 /* Products */, 4460E048E3AC1C9708C4FA33 /* Pods */, @@ -1029,8 +1391,11 @@ BFD2476A2284B9A500981D42 /* AltStore.app */, BF45868D229872EA00BD7491 /* AltServer.app */, BF45872B2298D31600BD7491 /* libimobiledevice.a */, - BF1E315022A0616100370A3C /* libAltKit.a */, BF5C5FC5237DF5AE00EDD0C6 /* AltPlugin.mailbundle */, + BF58047B246A28F7008AE704 /* AltBackup.app */, + BF18BFE724857D7900DD5981 /* AltDaemon */, + BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */, + BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -1040,6 +1405,7 @@ children = ( BF219A7E22CAC431007676A6 /* AltStore.entitlements */, BFD2476D2284B9A500981D42 /* AppDelegate.swift */, + BF4B78FD24B3D1DB008AB4AC /* SceneDelegate.swift */, BFD247732284B9A500981D42 /* Main.storyboard */, BFE338E722F10E56002E24B9 /* LaunchViewController.swift */, BF41B805233423AE00C593A3 /* TabBarController.swift */, @@ -1049,17 +1415,15 @@ BF3D64A022E7FAD800E9056B /* App Detail */, BFBBE2E2229320A2002097FA /* My Apps */, BFDB69FB22A9A7A6007EA6D6 /* Settings */, - BFD5D6E6230CC94B007955AB /* Patreon */, BFD2478A2284C49000981D42 /* Managing Apps */, BF56D2AD23DF9E170006506D /* App IDs */, BFC84A4B2421A13000853474 /* Sources */, BFC51D7922972F1F00388324 /* Server */, BF0DCA642433BDE200E3A595 /* Analytics */, - BFD247982284D7FC00981D42 /* Model */, + BFF00D2E2501BD4B00746320 /* Intents */, BFDB6A0922AAEDA1007EA6D6 /* Operations */, BFD2478D2284C4C700981D42 /* Components */, BF3D648922E79A7700E9056B /* Types */, - BFDB6A0622A9B114007EA6D6 /* Protocols */, BFD2479D2284FBBD00981D42 /* Extensions */, BFD247962284D7C100981D42 /* Resources */, BF6C8FA8242935CA00125131 /* Dependencies */, @@ -1071,16 +1435,14 @@ BFD247852284BB3300981D42 /* Frameworks */ = { isa = PBXGroup; children = ( + BF088D322501A4FF008082D9 /* OpenSSL.xcframework */, + BF580497246A3D19008AE704 /* UIKit.framework */, BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */, - BF9B63C5229DD44D002F0A62 /* AltSign.framework */, - BF4588962298DE6E00BD7491 /* libzip.framework */, BF4588872298DD3F00BD7491 /* libxml2.tbd */, - BF4588542298DC5400BD7491 /* openssl.framework */, BFD247862284BB3B00981D42 /* Roxas.framework */, - BF5AB3A72285FE6C00DC914B /* AltSign.framework */, - BF4713A422976CFC00784A2F /* openssl.framework */, FC3822AB1C4CF1D4CDF7445D /* Pods_AltServer.framework */, - 0DE618FA97EA42C3F468D186 /* libPods-AltStore.a */, + C9EEAA842DA87A88A870053B /* Pods_AltStore.framework */, + A08F67C18350C7990753F03F /* Pods_AltStoreCore.framework */, ); name = Frameworks; sourceTree = ""; @@ -1089,6 +1451,7 @@ isa = PBXGroup; children = ( BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */, + BF88F97124F8727D00BB75DF /* AppManagerErrors.swift */, ); path = "Managing Apps"; sourceTree = ""; @@ -1099,7 +1462,6 @@ BFD2478B2284C4C300981D42 /* AppIconImageView.swift */, BF74989A23621C0700CED65F /* ForwardingNavigationController.swift */, BFD2478E2284C8F900981D42 /* Button.swift */, - BF43002D22A714AF0051E2BC /* Keychain.swift */, BF770E6622BD57C3002A40FE /* BackgroundTaskManager.swift */, BF9ABA4A22DD137F008935CF /* NavigationBar.swift */, BF9ABA4C22DD16DE008935CF /* PillButton.swift */, @@ -1116,6 +1478,7 @@ BFD247962284D7C100981D42 /* Resources */ = { isa = PBXGroup; children = ( + BF44EEF2246B3A17002A52F2 /* AltBackup.ipa */, BFB1169C22932DB100BB457C /* apps.json */, BFD247762284B9A700981D42 /* Assets.xcassets */, BF770E6822BD57DD002A40FE /* Silence.m4a */, @@ -1133,39 +1496,14 @@ name = "Supporting Files"; sourceTree = ""; }; - BFD247982284D7FC00981D42 /* Model */ = { - isa = PBXGroup; - children = ( - BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */, - BFB11691229322E400BB457C /* DatabaseManager.swift */, - BF3D64A122E8031100E9056B /* MergePolicy.swift */, - BFE6326722A858F300F30809 /* Account.swift */, - BF56D2A923DF88310006506D /* AppID.swift */, - BF3D648722E79A3700E9056B /* AppPermission.swift */, - BFBBE2E022931F81002097FA /* InstalledApp.swift */, - BFA8172C23C5823E001B5953 /* InstalledExtension.swift */, - BFB6B21A23186D640022A802 /* NewsItem.swift */, - BFD5D6E9230CCAE5007955AB /* PatreonAccount.swift */, - BF02419322F2156E00129732 /* RefreshAttempt.swift */, - BFE338DC22F0E7F3002E24B9 /* Source.swift */, - BFBBE2DE22931F73002097FA /* StoreApp.swift */, - BFE6326522A857C100F30809 /* Team.swift */, - BF100C4E232D7C95006A8926 /* Migrations */, - ); - path = Model; - sourceTree = ""; - }; BFD2479D2284FBBD00981D42 /* Extensions */ = { isa = PBXGroup; children = ( - BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */, - BFB1169A2293274D00BB457C /* JSONDecoder+Properties.swift */, - BF43002F22A71C960051E2BC /* UserDefaults+AltStore.swift */, - BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */, - BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */, BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */, - BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */, + BF6C336124197D700034FD24 /* NSError+AltStore.swift */, BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */, + BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */, + BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */, ); path = Extensions; sourceTree = ""; @@ -1173,30 +1511,19 @@ BFD52BDC22A0A659000B7ED1 /* Connections */ = { isa = PBXGroup; children = ( - BF1E3129229F474900370A3C /* ConnectionManager.swift */, - BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */, + BF1E3129229F474900370A3C /* RequestHandler.swift */, + BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */, + BF18BFFC2485A1E400DD5981 /* WiredConnectionHandler.swift */, BF718BCF23C91BD300A89F2D /* ALTWiredConnection.h */, BF718BD223C91C7000A89F2D /* ALTWiredConnection+Private.h */, - BF718BD023C91BD300A89F2D /* ALTWiredConnection.m */, + BF718BD023C91BD300A89F2D /* ALTWiredConnection.mm */, BF718BD323C928A300A89F2D /* ALTNotificationConnection.h */, BF718BD623C92B3700A89F2D /* ALTNotificationConnection+Private.h */, - BF718BD423C928A300A89F2D /* ALTNotificationConnection.m */, + BF718BD423C928A300A89F2D /* ALTNotificationConnection.mm */, ); path = Connections; sourceTree = ""; }; - BFD5D6E6230CC94B007955AB /* Patreon */ = { - isa = PBXGroup; - children = ( - BFD5D6E7230CC961007955AB /* PatreonAPI.swift */, - BFD5D6ED230D8A86007955AB /* Patron.swift */, - BFD5D6F3230DDB0A007955AB /* Campaign.swift */, - BFD5D6F5230DDB12007955AB /* Tier.swift */, - BFD5D6F1230DD974007955AB /* Benefit.swift */, - ); - path = Patreon; - sourceTree = ""; - }; BFDB69FB22A9A7A6007EA6D6 /* Settings */ = { isa = PBXGroup; children = ( @@ -1214,15 +1541,6 @@ path = Settings; sourceTree = ""; }; - BFDB6A0622A9B114007EA6D6 /* Protocols */ = { - isa = PBXGroup; - children = ( - BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */, - BF258CE222EBAE2800023032 /* AppProtocol.swift */, - ); - path = Protocols; - sourceTree = ""; - }; BFDB6A0922AAEDA1007EA6D6 /* Operations */ = { isa = PBXGroup; children = ( @@ -1242,6 +1560,11 @@ BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */, BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */, BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */, + BFCCB519245E3401001853EA /* VerifyAppOperation.swift */, + BF44EEFB246B4550002A52F2 /* RemoveAppOperation.swift */, + BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */, + BFDBBD7F246CB84F004ED2F3 /* RemoveAppBackupOperation.swift */, + BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */, ); path = Operations; sourceTree = ""; @@ -1257,6 +1580,49 @@ path = Authentication; sourceTree = ""; }; + BFF00D2E2501BD4B00746320 /* Intents */ = { + isa = PBXGroup; + children = ( + BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */, + BFF00D332501BDCF00746320 /* IntentHandler.swift */, + ); + path = Intents; + sourceTree = ""; + }; + BFF767C32489A6800097E58C /* Extensions */ = { + isa = PBXGroup; + children = ( + BFBAC8852295C90300587369 /* Result+Conveniences.swift */, + BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */, + BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */, + BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + BFF767C42489A6980097E58C /* Categories */ = { + isa = PBXGroup; + children = ( + BF1E314822A060F400370A3C /* NSError+ALTServerError.h */, + BF1E314922A060F400370A3C /* NSError+ALTServerError.m */, + BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */, + BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */, + ); + path = Categories; + sourceTree = ""; + }; + BFF767CF2489AC240097E58C /* Connections */ = { + isa = PBXGroup; + children = ( + BF18BFF22485828200DD5981 /* ConnectionManager.swift */, + BF18BFFE2485A42800DD5981 /* ALTConnection.h */, + BF18BFF624858BDE00DD5981 /* Connection.swift */, + BFF767CD2489ABE90097E58C /* NetworkConnection.swift */, + BFC712C12512D5F100AB5EBE /* XPCConnection.swift */, + ); + path = Connections; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1305,25 +1671,42 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF66EE792501AE50007EE018 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + BF66EE822501AE50007EE018 /* AltStoreCore.h in Headers */, + BF66EE952501AEBC007EE018 /* ALTSourceUserInfoKey.h in Headers */, + BF66EE982501AEBC007EE018 /* ALTPatreonBenefitType.h in Headers */, + BFAECC5F2501B0BF00528F27 /* ALTConstants.h in Headers */, + BFAECC5D2501B0BF00528F27 /* ALTConnection.h in Headers */, + BF66EE942501AEBC007EE018 /* ALTAppPermission.h in Headers */, + BFAECC602501B0BF00528F27 /* NSError+ALTServerError.h in Headers */, + BFAECC5E2501B0BF00528F27 /* CFNotificationName+AltStore.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - BF1E314F22A0616100370A3C /* AltKit */ = { + BF18BFE624857D7900DD5981 /* AltDaemon */ = { isa = PBXNativeTarget; - buildConfigurationList = BF1E315422A0616100370A3C /* Build configuration list for PBXNativeTarget "AltKit" */; + buildConfigurationList = BF18BFEB24857D7900DD5981 /* Build configuration list for PBXNativeTarget "AltDaemon" */; buildPhases = ( - BF1E314C22A0616100370A3C /* Sources */, - BF1E314D22A0616100370A3C /* Frameworks */, - BF1E314E22A0616100370A3C /* CopyFiles */, + BF18BFE324857D7900DD5981 /* Sources */, + BF18BFE424857D7900DD5981 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); - name = AltKit; - productName = AltKit; - productReference = BF1E315022A0616100370A3C /* libAltKit.a */; - productType = "com.apple.product-type.library.static"; + name = AltDaemon; + packageProductDependencies = ( + BF088D0E25019ABA008082D9 /* AltSign-Static */, + ); + productName = AltDaemon; + productReference = BF18BFE724857D7900DD5981 /* AltDaemon */; + productType = "com.apple.product-type.library.dynamic"; }; BF45868C229872EA00BD7491 /* AltServer */ = { isa = PBXNativeTarget; @@ -1342,10 +1725,12 @@ ); dependencies = ( BFBFFB272380C72F00993A4A /* PBXTargetDependency */, - BF1E315E22A0621F00370A3C /* PBXTargetDependency */, BF4588452298D48B00BD7491 /* PBXTargetDependency */, ); name = AltServer; + packageProductDependencies = ( + BF088D352501A821008082D9 /* AltSign-Dynamic */, + ); productName = AltServer; productReference = BF45868D229872EA00BD7491 /* AltServer.app */; productType = "com.apple.product-type.application"; @@ -1367,6 +1752,23 @@ productReference = BF45872B2298D31600BD7491 /* libimobiledevice.a */; productType = "com.apple.product-type.library.static"; }; + BF58047A246A28F7008AE704 /* AltBackup */ = { + isa = PBXNativeTarget; + buildConfigurationList = BF58048E246A28F9008AE704 /* Build configuration list for PBXNativeTarget "AltBackup" */; + buildPhases = ( + BF580477246A28F7008AE704 /* Sources */, + BF580478246A28F7008AE704 /* Frameworks */, + BF580479246A28F7008AE704 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AltBackup; + productName = AltBackup; + productReference = BF58047B246A28F7008AE704 /* AltBackup.app */; + productType = "com.apple.product-type.application"; + }; BF5C5FC4237DF5AE00EDD0C6 /* AltPlugin */ = { isa = PBXNativeTarget; buildConfigurationList = BF5C5FC8237DF5AE00EDD0C6 /* Build configuration list for PBXNativeTarget "AltPlugin" */; @@ -1385,6 +1787,46 @@ productReference = BF5C5FC5237DF5AE00EDD0C6 /* AltPlugin.mailbundle */; productType = "com.apple.product-type.bundle"; }; + BF66EE7D2501AE50007EE018 /* AltStoreCore */ = { + isa = PBXNativeTarget; + buildConfigurationList = BF66EE892501AE50007EE018 /* Build configuration list for PBXNativeTarget "AltStoreCore" */; + buildPhases = ( + 702C290C354EA165FF645858 /* [CP] Check Pods Manifest.lock */, + BF66EE792501AE50007EE018 /* Headers */, + BF66EE7A2501AE50007EE018 /* Sources */, + BF66EE7B2501AE50007EE018 /* Frameworks */, + BF66EE7C2501AE50007EE018 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AltStoreCore; + packageProductDependencies = ( + BF42345B251024B0006D1EB2 /* AltSign-Static */, + ); + productName = AltStoreCore; + productReference = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; + productType = "com.apple.product-type.framework"; + }; + BF989166250AABF3002ACF50 /* AltWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "AltWidgetExtension" */; + buildPhases = ( + BF989163250AABF3002ACF50 /* Sources */, + BF989164250AABF3002ACF50 /* Frameworks */, + BF989165250AABF3002ACF50 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BFF615AB2510042B00484D3B /* PBXTargetDependency */, + ); + name = AltWidgetExtension; + productName = AltWidgetExtension; + productReference = BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; BFD247692284B9A500981D42 /* AltStore */ = { isa = PBXNativeTarget; buildConfigurationList = BFD2477E2284B9A700981D42 /* Build configuration list for PBXNativeTarget "AltStore" */; @@ -1393,12 +1835,15 @@ BFD247662284B9A500981D42 /* Sources */, BFD247672284B9A500981D42 /* Frameworks */, BFD247682284B9A500981D42 /* Resources */, - 8C9013C41DD92A1476195C0E /* [CP] Copy Pods Resources */, + BF088D2B2501A087008082D9 /* Embed Frameworks */, + 744AE3B03F6BF664FC5705C5 /* [CP] Embed Pods Frameworks */, + BF98917B250AABF4002ACF50 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( - BF1E315C22A0621900370A3C /* PBXTargetDependency */, + BF66EE842501AE50007EE018 /* PBXTargetDependency */, + BF989176250AABF4002ACF50 /* PBXTargetDependency */, ); name = AltStore; productName = AltStore; @@ -1411,12 +1856,13 @@ BFD247622284B9A500981D42 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1120; + LastSwiftUpdateCheck = 1200; LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Riley Testut"; TargetAttributes = { - BF1E314F22A0616100370A3C = { - CreatedOnToolsVersion = 10.2.1; + BF18BFE624857D7900DD5981 = { + CreatedOnToolsVersion = 11.5; + LastSwiftMigration = 1150; }; BF45868C229872EA00BD7491 = { CreatedOnToolsVersion = 10.2.1; @@ -1433,10 +1879,20 @@ BF45872A2298D31600BD7491 = { CreatedOnToolsVersion = 10.2.1; }; + BF58047A246A28F7008AE704 = { + CreatedOnToolsVersion = 11.4.1; + }; BF5C5FC4237DF5AE00EDD0C6 = { CreatedOnToolsVersion = 11.2; LastSwiftMigration = 1120; }; + BF66EE7D2501AE50007EE018 = { + CreatedOnToolsVersion = 12.0; + }; + BF989166250AABF3002ACF50 = { + CreatedOnToolsVersion = 12.0; + LastSwiftMigration = 1200; + }; BFD247692284B9A500981D42 = { CreatedOnToolsVersion = 10.2.1; LastSwiftMigration = 1020; @@ -1466,9 +1922,12 @@ targets = ( BFD247692284B9A500981D42 /* AltStore */, BF45868C229872EA00BD7491 /* AltServer */, - BF1E314F22A0616100370A3C /* AltKit */, BF45872A2298D31600BD7491 /* libimobiledevice */, BF5C5FC4237DF5AE00EDD0C6 /* AltPlugin */, + BF58047A246A28F7008AE704 /* AltBackup */, + BF18BFE624857D7900DD5981 /* AltDaemon */, + BF66EE7D2501AE50007EE018 /* AltStoreCore */, + BF989166250AABF3002ACF50 /* AltWidgetExtension */, ); }; /* End PBXProject section */ @@ -1478,11 +1937,21 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + BFE4FFB6251BF7BA0018CF9B /* AltPlugin.zip in Resources */, BF458694229872EA00BD7491 /* Assets.xcassets in Resources */, BF458697229872EA00BD7491 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; + BF580479246A28F7008AE704 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF58048A246A28F9008AE704 /* LaunchScreen.storyboard in Resources */, + BF580487246A28F9008AE704 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BF5C5FC3237DF5AE00EDD0C6 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1490,6 +1959,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF66EE7C2501AE50007EE018 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BFCB9207250AB2120057B44E /* Colors.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BF989165250AABF3002ACF50 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF989171250AABF4002ACF50 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BFD247682284B9A500981D42 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1498,6 +1983,7 @@ BFB4323F22DE852000B7F8BC /* UpdateCollectionViewCell.xib in Resources */, BFE60738231ADF49002B0E8E /* Settings.storyboard in Resources */, BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */, + BF44EEF3246B3A17002A52F2 /* AltBackup.ipa in Resources */, BF770E6922BD57DD002A40FE /* Silence.m4a in Resources */, BFD247772284B9A700981D42 /* Assets.xcassets in Resources */, BFF0B6922321A305007A79E1 /* AboutPatreonHeaderView.xib in Resources */, @@ -1513,21 +1999,43 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 8C9013C41DD92A1476195C0E /* [CP] Copy Pods Resources */ = { + 702C290C354EA165FF645858 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-AltStore/Pods-AltStore-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-AltStore/Pods-AltStore-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-AltStoreCore-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AltStore/Pods-AltStore-resources.sh\"\n"; + 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; + }; + 744AE3B03F6BF664FC5705C5 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AltStore/Pods-AltStore-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AltStore/Pods-AltStore-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AltStore/Pods-AltStore-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 98BF22D155DBAEA97544E3E6 /* [CP] Embed Pods Frameworks */ = { @@ -1630,17 +2138,27 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - BF1E314C22A0616100370A3C /* Sources */ = { + BF18BFE324857D7900DD5981 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - BF718BD823C93DB700A89F2D /* AltKit.m in Sources */, - BF1E315A22A0620000370A3C /* NSError+ALTServerError.m in Sources */, - BF1E315922A061FB00370A3C /* Bundle+AltStore.swift in Sources */, - BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */, - BF718BC923C919E300A89F2D /* CFNotificationName+AltStore.m in Sources */, - BFD44606241188C400EAB90A /* CodableServerError.swift in Sources */, - BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */, + BF8CAE452489E772004D6CCE /* AnisetteDataManager.swift in Sources */, + BF1FE358251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */, + BFECAC8F24FD950E0077C41F /* Result+Conveniences.swift in Sources */, + BF8CAE472489E772004D6CCE /* DaemonRequestHandler.swift in Sources */, + BFECAC8824FD950E0077C41F /* CodableServerError.swift in Sources */, + BFC712C32512D5F100AB5EBE /* XPCConnection.swift in Sources */, + BFC712C52512D5F100AB5EBE /* XPCConnectionHandler.swift in Sources */, + BFECAC8A24FD950E0077C41F /* ALTServerError+Conveniences.swift in Sources */, + BFECAC8D24FD950E0077C41F /* ALTConstants.m in Sources */, + BFECAC8924FD950E0077C41F /* ConnectionManager.swift in Sources */, + BFECAC9524FD98BB0077C41F /* CFNotificationName+AltStore.m in Sources */, + BFECAC8E24FD950E0077C41F /* Connection.swift in Sources */, + BFECAC8B24FD950E0077C41F /* ServerProtocol.swift in Sources */, + BFECAC9624FD98BB0077C41F /* NSError+ALTServerError.m in Sources */, + BF10EB34248730750055E6DB /* main.swift in Sources */, + BF8CAE462489E772004D6CCE /* AppManager.swift in Sources */, + BFECAC9024FD950E0077C41F /* Bundle+AltStore.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1648,14 +2166,27 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */, + BFECAC8024FD950B0077C41F /* ConnectionManager.swift in Sources */, + BFECAC8324FD950B0077C41F /* NetworkConnection.swift in Sources */, + BFECAC8724FD950B0077C41F /* Bundle+AltStore.swift in Sources */, BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */, - BF718BD523C928A300A89F2D /* ALTNotificationConnection.m in Sources */, - BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */, - BF9A03C623C7DD0D000D08DB /* ClientConnection.swift in Sources */, - BF718BD123C91BD300A89F2D /* ALTWiredConnection.m in Sources */, + BF18BFFD2485A1E400DD5981 /* WiredConnectionHandler.swift in Sources */, + BFC712BB2512B9CF00AB5EBE /* PluginManager.swift in Sources */, + BFECAC8224FD950B0077C41F /* ServerProtocol.swift in Sources */, + BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */, + BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */, + BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */, + BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */, + BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */, + BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */, + BFECAC8524FD950B0077C41F /* Connection.swift in Sources */, BF458690229872EA00BD7491 /* AppDelegate.swift in Sources */, + BFECAC8424FD950B0077C41F /* ALTConstants.m in Sources */, BF4586C52298CDB800BD7491 /* ALTDeviceManager.mm in Sources */, BF0241AA22F29CCD00129732 /* UserDefaults+AltServer.swift in Sources */, + BFECAC9424FD98BA0077C41F /* NSError+ALTServerError.m in Sources */, + BFECAC9324FD98BA0077C41F /* CFNotificationName+AltStore.m in Sources */, BFE48975238007CE003239E0 /* AnisetteDataManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1721,6 +2252,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF580477246A28F7008AE704 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF580496246A3CB5008AE704 /* UIColor+AltBackup.swift in Sources */, + BF580482246A28F7008AE704 /* ViewController.swift in Sources */, + BF44EEF0246B08BA002A52F2 /* BackupController.swift in Sources */, + BF58049B246A432D008AE704 /* NSError+AltStore.swift in Sources */, + BF58047E246A28F7008AE704 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BF5C5FC1237DF5AE00EDD0C6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1730,6 +2273,79 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF66EE7A2501AE50007EE018 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF66EED32501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel in Sources */, + BF66EEA52501AEC5007EE018 /* Benefit.swift in Sources */, + BF66EED22501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel in Sources */, + BFAECC5B2501B0A400528F27 /* Bundle+AltStore.swift in Sources */, + BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */, + BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */, + BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */, + BFAECC522501B0A400528F27 /* CodableServerError.swift in Sources */, + BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */, + BF66EEDF2501AECA007EE018 /* PatreonAccount.swift in Sources */, + BFAECC532501B0A400528F27 /* ServerProtocol.swift in Sources */, + BFAECC572501B0A400528F27 /* ConnectionManager.swift in Sources */, + BF66EE9D2501AEC1007EE018 /* AppProtocol.swift in Sources */, + BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */, + BF66EE8C2501AEB2007EE018 /* Keychain.swift in Sources */, + BF66EED42501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel in Sources */, + BF66EE972501AEBC007EE018 /* ALTAppPermission.m in Sources */, + BFAECC552501B0A400528F27 /* Connection.swift in Sources */, + BF66EEDA2501AECA007EE018 /* RefreshAttempt.swift in Sources */, + BF66EEA92501AEC5007EE018 /* Tier.swift in Sources */, + BF66EEDB2501AECA007EE018 /* StoreApp.swift in Sources */, + BF66EEDE2501AECA007EE018 /* AppID.swift in Sources */, + BF66EECF2501AECA007EE018 /* AltStoreToAltStore2.xcmappingmodel in Sources */, + BF66EEA82501AEC5007EE018 /* Patron.swift in Sources */, + BF66EEDD2501AECA007EE018 /* AppPermission.swift in Sources */, + BFBF331B2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel in Sources */, + BF989184250AACFC002ACF50 /* Date+RelativeDate.swift in Sources */, + BF66EE962501AEBC007EE018 /* ALTPatreonBenefitType.m in Sources */, + BFAECC5A2501B0A400528F27 /* NetworkConnection.swift in Sources */, + BF66EEE92501AED0007EE018 /* JSONDecoder+Properties.swift in Sources */, + BF66EEEB2501AED0007EE018 /* UIApplication+AppExtension.swift in Sources */, + BF66EED92501AECA007EE018 /* Team.swift in Sources */, + BF66EED12501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel in Sources */, + BFAECC5C2501B0A400528F27 /* CFNotificationName+AltStore.m in Sources */, + BF66EED82501AECA007EE018 /* SecureValueTransformer.swift in Sources */, + BF8B17EB250AC40000F8157F /* FileManager+SharedDirectories.swift in Sources */, + BF66EEE02501AECA007EE018 /* Account.swift in Sources */, + BF66EED52501AECA007EE018 /* AltStore.xcdatamodeld in Sources */, + BFAECC582501B0A400528F27 /* ALTConstants.m in Sources */, + BFAECC562501B0A400528F27 /* ALTServerError+Conveniences.swift in Sources */, + BFAECC592501B0A400528F27 /* Result+Conveniences.swift in Sources */, + BFAECC542501B0A400528F27 /* NSError+ALTServerError.m in Sources */, + BF66EEE12501AECA007EE018 /* DatabaseManager.swift in Sources */, + BF66EEEA2501AED0007EE018 /* UIColor+Hex.swift in Sources */, + BF66EECC2501AECA007EE018 /* Source.swift in Sources */, + BF66EED72501AECA007EE018 /* InstalledApp.swift in Sources */, + BF66EECE2501AECA007EE018 /* InstalledAppPolicy.swift in Sources */, + BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */, + BF66EEA62501AEC5007EE018 /* PatreonAPI.swift in Sources */, + BF66EED02501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel in Sources */, + BF66EEDC2501AECA007EE018 /* MergePolicy.swift in Sources */, + BF66EEE22501AECA007EE018 /* InstalledExtension.swift in Sources */, + BF66EED62501AECA007EE018 /* NewsItem.swift in Sources */, + BF66EEA72501AEC5007EE018 /* Campaign.swift in Sources */, + BF66EE992501AEBC007EE018 /* ALTSourceUserInfoKey.m in Sources */, + BF989185250AAD1D002ACF50 /* UIColor+AltStore.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BF989163250AABF3002ACF50 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */, + BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */, + BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BFD247662284B9A500981D42 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1739,129 +2355,111 @@ BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */, BF3D649D22E7AC1B00E9056B /* PermissionPopoverViewController.swift in Sources */, BFD2478F2284C8F900981D42 /* Button.swift in Sources */, - BFD5D6F6230DDB12007955AB /* Tier.swift in Sources */, - BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */, BF56D2AC23DF8E170006506D /* FetchAppIDsOperation.swift in Sources */, - BF26A0E12370C5D400F53F9F /* ALTSourceUserInfoKey.m in Sources */, BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */, - BF54E8212315EF0D000AE0D8 /* ALTPatreonBenefitType.m in Sources */, - BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */, BFE6073A231ADF82002B0E8E /* SettingsViewController.swift in Sources */, + BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */, BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */, - BFBBE2DF22931F73002097FA /* StoreApp.swift in Sources */, BFB6B21E231870160022A802 /* NewsViewController.swift in Sources */, BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */, BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */, BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */, BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */, - BFD5D6F4230DDB0A007955AB /* Campaign.swift in Sources */, - BFB6B21B23186D640022A802 /* NewsItem.swift in Sources */, + BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */, BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */, - BFA8172D23C5823E001B5953 /* InstalledExtension.swift in Sources */, - BFD5D6E8230CC961007955AB /* PatreonAPI.swift in Sources */, - BF6C33652419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel in Sources */, BF9ABA4522DCFF43008935CF /* BrowseViewController.swift in Sources */, - BF43002E22A714AF0051E2BC /* Keychain.swift in Sources */, - BF9F8D1A242AA6BC0024E48B /* AltStore5ToAltStore6.xcmappingmodel in Sources */, BF770E5422BC044E002A40FE /* OperationContexts.swift in Sources */, BFD2478C2284C4C300981D42 /* AppIconImageView.swift in Sources */, - BFE338DD22F0E7F3002E24B9 /* Source.swift in Sources */, BF8F69C422E662D300049BA1 /* AppViewController.swift in Sources */, BFF0B68E23219520007A79E1 /* PatreonViewController.swift in Sources */, - BFD5D6EA230CCAE5007955AB /* PatreonAccount.swift in Sources */, - BFE6326822A858F300F30809 /* Account.swift in Sources */, - BF6C336224197D700034FD24 /* NSError+LocalizedFailure.swift in Sources */, - BFE6326622A857C200F30809 /* Team.swift in Sources */, + BFF00D302501BD7D00746320 /* Intents.intentdefinition in Sources */, + BF6C336224197D700034FD24 /* NSError+AltStore.swift in Sources */, BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */, BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */, + BFE00A202503097F00EB4D0C /* INInteraction+AltStore.swift in Sources */, BFDB6A0B22AAEDB7007EA6D6 /* Operation.swift in Sources */, BF770E6722BD57C4002A40FE /* BackgroundTaskManager.swift in Sources */, - BF100C54232D7DAE006A8926 /* StoreAppPolicy.swift in Sources */, - BF100C50232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel in Sources */, + BF44EEFC246B4550002A52F2 /* RemoveAppOperation.swift in Sources */, BF3D64B022E8D4B800E9056B /* AppContentViewControllerCells.swift in Sources */, BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */, + BF88F97224F8727D00BB75DF /* AppManagerErrors.swift in Sources */, BF6C8FAE2429597900125131 /* BannerCollectionViewCell.swift in Sources */, - BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */, - BF56D2AA23DF88310006506D /* AppID.swift in Sources */, - BF02419422F2156E00129732 /* RefreshAttempt.swift in Sources */, BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */, BFE60742231B07E6002B0E8E /* SettingsHeaderFooterView.swift in Sources */, BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */, BFA8172B23C5633D001B5953 /* FetchAnisetteDataOperation.swift in Sources */, - BFB1169B2293274D00BB457C /* JSONDecoder+Properties.swift in Sources */, BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */, - BF3D648822E79A3700E9056B /* AppPermission.swift in Sources */, BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */, BF41B808233433C100C593A3 /* LoadingState.swift in Sources */, BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */, - BFD5D6EE230D8A86007955AB /* Patron.swift in Sources */, BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */, - BF7C627423DBB78C00515A2D /* InstalledAppPolicy.swift in Sources */, BF08858522DE7EC800DE9F1E /* UpdateCollectionViewCell.swift in Sources */, - BF258CE322EBAE2800023032 /* AppProtocol.swift in Sources */, + BFB39B5C252BC10E00D1BE50 /* Managed.swift in Sources */, BF770E5822BC3D0F002A40FE /* RefreshGroup.swift in Sources */, BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */, BF3D649F22E7B24C00E9056B /* CollapsingTextView.swift in Sources */, - BF0F5FC723F394AD0080DB64 /* AltStore3ToAltStore4.xcmappingmodel in Sources */, BF02419622F2199300129732 /* RefreshAttemptsViewController.swift in Sources */, BF08858322DE795100DE9F1E /* MyAppsViewController.swift in Sources */, BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */, - BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */, BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */, BFD52BD422A0800A000B7ED1 /* ServerManager.swift in Sources */, + BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */, BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */, - BF3D64A222E8031100E9056B /* MergePolicy.swift in Sources */, - BF7C627223DBB3B400515A2D /* AltStore2ToAltStore3.xcmappingmodel in Sources */, BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */, BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */, BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */, + BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */, BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */, BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */, - BF3D648D22E79AC800E9056B /* ALTAppPermission.m in Sources */, - BFD5D6F2230DD974007955AB /* Benefit.swift in Sources */, + BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */, + BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */, BFF0B6942321CB85007A79E1 /* AuthenticationViewController.swift in Sources */, + BF3432FB246B894F0052F4A1 /* BackupAppOperation.swift in Sources */, BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */, BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */, BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */, + BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */, BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */, BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */, BFB6B220231870B00022A802 /* NewsCollectionViewCell.swift in Sources */, - BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */, BFB3645A2325985F00CD0EB1 /* FindServerOperation.swift in Sources */, - BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */, - BFDB5B1622EE90D300F74113 /* Date+RelativeDate.swift in Sources */, BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */, BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */, + BFBE0004250ACFFB0080826E /* ViewApp.intentdefinition in Sources */, BF770E5622BC3C03002A40FE /* Server.swift in Sources */, BFA8172923C56042001B5953 /* ServerConnection.swift in Sources */, BF56D2AF23DF9E310006506D /* AppIDsViewController.swift in Sources */, - BF43003022A71C960051E2BC /* UserDefaults+AltStore.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - BF1E315C22A0621900370A3C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BF1E314F22A0616100370A3C /* AltKit */; - targetProxy = BF1E315B22A0621900370A3C /* PBXContainerItemProxy */; - }; - BF1E315E22A0621F00370A3C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BF1E314F22A0616100370A3C /* AltKit */; - targetProxy = BF1E315D22A0621F00370A3C /* PBXContainerItemProxy */; - }; BF4588452298D48B00BD7491 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BF45872A2298D31600BD7491 /* libimobiledevice */; targetProxy = BF4588442298D48B00BD7491 /* PBXContainerItemProxy */; }; + BF66EE842501AE50007EE018 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BF66EE7D2501AE50007EE018 /* AltStoreCore */; + targetProxy = BF66EE832501AE50007EE018 /* PBXContainerItemProxy */; + }; + BF989176250AABF4002ACF50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BF989166250AABF3002ACF50 /* AltWidgetExtension */; + targetProxy = BF989175250AABF4002ACF50 /* PBXContainerItemProxy */; + }; BFBFFB272380C72F00993A4A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BF5C5FC4237DF5AE00EDD0C6 /* AltPlugin */; targetProxy = BFBFFB262380C72F00993A4A /* PBXContainerItemProxy */; }; + BFF615AB2510042B00484D3B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BF66EE7D2501AE50007EE018 /* AltStoreCore */; + targetProxy = BFF615AA2510042B00484D3B /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1873,6 +2471,14 @@ name = Main.storyboard; sourceTree = ""; }; + BF580488246A28F9008AE704 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BF580489246A28F9008AE704 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; BFD247732284B9A500981D42 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -1892,35 +2498,86 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - BF1E315522A0616100370A3C /* Debug */ = { + BF18BFEC24857D7900DD5981 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; + CLANG_MODULES_AUTOLINK = NO; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = AltDaemon/AltDaemon.entitlements; CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 6XVY5G3U44; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MACOSX_DEPLOYMENT_TARGET = 10.14; - OTHER_LDFLAGS = "-ObjC"; + DYLIB_COMPATIBILITY_VERSION = ""; + DYLIB_CURRENT_VERSION = ""; + EXECUTABLE_EXTENSION = ""; + EXECUTABLE_SUFFIX = ""; + INSTALL_PATH = /usr/bin; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LD_DYLIB_INSTALL_NAME = /usr/bin/AltDaemon; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Roxas\"", + "\"$(SRCROOT)/Dependencies/AltSign/Dependencies/OpenSSL/ios/lib\"", + ); + MACH_O_TYPE = mh_execute; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "-lcrypto", + "-lssl", + ); PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "AltStore/AltStore-Bridging-Header.h"; + SKIP_INSTALL = NO; + SWIFT_OBJC_BRIDGING_HEADER = "AltDaemon/AltDaemon-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - BF1E315622A0616100370A3C /* Release */ = { + BF18BFED24857D7900DD5981 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; + CLANG_MODULES_AUTOLINK = NO; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = AltDaemon/AltDaemon.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 6XVY5G3U44; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MACOSX_DEPLOYMENT_TARGET = 10.14; - OTHER_LDFLAGS = "-ObjC"; + DYLIB_COMPATIBILITY_VERSION = ""; + DYLIB_CURRENT_VERSION = ""; + EXECUTABLE_EXTENSION = ""; + EXECUTABLE_SUFFIX = ""; + GCC_OPTIMIZATION_LEVEL = z; + INSTALL_PATH = /usr/bin; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LD_DYLIB_INSTALL_NAME = /usr/bin/AltDaemon; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Roxas\"", + "\"$(SRCROOT)/Dependencies/AltSign/Dependencies/OpenSSL/ios/lib\"", + ); + MACH_O_TYPE = mh_execute; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "-lcrypto", + "-lssl", + ); PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "AltStore/AltStore-Bridging-Header.h"; + SKIP_INSTALL = NO; + SWIFT_OBJC_BRIDGING_HEADER = "AltDaemon/AltDaemon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1936,7 +2593,7 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 40; DEVELOPMENT_TEAM = 6XVY5G3U44; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -1969,7 +2626,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14.4; - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4.1; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltServer; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -1989,7 +2646,7 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 40; DEVELOPMENT_TEAM = 6XVY5G3U44; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -2022,7 +2679,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14.4; - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4.1; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltServer; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -2105,6 +2762,47 @@ }; name = Release; }; + BF58048C246A28F9008AE704 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = AltBackup/AltBackup.entitlements; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 6XVY5G3U44; + INFOPLIST_FILE = AltBackup/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltBackup; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BF58048D246A28F9008AE704 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = AltBackup/AltBackup.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 6XVY5G3U44; + INFOPLIST_FILE = AltBackup/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltBackup; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; BF5C5FC9237DF5AE00EDD0C6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2124,6 +2822,7 @@ "/Users/Riley/Library/Developer/Xcode/DerivedData/AltStore-bhqnkrahfutztzeudtxhcxccgtlo/Build/Products/Debug", ); MACOSX_DEPLOYMENT_TARGET = 10.14; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltPlugin; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -2153,6 +2852,7 @@ "/Users/Riley/Library/Developer/Xcode/DerivedData/AltStore-bhqnkrahfutztzeudtxhcxccgtlo/Build/Products/Debug", ); MACOSX_DEPLOYMENT_TARGET = 10.14; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltPlugin; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -2162,6 +2862,139 @@ }; name = Release; }; + BF66EE872501AE50007EE018 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B39BC452F0753C2C33A2D43B /* Pods-AltStoreCore.debug.xcconfig */; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 6XVY5G3U44; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = AltStoreCore/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStoreCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + BF66EE882501AE50007EE018 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC373DB2C2B6CB739CCBFB5F /* Pods-AltStoreCore.release.xcconfig */; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 6XVY5G3U44; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = AltStoreCore/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStoreCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + BF989179250AABF4002ACF50 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 6XVY5G3U44; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/iossimulator", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/ios", + ); + INFOPLIST_FILE = AltWidget/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore.AltWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BF98917A250AABF4002ACF50 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 6XVY5G3U44; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/iossimulator", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/ios", + ); + INFOPLIST_FILE = AltWidget/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore.AltWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; BFD2477C2284B9A700981D42 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2217,10 +3050,15 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-Wno-module-import-in-extern-c", + ); SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG BETA"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SYSTEM_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Dependencies/AltSign/Dependencies\""; }; name = Debug; }; @@ -2272,10 +3110,15 @@ IPHONEOS_DEPLOYMENT_TARGET = 12.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-Wno-module-import-in-extern-c", + ); SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SYSTEM_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Dependencies/AltSign/Dependencies\""; VALIDATE_PRODUCT = YES; }; name = Release; @@ -2284,6 +3127,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = EA79A60285C6AF5848AA16E9 /* Pods-AltStore.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AltStore/AltStore.entitlements; @@ -2296,11 +3140,10 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.1; + MARKETING_VERSION = 1.4.2; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG BETA"; SWIFT_OBJC_BRIDGING_HEADER = "AltStore/AltStore-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -2312,6 +3155,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = A136EE677716B80768E9F0A2 /* Pods-AltStore.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AltStore/AltStore.entitlements; @@ -2324,11 +3168,10 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.1; + MARKETING_VERSION = 1.4.2; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = BETA; SWIFT_OBJC_BRIDGING_HEADER = "AltStore/AltStore-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; @@ -2338,11 +3181,11 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - BF1E315422A0616100370A3C /* Build configuration list for PBXNativeTarget "AltKit" */ = { + BF18BFEB24857D7900DD5981 /* Build configuration list for PBXNativeTarget "AltDaemon" */ = { isa = XCConfigurationList; buildConfigurations = ( - BF1E315522A0616100370A3C /* Debug */, - BF1E315622A0616100370A3C /* Release */, + BF18BFEC24857D7900DD5981 /* Debug */, + BF18BFED24857D7900DD5981 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2365,6 +3208,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + BF58048E246A28F9008AE704 /* Build configuration list for PBXNativeTarget "AltBackup" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BF58048C246A28F9008AE704 /* Debug */, + BF58048D246A28F9008AE704 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; BF5C5FC8237DF5AE00EDD0C6 /* Build configuration list for PBXNativeTarget "AltPlugin" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2374,6 +3226,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + BF66EE892501AE50007EE018 /* Build configuration list for PBXNativeTarget "AltStoreCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BF66EE872501AE50007EE018 /* Debug */, + BF66EE882501AE50007EE018 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "AltWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BF989179250AABF4002ACF50 /* Debug */, + BF98917A250AABF4002ACF50 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; BFD247652284B9A500981D42 /* Build configuration list for PBXProject "AltStore" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2394,18 +3264,36 @@ }; /* End XCConfigurationList section */ +/* Begin XCSwiftPackageProductDependency section */ + BF088D0E25019ABA008082D9 /* AltSign-Static */ = { + isa = XCSwiftPackageProductDependency; + productName = "AltSign-Static"; + }; + BF088D352501A821008082D9 /* AltSign-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + productName = "AltSign-Dynamic"; + }; + BF42345B251024B0006D1EB2 /* AltSign-Static */ = { + isa = XCSwiftPackageProductDependency; + productName = "AltSign-Static"; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ - BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */ = { + BF66EEB72501AECA007EE018 /* AltStore.xcdatamodeld */ = { isa = XCVersionGroup; children = ( - BF9F8D18242AA6680024E48B /* AltStore 6.xcdatamodel */, - BF6C33632419ADEB0034FD24 /* AltStore 5.xcdatamodel */, - BF56D2A823DF87570006506D /* AltStore 4.xcdatamodel */, - BF7C627023DBB33300515A2D /* AltStore 3.xcdatamodel */, - BF100C46232D7828006A8926 /* AltStore 2.xcdatamodel */, - BFBBE2DC22931B20002097FA /* AltStore.xcdatamodel */, + BFBF33142526754700B7B8C9 /* AltStore 9.xcdatamodel */, + BFF7EC4C25081E9300BDE521 /* AltStore 8.xcdatamodel */, + BF66EEB82501AECA007EE018 /* AltStore 3.xcdatamodel */, + BF66EEB92501AECA007EE018 /* AltStore.xcdatamodel */, + BF66EEBA2501AECA007EE018 /* AltStore 6.xcdatamodel */, + BF66EEBB2501AECA007EE018 /* AltStore 5.xcdatamodel */, + BF66EEBC2501AECA007EE018 /* AltStore 7.xcdatamodel */, + BF66EEBD2501AECA007EE018 /* AltStore 2.xcdatamodel */, + BF66EEBE2501AECA007EE018 /* AltStore 4.xcdatamodel */, ); - currentVersion = BF9F8D18242AA6680024E48B /* AltStore 6.xcdatamodel */; + currentVersion = BFBF33142526754700B7B8C9 /* AltStore 9.xcdatamodel */; path = AltStore.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/AltStore.xcodeproj/xcshareddata/xcschemes/AltDaemon.xcscheme b/AltStore.xcodeproj/xcshareddata/xcschemes/AltDaemon.xcscheme new file mode 100644 index 00000000..c65d4dbf --- /dev/null +++ b/AltStore.xcodeproj/xcshareddata/xcschemes/AltDaemon.xcscheme @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AltStore.xcodeproj/xcshareddata/xcschemes/AltStore.xcscheme b/AltStore.xcodeproj/xcshareddata/xcschemes/AltStore.xcscheme index db972061..cdc3f471 100644 --- a/AltStore.xcodeproj/xcshareddata/xcschemes/AltStore.xcscheme +++ b/AltStore.xcodeproj/xcshareddata/xcschemes/AltStore.xcscheme @@ -29,17 +29,6 @@ shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - - - + location = "group:Dependencies/AltSign"> diff --git a/AltStore/AltStore-Bridging-Header.h b/AltStore/AltStore-Bridging-Header.h index d63d8c80..c8dd6ac5 100644 --- a/AltStore/AltStore-Bridging-Header.h +++ b/AltStore/AltStore-Bridging-Header.h @@ -2,10 +2,4 @@ // Use this file to import your target's public headers that you would like to expose to Swift. // -#import "AltKit.h" - -#import "ALTAppPermission.h" -#import "ALTPatreonBenefitType.h" -#import "ALTSourceUserInfoKey.h" - #import "NSAttributedString+Markdown.h" diff --git a/AltStore/AltStore.entitlements b/AltStore/AltStore.entitlements index 903def2a..9f0dbc01 100644 --- a/AltStore/AltStore.entitlements +++ b/AltStore/AltStore.entitlements @@ -4,5 +4,11 @@ aps-environment development + com.apple.developer.siri + + com.apple.security.application-groups + + group.com.rileytestut.AltStore + diff --git a/AltStore/Analytics/AnalyticsManager.swift b/AltStore/Analytics/AnalyticsManager.swift index ccbc08c9..c3d7575b 100644 --- a/AltStore/Analytics/AnalyticsManager.swift +++ b/AltStore/Analytics/AnalyticsManager.swift @@ -8,6 +8,8 @@ import Foundation +import AltStoreCore + import AppCenter import AppCenterAnalytics import AppCenterCrashes diff --git a/AltStore/App Detail/AppContentViewController.swift b/AltStore/App Detail/AppContentViewController.swift index fcdb9bfd..8783f429 100644 --- a/AltStore/App Detail/AppContentViewController.swift +++ b/AltStore/App Detail/AppContentViewController.swift @@ -8,6 +8,7 @@ import UIKit +import AltStoreCore import Roxas import Nuke diff --git a/AltStore/App Detail/AppViewController.swift b/AltStore/App Detail/AppViewController.swift index c9c04f99..67537d2d 100644 --- a/AltStore/App Detail/AppViewController.swift +++ b/AltStore/App Detail/AppViewController.swift @@ -8,6 +8,7 @@ import UIKit +import AltStoreCore import Roxas import Nuke @@ -81,14 +82,14 @@ class AppViewController: UIViewController self.bannerView.frame = CGRect(x: 0, y: 0, width: 300, height: 93) self.bannerView.backgroundEffectView.effect = UIBlurEffect(style: .regular) self.bannerView.backgroundEffectView.backgroundColor = .clear - self.bannerView.titleLabel.text = self.app.name - self.bannerView.subtitleLabel.text = self.app.developerName self.bannerView.iconImageView.image = nil self.bannerView.iconImageView.tintColor = self.app.tintColor self.bannerView.button.tintColor = self.app.tintColor - self.bannerView.betaBadgeView.isHidden = !self.app.isBeta self.bannerView.tintColor = self.app.tintColor + self.bannerView.configure(for: self.app) + self.bannerView.accessibilityTraits.remove(.button) + self.bannerView.button.addTarget(self, action: #selector(AppViewController.performAppAction(_:)), for: .primaryActionTriggered) self.backButtonContainerView.tintColor = self.app.tintColor diff --git a/AltStore/App Detail/PermissionPopoverViewController.swift b/AltStore/App Detail/PermissionPopoverViewController.swift index b34e2c68..7c3b1f1d 100644 --- a/AltStore/App Detail/PermissionPopoverViewController.swift +++ b/AltStore/App Detail/PermissionPopoverViewController.swift @@ -8,6 +8,8 @@ import UIKit +import AltStoreCore + class PermissionPopoverViewController: UIViewController { var permission: AppPermission! diff --git a/AltStore/App IDs/AppIDsViewController.swift b/AltStore/App IDs/AppIDsViewController.swift index 49d012e8..8a4d39f6 100644 --- a/AltStore/App IDs/AppIDsViewController.swift +++ b/AltStore/App IDs/AppIDsViewController.swift @@ -8,6 +8,7 @@ import UIKit +import AltStoreCore import Roxas class AppIDsViewController: UICollectionViewController @@ -78,10 +79,11 @@ private extension AppIDsViewController cell.bannerView.iconImageView.isHidden = true cell.bannerView.button.isIndicatingActivity = false - cell.bannerView.betaBadgeView.isHidden = true cell.bannerView.buttonLabel.text = NSLocalizedString("Expires in", comment: "") + let attributedAccessibilityLabel = NSMutableAttributedString(string: appID.name + ". ") + if let expirationDate = appID.expirationDate { cell.bannerView.button.isHidden = false @@ -92,19 +94,16 @@ private extension AppIDsViewController let currentDate = Date() let numberOfDays = expirationDate.numberOfCalendarDays(since: currentDate) + let numberOfDaysText = (numberOfDays == 1) ? NSLocalizedString("1 day", comment: "") : String(format: NSLocalizedString("%@ days", comment: ""), NSNumber(value: numberOfDays)) + cell.bannerView.button.setTitle(numberOfDaysText.uppercased(), for: .normal) - if numberOfDays == 1 - { - cell.bannerView.button.setTitle(NSLocalizedString("1 DAY", comment: ""), for: .normal) - } - else - { - cell.bannerView.button.setTitle(String(format: NSLocalizedString("%@ DAYS", comment: ""), NSNumber(value: numberOfDays)), for: .normal) - } + attributedAccessibilityLabel.mutableString.append(String(format: NSLocalizedString("Expires in %@.", comment: ""), numberOfDaysText) + " ") } else { cell.bannerView.button.isHidden = true + cell.bannerView.button.isUserInteractionEnabled = true + cell.bannerView.buttonLabel.isHidden = true } @@ -112,6 +111,18 @@ private extension AppIDsViewController cell.bannerView.subtitleLabel.text = appID.bundleIdentifier cell.bannerView.subtitleLabel.numberOfLines = 2 + let attributedBundleIdentifier = NSMutableAttributedString(string: appID.bundleIdentifier.lowercased(), attributes: [.accessibilitySpeechPunctuation: true]) + + if let team = appID.team, let range = attributedBundleIdentifier.string.range(of: team.identifier.lowercased()), #available(iOS 13, *) + { + // Prefer to speak the team ID one character at a time. + let nsRange = NSRange(range, in: attributedBundleIdentifier.string) + attributedBundleIdentifier.addAttributes([.accessibilitySpeechSpellOut: true], range: nsRange) + } + + attributedAccessibilityLabel.append(attributedBundleIdentifier) + cell.bannerView.accessibilityAttributedLabel = attributedAccessibilityLabel + // Make sure refresh button is correct size. cell.layoutIfNeeded() } diff --git a/AltStore/AppDelegate.swift b/AltStore/AppDelegate.swift index 65106524..49be4e2a 100644 --- a/AltStore/AppDelegate.swift +++ b/AltStore/AppDelegate.swift @@ -9,53 +9,23 @@ import UIKit import UserNotifications import AVFoundation +import Intents +import AltStoreCore import AltSign -import AltKit import Roxas -private enum RefreshError: LocalizedError -{ - case noInstalledApps - - var errorDescription: String? { - switch self - { - case .noInstalledApps: return NSLocalizedString("No active apps require refreshing.", comment: "") - } - } -} - -private extension CFNotificationName -{ - static let requestAppState = CFNotificationName("com.altstore.RequestAppState" as CFString) - static let appIsRunning = CFNotificationName("com.altstore.AppState.Running" as CFString) - - static func requestAppState(for appID: String) -> CFNotificationName - { - let name = String(CFNotificationName.requestAppState.rawValue) + "." + appID - return CFNotificationName(name as CFString) - } - - static func appIsRunning(for appID: String) -> CFNotificationName - { - let name = String(CFNotificationName.appIsRunning.rawValue) + "." + appID - return CFNotificationName(name as CFString) - } -} - -private let ReceivedApplicationState: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void = -{ (center, observer, name, object, userInfo) in - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let name = name else { return } - appDelegate.receivedApplicationState(notification: name) -} - extension AppDelegate { static let openPatreonSettingsDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.OpenPatreonSettingsDeepLinkNotification") static let importAppDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.ImportAppDeepLinkNotification") + static let addSourceDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.AddSourceDeepLinkNotification") + + static let appBackupDidFinish = Notification.Name("com.rileytestut.AltStore.AppBackupDidFinish") static let importAppDeepLinkURLKey = "fileURL" + static let appBackupResultKey = "result" + static let addSourceDeepLinkURLKey = "sourceURL" } @UIApplicationMain @@ -63,18 +33,35 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - private var runningApplications: Set? - private var backgroundRefreshContext: NSManagedObjectContext? // Keep context alive until finished refreshing. + @available(iOS 14, *) + private lazy var intentHandler = IntentHandler() + + @available(iOS 14, *) + private lazy var viewAppIntentHandler = ViewAppIntentHandler() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Register default settings before doing anything else. + UserDefaults.registerDefaults() + + DatabaseManager.shared.start { (error) in + if let error = error + { + print("Failed to start DatabaseManager. Error:", error as Any) + } + else + { + print("Started DatabaseManager.") + } + } + AnalyticsManager.shared.start() self.setTintColor() ServerManager.shared.startDiscovering() - UserDefaults.standard.registerDefaults() + SecureValueTransformer.register() if UserDefaults.standard.firstLaunch == nil { @@ -110,6 +97,36 @@ class AppDelegate: UIResponder, UIApplicationDelegate { { return self.open(url) } + + func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any? + { + guard #available(iOS 14, *) else { return nil } + + switch intent + { + case is RefreshAllIntent: return self.intentHandler + case is ViewAppIntent: return self.viewAppIntentHandler + default: return nil + } + } +} + +@available(iOS 13, *) +extension AppDelegate +{ + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration + { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) + { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } } private extension AppDelegate @@ -134,13 +151,63 @@ private extension AppDelegate else { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false } - guard let host = components.host, host.lowercased() == "patreon" else { return false } + guard let host = components.host?.lowercased() else { return false } - DispatchQueue.main.async { - NotificationCenter.default.post(name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil) + switch host + { + case "patreon": + DispatchQueue.main.async { + NotificationCenter.default.post(name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil) + } + + return true + + case "appbackupresponse": + let result: Result + + switch url.path.lowercased() + { + case "/success": result = .success(()) + case "/failure": + let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name] = $1.value } ?? [:] + guard + let errorDomain = queryItems["errorDomain"], + let errorCodeString = queryItems["errorCode"], let errorCode = Int(errorCodeString), + let errorDescription = queryItems["errorDescription"] + else { return false } + + let error = NSError(domain: errorDomain, code: errorCode, userInfo: [NSLocalizedDescriptionKey: errorDescription]) + result = .failure(error) + + default: return false + } + + NotificationCenter.default.post(name: AppDelegate.appBackupDidFinish, object: nil, userInfo: [AppDelegate.appBackupResultKey: result]) + + return true + + case "install": + let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:] + guard let downloadURLString = queryItems["url"], let downloadURL = URL(string: downloadURLString) else { return false } + + DispatchQueue.main.async { + NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: downloadURL]) + } + + return true + + case "source": + let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:] + guard let sourceURLString = queryItems["url"], let sourceURL = URL(string: sourceURLString) else { return false } + + DispatchQueue.main.async { + NotificationCenter.default.post(name: AppDelegate.addSourceDeepLinkNotification, object: nil, userInfo: [AppDelegate.addSourceDeepLinkURLKey: sourceURL]) + } + + return true + + default: return false } - - return true } } } @@ -177,95 +244,92 @@ extension AppDelegate func application(_ application: UIApplication, performFetchWithCompletionHandler backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - if UserDefaults.standard.isBackgroundRefreshEnabled + if UserDefaults.standard.isBackgroundRefreshEnabled && !UserDefaults.standard.presentedLaunchReminderNotification { - ServerManager.shared.startDiscovering() + let threeHours: TimeInterval = 3 * 60 * 60 + let trigger = UNTimeIntervalNotificationTrigger(timeInterval: threeHours, repeats: false) - if !UserDefaults.standard.presentedLaunchReminderNotification - { - let threeHours: TimeInterval = 3 * 60 * 60 - let trigger = UNTimeIntervalNotificationTrigger(timeInterval: threeHours, repeats: false) - - let content = UNMutableNotificationContent() - content.title = NSLocalizedString("App Refresh Tip", comment: "") - content.body = NSLocalizedString("The more you open AltStore, the more chances it's given to refresh apps in the background.", comment: "") - - let request = UNNotificationRequest(identifier: "background-refresh-reminder5", content: content, trigger: trigger) - UNUserNotificationCenter.current().add(request) - - UserDefaults.standard.presentedLaunchReminderNotification = true - } + let content = UNMutableNotificationContent() + content.title = NSLocalizedString("App Refresh Tip", comment: "") + content.body = NSLocalizedString("The more you open AltStore, the more chances it's given to refresh apps in the background.", comment: "") + + let request = UNNotificationRequest(identifier: "background-refresh-reminder5", content: content, trigger: trigger) + UNUserNotificationCenter.current().add(request) + + UserDefaults.standard.presentedLaunchReminderNotification = true } - let refreshIdentifier = UUID().uuidString - BackgroundTaskManager.shared.performExtendedBackgroundTask { (taskResult, taskCompletionHandler) in - - func finish(_ result: Result<[String: Result], Error>) - { - // If finish is actually called, that means an error occured during installation. - - if UserDefaults.standard.isBackgroundRefreshEnabled - { - ServerManager.shared.stopDiscovering() - self.scheduleFinishedRefreshingNotification(for: result, identifier: refreshIdentifier, delay: 0) - } - - taskCompletionHandler() - - self.backgroundRefreshContext = nil - } - if let error = taskResult.error { print("Error starting extended background task. Aborting.", error) backgroundFetchCompletionHandler(.failed) - finish(.failure(error)) + taskCompletionHandler() return } if !DatabaseManager.shared.isStarted { DatabaseManager.shared.start() { (error) in - if let error = error + if error != nil { backgroundFetchCompletionHandler(.failed) - finish(.failure(error)) + taskCompletionHandler() } else { - self.refreshApps(identifier: refreshIdentifier, backgroundFetchCompletionHandler: backgroundFetchCompletionHandler, completionHandler: finish(_:)) + self.performBackgroundFetch { (backgroundFetchResult) in + backgroundFetchCompletionHandler(backgroundFetchResult) + } refreshAppsCompletionHandler: { (refreshAppsResult) in + taskCompletionHandler() + } } } } else { - self.refreshApps(identifier: refreshIdentifier, backgroundFetchCompletionHandler: backgroundFetchCompletionHandler, completionHandler: finish(_:)) + self.performBackgroundFetch { (backgroundFetchResult) in + backgroundFetchCompletionHandler(backgroundFetchResult) + } refreshAppsCompletionHandler: { (refreshAppsResult) in + taskCompletionHandler() + } } } } + + func performBackgroundFetch(backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void, + refreshAppsCompletionHandler: @escaping (Result<[String: Result], Error>) -> Void) + { + self.fetchSources { (result) in + switch result + { + case .failure: backgroundFetchCompletionHandler(.failed) + case .success: backgroundFetchCompletionHandler(.newData) + } + + if !UserDefaults.standard.isBackgroundRefreshEnabled + { + refreshAppsCompletionHandler(.success([:])) + } + } + + guard UserDefaults.standard.isBackgroundRefreshEnabled else { return } + + DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in + let installedApps = InstalledApp.fetchAppsForBackgroundRefresh(in: context) + AppManager.shared.backgroundRefresh(installedApps, completionHandler: refreshAppsCompletionHandler) + } + } } private extension AppDelegate { - func refreshApps(identifier: String, - backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void, - completionHandler: @escaping (Result<[String: Result], Error>) -> Void) + func fetchSources(completionHandler: @escaping (Result, Error>) -> Void) { - var fetchSourcesResult: Result, Error>? - var serversResult: Result? - - let dispatchGroup = DispatchGroup() - dispatchGroup.enter() - AppManager.shared.fetchSources() { (result) in - fetchSourcesResult = result - do { - let sources = try result.get() - - guard let context = sources.first?.managedObjectContext else { return } + let (sources, context) = try result.get() let previousUpdatesFetchRequest = InstalledApp.updatesFetchRequest() as! NSFetchRequest previousUpdatesFetchRequest.includesPendingChanges = false @@ -328,223 +392,14 @@ private extension AppDelegate DispatchQueue.main.async { UIApplication.shared.applicationIconBadgeNumber = updates.count } + + completionHandler(.success(sources)) } catch { print("Error fetching apps:", error) - - fetchSourcesResult = .failure(error) - } - - dispatchGroup.leave() - } - - if UserDefaults.standard.isBackgroundRefreshEnabled - { - dispatchGroup.enter() - - DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in - let installedApps = InstalledApp.fetchAppsForBackgroundRefresh(in: context) - guard !installedApps.isEmpty else { - serversResult = .success(()) - dispatchGroup.leave() - - completionHandler(.failure(RefreshError.noInstalledApps)) - - return - } - - self.runningApplications = [] - self.backgroundRefreshContext = context - - let identifiers = installedApps.compactMap { $0.bundleIdentifier } - print("Apps to refresh:", identifiers) - - DispatchQueue.global().async { - let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter() - - for identifier in identifiers - { - let appIsRunningNotification = CFNotificationName.appIsRunning(for: identifier) - CFNotificationCenterAddObserver(notificationCenter, nil, ReceivedApplicationState, appIsRunningNotification.rawValue, nil, .deliverImmediately) - - let requestAppStateNotification = CFNotificationName.requestAppState(for: identifier) - CFNotificationCenterPostNotification(notificationCenter, requestAppStateNotification, nil, nil, true) - } - } - - // Wait for three seconds to: - // a) give us time to discover AltServers - // b) give other processes a chance to respond to requestAppState notification - DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { - context.perform { - if ServerManager.shared.discoveredServers.isEmpty - { - serversResult = .failure(ConnectionError.serverNotFound) - } - else - { - serversResult = .success(()) - } - - dispatchGroup.leave() - - let filteredApps = installedApps.filter { !(self.runningApplications?.contains($0.bundleIdentifier) ?? false) } - print("Filtered Apps to Refresh:", filteredApps.map { $0.bundleIdentifier }) - - let group = AppManager.shared.refresh(filteredApps, presentingViewController: nil) - group.beginInstallationHandler = { (installedApp) in - guard installedApp.bundleIdentifier == StoreApp.altstoreAppID else { return } - - // We're starting to install AltStore, which means the app is about to quit. - // So, we schedule a "refresh successful" local notification to be displayed after a delay, - // but if the app is still running, we cancel the notification. - // Then, we schedule another notification and repeat the process. - - // Also since AltServer has already received the app, it can finish installing even if we're no longer running in background. - - if let error = group.context.error - { - self.scheduleFinishedRefreshingNotification(for: .failure(error), identifier: identifier) - } - else - { - var results = group.results - results[installedApp.bundleIdentifier] = .success(installedApp) - - self.scheduleFinishedRefreshingNotification(for: .success(results), identifier: identifier) - } - } - group.completionHandler = { (results) in - completionHandler(.success(results)) - } - } - } + completionHandler(.failure(error)) } } - - dispatchGroup.notify(queue: .main) { - if !UserDefaults.standard.isBackgroundRefreshEnabled - { - guard let fetchSourcesResult = fetchSourcesResult else { - backgroundFetchCompletionHandler(.failed) - return - } - - switch fetchSourcesResult - { - case .failure: backgroundFetchCompletionHandler(.failed) - case .success: backgroundFetchCompletionHandler(.newData) - } - - completionHandler(.success([:])) - } - else - { - guard let fetchSourcesResult = fetchSourcesResult, let serversResult = serversResult else { - backgroundFetchCompletionHandler(.failed) - return - } - - // Call completionHandler early to improve chances of refreshing in the background again. - switch (fetchSourcesResult, serversResult) - { - case (.success, .success): backgroundFetchCompletionHandler(.newData) - case (.success, .failure(ConnectionError.serverNotFound)): backgroundFetchCompletionHandler(.newData) - case (.failure, _), (_, .failure): backgroundFetchCompletionHandler(.failed) - } - } - } - } - - func receivedApplicationState(notification: CFNotificationName) - { - let baseName = String(CFNotificationName.appIsRunning.rawValue) - - let appID = String(notification.rawValue).replacingOccurrences(of: baseName + ".", with: "") - self.runningApplications?.insert(appID) - } - - func scheduleFinishedRefreshingNotification(for result: Result<[String: Result], Error>, identifier: String, delay: TimeInterval = 5) - { - func scheduleFinishedRefreshingNotification() - { - self.cancelFinishedRefreshingNotification(identifier: identifier) - - let content = UNMutableNotificationContent() - - var shouldPresentAlert = true - - do - { - let results = try result.get() - shouldPresentAlert = !results.isEmpty - - for (_, result) in results - { - guard case let .failure(error) = result else { continue } - throw error - } - - content.title = NSLocalizedString("Refreshed Apps", comment: "") - content.body = NSLocalizedString("All apps have been refreshed.", comment: "") - } - catch ConnectionError.serverNotFound - { - shouldPresentAlert = false - } - catch RefreshError.noInstalledApps - { - shouldPresentAlert = false - } - catch - { - print("Failed to refresh apps in background.", error) - - content.title = NSLocalizedString("Failed to Refresh Apps", comment: "") - content.body = error.localizedDescription - - shouldPresentAlert = true - } - - if shouldPresentAlert - { - let trigger = UNTimeIntervalNotificationTrigger(timeInterval: delay + 1, repeats: false) - - let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger) - UNUserNotificationCenter.current().add(request) - - if delay > 0 - { - DispatchQueue.global().asyncAfter(deadline: .now() + delay) { - UNUserNotificationCenter.current().getPendingNotificationRequests() { (requests) in - // If app is still running at this point, we schedule another notification with same identifier. - // This prevents the currently scheduled notification from displaying, and starts another countdown timer. - // First though, make sure there _is_ still a pending request, otherwise it's been cancelled - // and we should stop polling. - guard requests.contains(where: { $0.identifier == identifier }) else { return } - - scheduleFinishedRefreshingNotification() - } - } - } - } - } - - scheduleFinishedRefreshingNotification() - - // Perform synchronously to ensure app doesn't quit before we've finishing saving to disk. - let context = DatabaseManager.shared.persistentContainer.newBackgroundContext() - context.performAndWait { - _ = RefreshAttempt(identifier: identifier, result: result, context: context) - - do { try context.save() } - catch { print("Failed to save refresh attempt.", error) } - } - } - - func cancelFinishedRefreshingNotification(identifier: String) - { - UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [identifier]) } } diff --git a/AltStore/Authentication/RefreshAltStoreViewController.swift b/AltStore/Authentication/RefreshAltStoreViewController.swift index a14124b0..46dac9a2 100644 --- a/AltStore/Authentication/RefreshAltStoreViewController.swift +++ b/AltStore/Authentication/RefreshAltStoreViewController.swift @@ -7,8 +7,9 @@ // import UIKit -import AltSign +import AltStoreCore +import AltSign import Roxas class RefreshAltStoreViewController: UIViewController diff --git a/AltStore/Base.lproj/Main.storyboard b/AltStore/Base.lproj/Main.storyboard index 79a6a15b..d7d95276 100644 --- a/AltStore/Base.lproj/Main.storyboard +++ b/AltStore/Base.lproj/Main.storyboard @@ -1,11 +1,12 @@ - + - + + @@ -16,8 +17,8 @@ - + @@ -38,6 +39,7 @@ + @@ -71,6 +73,9 @@ + + + @@ -150,7 +155,7 @@ -