From 66ef234f02029b50741a36ceb7b6586da3ff3cef Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 11 Nov 2020 16:38:45 -0800 Subject: [PATCH 01/10] [AltServer] Fixes wireless devices not appearing in devices list --- AltServer/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AltServer/AppDelegate.swift b/AltServer/AppDelegate.swift index 773d6305..6fc4c464 100644 --- a/AltServer/AppDelegate.swift +++ b/AltServer/AppDelegate.swift @@ -259,7 +259,7 @@ extension AppDelegate: NSMenuDelegate { guard menu == self.appMenu else { return } - self.connectedDevices = ALTDeviceManager.shared.connectedDevices + self.connectedDevices = ALTDeviceManager.shared.availableDevices self.launchAtLoginMenuItem.target = self self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:)) From bb3b0396721c3830c4bb8710411d533796321633 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 11 Nov 2020 17:25:16 -0800 Subject: [PATCH 02/10] [AltServer] Supports sideloading .ipa files directly to iOS devices --- AltServer/AppDelegate.swift | 48 ++++-- AltServer/Base.lproj/Main.storyboard | 17 ++ .../ALTDeviceManager+Installation.swift | 156 +++++++++++------- AltStore.xcodeproj/project.pbxproj | 6 + .../ALTApplication+AltStoreApp.swift | 19 +++ 5 files changed, 172 insertions(+), 74 deletions(-) create mode 100644 Shared/Extensions/ALTApplication+AltStoreApp.swift diff --git a/AltServer/AppDelegate.swift b/AltServer/AppDelegate.swift index 6fc4c464..2393c730 100644 --- a/AltServer/AppDelegate.swift +++ b/AltServer/AppDelegate.swift @@ -13,6 +13,12 @@ import AltSign import LaunchAtLogin +#if STAGING +private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/altstore-staging/altstore.ipa")! +#else +private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altstore.ipa")! +#endif + @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @@ -26,6 +32,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet private var appMenu: NSMenu! @IBOutlet private var connectedDevicesMenu: NSMenu! + @IBOutlet private var sideloadIPAConnectedDevicesMenu: NSMenu! @IBOutlet private var launchAtLoginMenuItem: NSMenuItem! @IBOutlet private var installMailPluginMenuItem: NSMenuItem! @@ -48,6 +55,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.appMenu.delegate = self self.connectedDevicesMenu.delegate = self + self.sideloadIPAConnectedDevicesMenu.delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in guard success else { return } @@ -81,8 +89,30 @@ private extension AppDelegate { @objc func installAltStore(_ item: NSMenuItem) { - guard case let index = self.connectedDevicesMenu.index(of: item), index != -1 else { return } + guard let index = item.menu?.index(of: item), index != -1 else { return } + let device = self.connectedDevices[index] + self.installApplication(at: altstoreAppURL, to: device) + } + + @objc func sideloadIPA(_ item: NSMenuItem) + { + guard let index = item.menu?.index(of: item), index != -1 else { return } + + let device = self.connectedDevices[index] + + let openPanel = NSOpenPanel() + openPanel.canChooseDirectories = false + openPanel.allowsMultipleSelection = false + openPanel.allowedFileTypes = ["ipa"] + openPanel.begin { (response) in + guard let fileURL = openPanel.url, response == .OK else { return } + self.installApplication(at: fileURL, to: device) + } + } + + func installApplication(at url: URL, to device: ALTDevice) + { let alert = NSAlert() alert.messageText = NSLocalizedString("Please enter your Apple ID and password.", comment: "") alert.informativeText = NSLocalizedString("Your Apple ID and password are not saved and are only sent to Apple for authentication.", comment: "") @@ -125,18 +155,16 @@ private extension AppDelegate let username = appleIDTextField.stringValue let password = passwordTextField.stringValue - - let device = self.connectedDevices[index] - + func install() { - ALTDeviceManager.shared.installAltStore(to: device, appleID: username, password: password) { (result) in + ALTDeviceManager.shared.installApplication(at: url, to: device, appleID: username, password: password) { (result) in switch result { - case .success: + case .success(let application): let content = UNMutableNotificationContent() content.title = NSLocalizedString("Installation Succeeded", comment: "") - content.body = String(format: NSLocalizedString("AltStore was successfully installed on %@.", comment: ""), device.name) + content.body = String(format: NSLocalizedString("%@ was successfully installed on %@.", comment: ""), application.name, device.name) let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) @@ -283,14 +311,14 @@ extension AppDelegate: NSMenuDelegate func numberOfItems(in menu: NSMenu) -> Int { - guard menu == self.connectedDevicesMenu else { return -1 } + guard menu == self.connectedDevicesMenu || menu == self.sideloadIPAConnectedDevicesMenu 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 } + guard menu == self.connectedDevicesMenu || menu == self.sideloadIPAConnectedDevicesMenu else { return false } if self.connectedDevices.isEmpty { @@ -305,7 +333,7 @@ extension AppDelegate: NSMenuDelegate item.title = device.name item.isEnabled = true item.target = self - item.action = #selector(AppDelegate.installAltStore) + item.action = (menu == self.connectedDevicesMenu) ? #selector(AppDelegate.installAltStore(_:)) : #selector(AppDelegate.sideloadIPA(_:)) item.tag = index } diff --git a/AltServer/Base.lproj/Main.storyboard b/AltServer/Base.lproj/Main.storyboard index 33f11093..9d6926ab 100644 --- a/AltServer/Base.lproj/Main.storyboard +++ b/AltServer/Base.lproj/Main.storyboard @@ -64,6 +64,7 @@ + @@ -97,6 +98,22 @@ + + + + + + + + + + + + + + + + diff --git a/AltServer/Devices/ALTDeviceManager+Installation.swift b/AltServer/Devices/ALTDeviceManager+Installation.swift index 63339ac0..9048232d 100644 --- a/AltServer/Devices/ALTDeviceManager+Installation.swift +++ b/AltServer/Devices/ALTDeviceManager+Installation.swift @@ -10,12 +10,6 @@ import Cocoa import UserNotifications import ObjectiveC -#if STAGING -private let appURL = URL(string: "https://f000.backblazeb2.com/file/altstore-staging/altstore.ipa")! -#else -private let appURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altstore.ipa")! -#endif - private let appGroupsLock = NSLock() enum InstallError: LocalizedError @@ -38,21 +32,14 @@ enum InstallError: LocalizedError extension ALTDeviceManager { - func installAltStore(to device: ALTDevice, appleID: String, password: String, completion: @escaping (Result) -> Void) + func installApplication(at url: URL, to device: ALTDevice, appleID: String, password: String, completion: @escaping (Result) -> Void) { let destinationDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) - func finish(_ error: Error?, title: String = "") + func finish(_ result: Result, title: String = "") { DispatchQueue.main.async { - if let error = error - { - completion(.failure(error)) - } - else - { - completion(.success(())) - } + completion(result) } try? FileManager.default.removeItem(at: destinationDirectoryURL) @@ -83,14 +70,13 @@ extension ALTDeviceManager { let certificate = try result.get() - let content = UNMutableNotificationContent() - content.title = String(format: NSLocalizedString("Installing AltStore to %@...", comment: ""), device.name) - content.body = NSLocalizedString("This may take a few seconds.", comment: "") - - let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) - UNUserNotificationCenter.current().add(request) - - self.downloadApp { (result) in + if !url.isFileURL + { + // Show alert before downloading remote .ipa. + self.showInstallationAlert(appName: NSLocalizedString("AltStore", comment: ""), deviceName: device.name) + } + + self.downloadApp(from: url) { (result) in do { let fileURL = try result.get() @@ -98,18 +84,14 @@ extension ALTDeviceManager try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil) let appBundleURL = try FileManager.default.unzipAppBundle(at: fileURL, toDirectory: destinationDirectoryURL) - - do - { - try FileManager.default.removeItem(at: fileURL) - } - catch - { - print("Failed to remove downloaded .ipa.", error) - } - guard let application = ALTApplication(fileURL: appBundleURL) else { throw ALTError(.invalidApp) } + if url.isFileURL + { + // Show alert after "downloading" local .ipa. + self.showInstallationAlert(appName: application.name, deviceName: device.name) + } + // Refresh anisette data to prevent session timeouts. AnisetteDataManager.shared.requestAnisetteData { (result) in do @@ -123,65 +105,73 @@ extension ALTDeviceManager let profiles = try result.get() self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in - finish(result.error, title: "Failed to Install AltStore") + finish(result.map { application }, title: "Failed to Install AltStore") } } catch { - finish(error, title: "Failed to Fetch Provisioning Profiles") + finish(.failure(error), title: "Failed to Fetch Provisioning Profiles") } } } catch { - finish(error, title: "Failed to Refresh Anisette Data") + finish(.failure(error), title: "Failed to Refresh Anisette Data") } } } catch { - finish(error, title: "Failed to Download AltStore") + finish(.failure(error), title: "Failed to Download AltStore") } } } catch { - finish(error, title: "Failed to Fetch Certificate") + finish(.failure(error), title: "Failed to Fetch Certificate") } } } catch { - finish(error, title: "Failed to Register Device") + finish(.failure(error), title: "Failed to Register Device") } } } catch { - finish(error, title: "Failed to Fetch Team") + finish(.failure(error), title: "Failed to Fetch Team") } } } catch { - finish(error, title: "Failed to Authenticate") + finish(.failure(error), title: "Failed to Authenticate") } } } catch { - finish(error, title: "Failed to Fetch Anisette Data") + finish(.failure(error), title: "Failed to Fetch Anisette Data") } } } - - func downloadApp(completionHandler: @escaping (Result) -> Void) +} + +private extension ALTDeviceManager +{ + func downloadApp(from url: URL, completionHandler: @escaping (Result) -> Void) { - let downloadTask = URLSession.shared.downloadTask(with: appURL) { (fileURL, response, error) in + guard !url.isFileURL else { return completionHandler(.success(url)) } + + let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in do { let (fileURL, _) = try Result((fileURL, response), error).get() completionHandler(.success(fileURL)) + + do { try FileManager.default.removeItem(at: fileURL) } + catch { print("Failed to remove downloaded .ipa.", error) } } catch { @@ -259,11 +249,11 @@ extension ALTDeviceManager { DispatchQueue.main.sync { let alert = NSAlert() - alert.messageText = NSLocalizedString("Installing AltStore will revoke your iOS development certificate.", comment: "") + alert.messageText = NSLocalizedString("Installing this app will revoke your iOS development certificate.", comment: "") alert.informativeText = NSLocalizedString(""" This will not affect apps you've submitted to the App Store, but may cause apps you've installed to your devices with Xcode to stop working until you reinstall them. -To prevent this from happening, feel free to try again with another Apple ID to install AltStore. +To prevent this from happening, feel free to try again with another Apple ID. """, comment: "") alert.addButton(withTitle: NSLocalizedString("Continue", comment: "")) @@ -410,7 +400,7 @@ 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 + self.prepareProvisioningProfile(for: application, parentApp: nil, team: team, session: session) { (result) in do { let profile = try result.get() @@ -424,7 +414,7 @@ To prevent this from happening, feel free to try again with another Apple ID to { dispatchGroup.enter() - self.prepareProvisioningProfile(for: appExtension, team: team, session: session) { (result) in + self.prepareProvisioningProfile(for: appExtension, parentApp: application, team: team, session: session) { (result) in switch result { case .failure(let e): error = e @@ -453,9 +443,35 @@ To prevent this from happening, feel free to try again with another Apple ID to } } - func prepareProvisioningProfile(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) + func prepareProvisioningProfile(for application: ALTApplication, parentApp: ALTApplication?, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { - self.registerAppID(name: application.name, identifier: application.bundleIdentifier, team: team, session: session) { (result) in + let parentBundleID = parentApp?.bundleIdentifier ?? application.bundleIdentifier + let updatedParentBundleID: String + + if application.isAltStoreApp + { + // Use legacy bundle ID format for AltStore (and its extensions). + updatedParentBundleID = "com.\(team.identifier).\(parentBundleID)" + } + else + { + updatedParentBundleID = parentBundleID + "." + team.identifier // Append just team identifier to make it harder to track. + } + + let bundleID = application.bundleIdentifier.replacingOccurrences(of: parentBundleID, with: updatedParentBundleID) + + let preferredName: String + + if let parentApp = parentApp + { + preferredName = parentApp.name + " " + application.name + } + else + { + preferredName = application.name + } + + self.registerAppID(name: preferredName, bundleID: bundleID, team: team, session: session) { (result) in do { let appID = try result.get() @@ -493,10 +509,8 @@ To prevent this from happening, feel free to try again with another Apple ID to } } - func registerAppID(name appName: String, identifier: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) + func registerAppID(name appName: String, bundleID: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { - let bundleID = "com.\(team.identifier).\(identifier)" - ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in do { @@ -723,17 +737,21 @@ To prevent this from happening, feel free to try again with another Apple ID to allURLSchemes.append(altstoreURLScheme) var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes] - additionalValues[Bundle.Info.deviceID] = device.identifier - additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID - if - let machineIdentifier = certificate.machineIdentifier, - let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier) + if application.isAltStoreApp { - additionalValues[Bundle.Info.certificateID] = certificate.serialNumber + additionalValues[Bundle.Info.deviceID] = device.identifier + additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID - let certificateURL = application.fileURL.appendingPathComponent("ALTCertificate.p12") - try encryptedData.write(to: certificateURL, options: .atomic) + 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) @@ -750,7 +768,7 @@ To prevent this from happening, feel free to try again with another Apple ID to { try Result(success, error).get() - let activeProfiles: Set? = (team.type == .free) ? Set(profiles.values.map(\.bundleIdentifier)) : nil + let activeProfiles: Set? = (team.type == .free && application.isAltStoreApp) ? Set(profiles.values.map(\.bundleIdentifier)) : nil ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier, activeProvisioningProfiles: activeProfiles) { (success, error) in completionHandler(Result(success, error)) } @@ -769,6 +787,16 @@ To prevent this from happening, feel free to try again with another Apple ID to } } } + + func showInstallationAlert(appName: String, deviceName: String) + { + let content = UNMutableNotificationContent() + content.title = String(format: NSLocalizedString("Installing %@ to %@...", comment: ""), appName, deviceName) + content.body = NSLocalizedString("This may take a few seconds.", comment: "") + + let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) + UNUserNotificationCenter.current().add(request) + } } private var securityCodeAlertKey = 0 diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 4db6af16..95f60421 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -333,6 +333,8 @@ 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 */; }; + BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */; }; + BFF435D9255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.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 */ @@ -750,6 +752,7 @@ 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 = ""; }; + BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTApplication+AltStoreApp.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 = ""; }; @@ -1596,6 +1599,7 @@ BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */, BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */, BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */, + BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */, ); path = Extensions; sourceTree = ""; @@ -2177,6 +2181,7 @@ BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */, BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */, BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */, + BFF435D9255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */, BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */, BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */, BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */, @@ -2418,6 +2423,7 @@ BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */, BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */, BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */, + BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */, BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */, BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */, BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */, diff --git a/Shared/Extensions/ALTApplication+AltStoreApp.swift b/Shared/Extensions/ALTApplication+AltStoreApp.swift new file mode 100644 index 00000000..bd06f010 --- /dev/null +++ b/Shared/Extensions/ALTApplication+AltStoreApp.swift @@ -0,0 +1,19 @@ +// +// ALTApplication+AltStoreApp.swift +// AltStore +// +// Created by Riley Testut on 11/11/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import AltSign + +extension ALTApplication +{ + static let altstoreBundleID = "com.rileytestut.AltStore" + + var isAltStoreApp: Bool { + let isAltStoreApp = self.bundleIdentifier.contains(ALTApplication.altstoreBundleID) + return isAltStoreApp + } +} From 3b45ab7f62a31c3e1de79e860187805cd1315873 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 3 Dec 2020 14:45:34 -0600 Subject: [PATCH 03/10] Updates AltSign dependency --- AltStore/Operations/AuthenticationOperation.swift | 4 ++-- .../Operations/FetchProvisioningProfilesOperation.swift | 8 ++++---- AltStore/Operations/ResignAppOperation.swift | 2 +- AltStoreCore/Model/StoreApp.swift | 3 --- Dependencies/AltSign | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/AltStore/Operations/AuthenticationOperation.swift b/AltStore/Operations/AuthenticationOperation.swift index 9a925262..36876e4a 100644 --- a/AltStore/Operations/AuthenticationOperation.swift +++ b/AltStore/Operations/AuthenticationOperation.swift @@ -582,7 +582,7 @@ private extension AuthenticationOperation return completionHandler(.failure(OperationError.unknownUDID)) } - ALTAppleAPI.shared.fetchDevices(for: team, session: session) { (devices, error) in + ALTAppleAPI.shared.fetchDevices(for: team, types: [.iphone, .ipad], session: session) { (devices, error) in do { let devices = try Result(devices, error).get() @@ -593,7 +593,7 @@ private extension AuthenticationOperation } else { - ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, team: team, session: session) { (device, error) in + ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, type: .iphone, team: team, session: session) { (device, error) in completionHandler(Result(device, error)) } } diff --git a/AltStore/Operations/FetchProvisioningProfilesOperation.swift b/AltStore/Operations/FetchProvisioningProfilesOperation.swift index b48cf9f4..0a836d04 100644 --- a/AltStore/Operations/FetchProvisioningProfilesOperation.swift +++ b/AltStore/Operations/FetchProvisioningProfilesOperation.swift @@ -133,7 +133,7 @@ extension FetchProvisioningProfilesOperation #if DEBUG - if app.bundleIdentifier.hasPrefix(StoreApp.altstoreAppID) || StoreApp.alternativeAltStoreAppIDs.contains(where: app.bundleIdentifier.hasPrefix) + if app.isAltStoreApp { // Use legacy bundle ID format for AltStore. preferredBundleID = "com.\(team.identifier).\(app.bundleIdentifier)" @@ -178,7 +178,7 @@ extension FetchProvisioningProfilesOperation let parentBundleID = parentApp?.bundleIdentifier ?? app.bundleIdentifier let updatedParentBundleID: String - if app.bundleIdentifier.hasPrefix(StoreApp.altstoreAppID) || StoreApp.alternativeAltStoreAppIDs.contains(where: app.bundleIdentifier.hasPrefix) + if app.isAltStoreApp { // Use legacy bundle ID format for AltStore (and its extensions). updatedParentBundleID = "com.\(team.identifier).\(parentBundleID)" @@ -463,7 +463,7 @@ extension FetchProvisioningProfilesOperation func fetchProvisioningProfile(for appID: ALTAppID, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { - ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, team: team, session: session) { (profile, error) in + ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in switch Result(profile, error) { case .failure(let error): completionHandler(.failure(error)) @@ -477,7 +477,7 @@ extension FetchProvisioningProfilesOperation case .success: // Fetch new provisiong profile - ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, team: team, session: session) { (profile, error) in + ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in completionHandler(Result(profile, error)) } } diff --git a/AltStore/Operations/ResignAppOperation.swift b/AltStore/Operations/ResignAppOperation.swift index 64884e0c..38f2e51d 100644 --- a/AltStore/Operations/ResignAppOperation.swift +++ b/AltStore/Operations/ResignAppOperation.swift @@ -169,7 +169,7 @@ private extension ResignAppOperation var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes] - if self.context.bundleIdentifier == StoreApp.altstoreAppID || StoreApp.alternativeAltStoreAppIDs.contains(self.context.bundleIdentifier) + if app.isAltStoreApp { guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { throw OperationError.unknownUDID } additionalValues[Bundle.Info.deviceID] = udid diff --git a/AltStoreCore/Model/StoreApp.swift b/AltStoreCore/Model/StoreApp.swift index 58b05c37..b5797521 100644 --- a/AltStoreCore/Model/StoreApp.swift +++ b/AltStoreCore/Model/StoreApp.swift @@ -16,13 +16,10 @@ public extension StoreApp { #if ALPHA static let altstoreAppID = "com.rileytestut.AltStore.Alpha" - static let alternativeAltStoreAppIDs: Set = ["com.rileytestut.AltStore", "com.rileytestut.AltStore.Beta"] #elseif BETA static let altstoreAppID = "com.rileytestut.AltStore.Beta" - static let alternativeAltStoreAppIDs: Set = ["com.rileytestut.AltStore", "com.rileytestut.AltStore.Alpha"] #else static let altstoreAppID = "com.rileytestut.AltStore" - static let alternativeAltStoreAppIDs: Set = ["com.rileytestut.AltStore.Beta", "com.rileytestut.AltStore.Alpha"] #endif static let dolphinAppID = "me.oatmealdome.dolphinios-njb" diff --git a/Dependencies/AltSign b/Dependencies/AltSign index 2856e479..b027505d 160000 --- a/Dependencies/AltSign +++ b/Dependencies/AltSign @@ -1 +1 @@ -Subproject commit 2856e479c3447b361b98bfc2b5489841f997ebe1 +Subproject commit b027505dcc228aa854272f55bb4fcd31cdf2d96e From 1ce973146571da59921cb56e27fc44ee037ee450 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 11 Nov 2020 17:50:19 -0800 Subject: [PATCH 04/10] [AltServer] Supports sideloading apps to Apple TV --- .../ALTDeviceManager+Installation.swift | 20 +++++----- AltServer/Devices/ALTDeviceManager.mm | 40 ++++++++++++++++++- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/AltServer/Devices/ALTDeviceManager+Installation.swift b/AltServer/Devices/ALTDeviceManager+Installation.swift index 9048232d..b52d7c6d 100644 --- a/AltServer/Devices/ALTDeviceManager+Installation.swift +++ b/AltServer/Devices/ALTDeviceManager+Installation.swift @@ -99,7 +99,7 @@ extension ALTDeviceManager let anisetteData = try result.get() session.anisetteData = anisetteData - self.prepareAllProvisioningProfiles(for: application, team: team, session: session) { (result) in + self.prepareAllProvisioningProfiles(for: application, device: device, team: team, session: session) { (result) in do { let profiles = try result.get() @@ -397,10 +397,10 @@ To prevent this from happening, feel free to try again with another Apple ID. } } - func prepareAllProvisioningProfiles(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, + func prepareAllProvisioningProfiles(for application: ALTApplication, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void) { - self.prepareProvisioningProfile(for: application, parentApp: nil, team: team, session: session) { (result) in + self.prepareProvisioningProfile(for: application, parentApp: nil, device: device, team: team, session: session) { (result) in do { let profile = try result.get() @@ -414,7 +414,7 @@ To prevent this from happening, feel free to try again with another Apple ID. { dispatchGroup.enter() - self.prepareProvisioningProfile(for: appExtension, parentApp: application, team: team, session: session) { (result) in + self.prepareProvisioningProfile(for: appExtension, parentApp: application, device: device, team: team, session: session) { (result) in switch result { case .failure(let e): error = e @@ -443,7 +443,7 @@ To prevent this from happening, feel free to try again with another Apple ID. } } - func prepareProvisioningProfile(for application: ALTApplication, parentApp: ALTApplication?, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) + func prepareProvisioningProfile(for application: ALTApplication, parentApp: ALTApplication?, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { let parentBundleID = parentApp?.bundleIdentifier ?? application.bundleIdentifier let updatedParentBundleID: String @@ -486,7 +486,7 @@ To prevent this from happening, feel free to try again with another Apple ID. { let appID = try result.get() - self.fetchProvisioningProfile(for: appID, team: team, session: session) { (result) in + self.fetchProvisioningProfile(for: appID, device: device, team: team, session: session) { (result) in completionHandler(result) } } @@ -666,7 +666,7 @@ To prevent this from happening, feel free to try again with another Apple ID. func register(_ device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { - ALTAppleAPI.shared.fetchDevices(for: team, session: session) { (devices, error) in + ALTAppleAPI.shared.fetchDevices(for: team, types: device.type, session: session) { (devices, error) in do { let devices = try Result(devices, error).get() @@ -677,7 +677,7 @@ To prevent this from happening, feel free to try again with another Apple ID. } else { - ALTAppleAPI.shared.registerDevice(name: device.name, identifier: device.identifier, team: team, session: session) { (device, error) in + ALTAppleAPI.shared.registerDevice(name: device.name, identifier: device.identifier, type: device.type, team: team, session: session) { (device, error) in completionHandler(Result(device, error)) } } @@ -689,9 +689,9 @@ To prevent this from happening, feel free to try again with another Apple ID. } } - func fetchProvisioningProfile(for appID: ALTAppID, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) + func fetchProvisioningProfile(for appID: ALTAppID, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { - ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, team: team, session: session) { (profile, error) in + ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: device.type, team: team, session: session) { (profile, error) in completionHandler(Result(profile, error)) } } diff --git a/AltServer/Devices/ALTDeviceManager.mm b/AltServer/Devices/ALTDeviceManager.mm index 219cbc46..ce1267e7 100644 --- a/AltServer/Devices/ALTDeviceManager.mm +++ b/AltServer/Devices/ALTDeviceManager.mm @@ -1113,13 +1113,51 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT continue; } + plist_t device_type_plist = NULL; + if (lockdownd_get_value(client, NULL, "ProductType", &device_type_plist) != LOCKDOWN_E_SUCCESS) + { + fprintf(stderr, "ERROR: Could not get device type for %s!\n", device_name); + + lockdownd_client_free(client); + idevice_free(device); + + continue; + } + + ALTDeviceType deviceType = ALTDeviceTypeiPhone; + + char *device_type_string = NULL; + plist_get_string_val(device_type_plist, &device_type_string); + + if ([@(device_type_string) hasPrefix:@"iPhone"]) + { + deviceType = ALTDeviceTypeiPhone; + } + else if ([@(device_type_string) hasPrefix:@"iPad"]) + { + deviceType = ALTDeviceTypeiPad; + } + else if ([@(device_type_string) hasPrefix:@"AppleTV"]) + { + deviceType = ALTDeviceTypeAppleTV; + } + else + { + fprintf(stderr, "ERROR: Unknown device type %s for %s!\n", device_type_string, device_name); + + lockdownd_client_free(client); + idevice_free(device); + + continue; + } + lockdownd_client_free(client); idevice_free(device); NSString *name = [NSString stringWithCString:device_name encoding:NSUTF8StringEncoding]; NSString *identifier = [NSString stringWithCString:udid encoding:NSUTF8StringEncoding]; - ALTDevice *altDevice = [[ALTDevice alloc] initWithName:name identifier:identifier type:ALTDeviceTypeiPhone]; + ALTDevice *altDevice = [[ALTDevice alloc] initWithName:name identifier:identifier type:deviceType]; [connectedDevices addObject:altDevice]; if (device_name != NULL) From 2b53e3483ae942b514dcbb0241234378cd0d03bb Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 30 Nov 2020 16:25:26 -0600 Subject: [PATCH 05/10] Updates apps.json with Delta 1.3b1 --- AltStore/Resources/apps.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/AltStore/Resources/apps.json b/AltStore/Resources/apps.json index 2ce35eb6..5936ae72 100644 --- a/AltStore/Resources/apps.json +++ b/AltStore/Resources/apps.json @@ -92,14 +92,14 @@ "bundleIdentifier": "com.rileytestut.Delta.Beta", "developerName": "Riley Testut", "subtitle": "Classic games in your pocket.", - "version": "1.2b3", - "versionDate": "2020-10-12T12:30:00-07:00", - "versionDescription": "**Delta 1.2 GM**\n\nIMPROVEMENTS\n• Improves sync error messages when a game is missing or can’t be synced\n\nBUG FIXES\n• Fixes misplaced DS screens when using external controller\n• Fixes NES games crashing after playing an N64 game\n• Fixes various issues with controller skin filters\n• Fixes swapped DS screens\n• Fixes importing games from Files\n\nThanks for your help beta testing Delta these past few months! This should be the last beta before the public release of Delta 1.2, so please report any last-minute issues so we can have a smooth release 🙂", - "downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/delta/1_2_b3.ipa", - "localizedDescription": "The next console for Delta is coming: this beta version of Delta brings support for playing DS games!\n\nDS support currently includes:\n• Playing DS games\n• Save States\n• Hold Button\n\nFeatures I'm still working on:\n• Fast Forward\n• Cheats\n• Controller skin (using placeholder controller skin for now)\n\nPlease report any issues you find to support@altstore.io. Thanks!", + "version": "1.3b1", + "versionDate": "2020-11-25T11:00:00-06:00", + "versionDescription": "NEW FEATURES\n• [MelonDS] DSi emulation (requires additional DSi BIOS files)\n• [MelonDS] Just-in-time (JIT) compilation on A12 devices running iOS 14.2 or newer.\n• [MelonDS] Microphone support — use your device's microphone to emulate the DS microphone.\n• [MelonDS] Emulate closing/opening lid — sleep device to \"close\" DS lid, wake to \"open\".\n\nIMPROVEMENTS\n• Dark mode support throughout app (thanks @ericlewis!)", + "downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/delta/1_3_b1.ipa", + "localizedDescription": "The next console for Delta is coming: this beta version of Delta brings support for playing DS games!\n\nPlease report any issues you find to support@altstore.io. Thanks!", "iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png", "tintColor": "8A28F7", - "size": 18456630, + "size": 18556391, "beta": true, "permissions": [ { @@ -274,6 +274,6 @@ } ], "userInfo": { - "patreonAccessToken": "vfuSOfSYTUC8pEq6TM9r6uoL-T-uTJEI85hFescT4AM" + "patreonAccessToken": "dRUfeocZhNq6K3LWfu2poRE9aVkSTcFp2xBviRoOlcY" } } From f8f26bfb4011c9977655e601e396f36b4a3159a2 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 3 Dec 2020 14:52:21 -0600 Subject: [PATCH 06/10] =?UTF-8?q?[AltServer]=20Fixes=20=E2=80=9CRSTPlaceho?= =?UTF-8?q?lderView.nib=20couldn=E2=80=99t=20be=20saved=E2=80=9D=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AltServer/Devices/ALTDeviceManager.mm | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/AltServer/Devices/ALTDeviceManager.mm b/AltServer/Devices/ALTDeviceManager.mm index ce1267e7..80f2a3f9 100644 --- a/AltServer/Devices/ALTDeviceManager.mm +++ b/AltServer/Devices/ALTDeviceManager.mm @@ -297,6 +297,9 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT NSError *writeError = nil; if (![self writeDirectory:appBundleURL toDestinationURL:destinationURL client:afc progress:nil error:&writeError]) { + int removeResult = afc_remove_path_and_contents(afc, stagingURL.relativePath.fileSystemRepresentation); + NSLog(@"Remove staging app result: %@", @(removeResult)); + return finish(writeError); } @@ -430,7 +433,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT else { NSURL *destinationFileURL = [destinationURL URLByAppendingPathComponent:fileURL.lastPathComponent isDirectory:NO]; - if (![self writeFile:fileURL toDestinationURL:destinationFileURL client:afc error:error]) + if (![self writeFile:fileURL toDestinationURL:destinationFileURL progress:progress client:afc error:error]) { return NO; } @@ -442,7 +445,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT return YES; } -- (BOOL)writeFile:(NSURL *)fileURL toDestinationURL:(NSURL *)destinationURL client:(afc_client_t)afc error:(NSError **)error +- (BOOL)writeFile:(NSURL *)fileURL toDestinationURL:(NSURL *)destinationURL progress:(NSProgress *)progress client:(afc_client_t)afc error:(NSError **)error { NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileURL.path]; if (fileHandle == nil) @@ -458,8 +461,16 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT NSData *data = [fileHandle readDataToEndOfFile]; uint64_t af = 0; - if ((afc_file_open(afc, destinationURL.relativePath.fileSystemRepresentation, AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS) || af == 0) + + int openResult = afc_file_open(afc, destinationURL.relativePath.fileSystemRepresentation, AFC_FOPEN_WRONLY, &af); + if (openResult != AFC_E_SUCCESS || af == 0) { + if (openResult == AFC_E_OBJECT_IS_DIR) + { + NSLog(@"Treating file as directory: %@ %@", fileURL, destinationURL); + return [self writeDirectory:fileURL toDestinationURL:destinationURL client:afc progress:progress error:error]; + } + if (error) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}]; @@ -475,10 +486,12 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT { uint32_t count = 0; - if (afc_file_write(afc, af, (const char *)data.bytes + bytesWritten, (uint32_t)data.length - bytesWritten, &count) != AFC_E_SUCCESS) + int writeResult = afc_file_write(afc, af, (const char *)data.bytes + bytesWritten, (uint32_t)data.length - bytesWritten, &count); + if (writeResult != AFC_E_SUCCESS) { if (error) { + NSLog(@"Failed writing file with error: %@ (%@ %@)", @(writeResult), fileURL, destinationURL); *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}]; } @@ -493,6 +506,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT { if (error) { + NSLog(@"Failed writing file due to mismatched sizes: %@ vs %@ (%@ %@)", @(bytesWritten), @(data.length), fileURL, destinationURL); *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}]; } From f7a2c9f9f03a1b8380224fce81ffb4921d695c48 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 3 Dec 2020 15:02:35 -0600 Subject: [PATCH 07/10] [AltServer] Supports multiple devices with same Apple ID AltServer now caches certificates for each Apple ID used to install AltStore, and will re-use them for future installations rather than revoke + create new ones each time (if possible). --- .../ALTDeviceManager+Installation.swift | 158 +++++++++--------- 1 file changed, 80 insertions(+), 78 deletions(-) diff --git a/AltServer/Devices/ALTDeviceManager+Installation.swift b/AltServer/Devices/ALTDeviceManager+Installation.swift index b52d7c6d..74854dc1 100644 --- a/AltServer/Devices/ALTDeviceManager+Installation.swift +++ b/AltServer/Devices/ALTDeviceManager+Installation.swift @@ -234,17 +234,82 @@ private extension ALTDeviceManager func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) { - func finish(_ result: Result) - { - switch result + ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in + do { - case .failure(let error): - completionHandler(.failure(error)) + let teams = try Result(teams, error).get() - case .success(let team): + if let team = teams.first(where: { $0.type == .free }) + { + return completionHandler(.success(team)) + } + else if let team = teams.first(where: { $0.type == .individual }) + { + return completionHandler(.success(team)) + } + else if let team = teams.first + { + return completionHandler(.success(team)) + } + else + { + throw InstallError.noTeam + } + } + catch + { + completionHandler(.failure(error)) + } + } + } + + func fetchCertificate(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) + { + ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in + do + { + let certificates = try Result(certificates, error).get() + + let applicationSupportDirectoryURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] + let altserverDirectoryURL = applicationSupportDirectoryURL.appendingPathComponent("com.rileytestut.AltServer") + let certificatesDirectoryURL = altserverDirectoryURL.appendingPathComponent("Certificates") + + try FileManager.default.createDirectory(at: certificatesDirectoryURL, withIntermediateDirectories: true, attributes: nil) + + let certificateFileURL = certificatesDirectoryURL.appendingPathComponent(team.identifier + ".p12") var isCancelled = false + // Check if there is another AltStore certificate, which means AltStore has been installed with this Apple ID before. + if let previousCertificate = certificates.first(where: { $0.machineName?.starts(with: "AltStore") == true }) + { + if FileManager.default.fileExists(atPath: certificateFileURL.path), + let data = try? Data(contentsOf: certificateFileURL), + let certificate = ALTCertificate(p12Data: data, password: previousCertificate.machineIdentifier) + { + return completionHandler(.success(certificate)) + } + + DispatchQueue.main.sync { + let alert = NSAlert() + alert.messageText = NSLocalizedString("Multiple AltServers Not Supported", comment: "") + alert.informativeText = NSLocalizedString("Please use the same AltServer you previously used with this Apple ID, or else apps installed with other AltServers will stop working.\n\nAre you sure you want to continue?", comment: "") + + alert.addButton(withTitle: NSLocalizedString("Continue", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + + NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) + + let buttonIndex = alert.runModal() + if buttonIndex == NSApplication.ModalResponse.alertSecondButtonReturn + { + isCancelled = true + } + } + + guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) } + } + if team.type != .free { DispatchQueue.main.sync { @@ -268,78 +333,7 @@ To prevent this from happening, feel free to try again with another Apple ID. } } - if isCancelled - { - return completionHandler(.failure(InstallError.cancelled)) - } - } - - completionHandler(.success(team)) - } - } - - ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in - do - { - let teams = try Result(teams, error).get() - - if let team = teams.first(where: { $0.type == .free }) - { - return finish(.success(team)) - } - else if let team = teams.first(where: { $0.type == .individual }) - { - return finish(.success(team)) - } - else if let team = teams.first - { - return finish(.success(team)) - } - else - { - throw InstallError.noTeam - } - } - catch - { - finish(.failure(error)) - } - } - } - - func fetchCertificate(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void) - { - ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in - do - { - let certificates = try Result(certificates, error).get() - - // Check if there is another AltStore certificate, which means AltStore has been installed with this Apple ID before. - if certificates.contains(where: { $0.machineName?.starts(with: "AltStore") == true }) - { - var isCancelled = false - - DispatchQueue.main.sync { - let alert = NSAlert() - alert.messageText = NSLocalizedString("AltStore already installed on another device.", comment: "") - alert.informativeText = NSLocalizedString("Apps installed with AltStore on your other devices will stop working. Are you sure you want to continue?", comment: "") - - alert.addButton(withTitle: NSLocalizedString("Continue", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) - - NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) - - let buttonIndex = alert.runModal() - if buttonIndex == NSApplication.ModalResponse.alertSecondButtonReturn - { - isCancelled = true - } - } - - if isCancelled - { - return completionHandler(.failure(InstallError.cancelled)) - } + guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) } } if let certificate = certificates.first @@ -376,6 +370,14 @@ To prevent this from happening, feel free to try again with another Apple ID. certificate.privateKey = privateKey completionHandler(.success(certificate)) + + if let machineIdentifier = certificate.machineIdentifier, + let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier) + { + // Cache certificate. + do { try encryptedData.write(to: certificateFileURL, options: .atomic) } + catch { print("Failed to cache certificate:", error) } + } } catch { From 915eed3a697dc9fe921b8532ba254c5ef2b7068d Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 3 Dec 2020 15:05:17 -0600 Subject: [PATCH 08/10] [AltServer] Prefers paid developer teams over free teams --- AltServer/Devices/ALTDeviceManager+Installation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AltServer/Devices/ALTDeviceManager+Installation.swift b/AltServer/Devices/ALTDeviceManager+Installation.swift index 74854dc1..4db8697d 100644 --- a/AltServer/Devices/ALTDeviceManager+Installation.swift +++ b/AltServer/Devices/ALTDeviceManager+Installation.swift @@ -239,11 +239,11 @@ private extension ALTDeviceManager { let teams = try Result(teams, error).get() - if let team = teams.first(where: { $0.type == .free }) + if let team = teams.first(where: { $0.type == .individual }) { return completionHandler(.success(team)) } - else if let team = teams.first(where: { $0.type == .individual }) + else if let team = teams.first(where: { $0.type == .free }) { return completionHandler(.success(team)) } From aedb3012a4dc9b0dc0f2d70d6fdb9b6e8f278636 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 3 Dec 2020 15:05:57 -0600 Subject: [PATCH 09/10] Prefers paid developer teams over free teams --- AltStore/Operations/AuthenticationOperation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AltStore/Operations/AuthenticationOperation.swift b/AltStore/Operations/AuthenticationOperation.swift index 36876e4a..d0055950 100644 --- a/AltStore/Operations/AuthenticationOperation.swift +++ b/AltStore/Operations/AuthenticationOperation.swift @@ -432,11 +432,11 @@ private extension AuthenticationOperation { func selectTeam(from teams: [ALTTeam]) { - if let team = teams.first(where: { $0.type == .free }) + if let team = teams.first(where: { $0.type == .individual }) { return completionHandler(.success(team)) } - else if let team = teams.first(where: { $0.type == .individual }) + else if let team = teams.first(where: { $0.type == .free }) { return completionHandler(.success(team)) } From e2ce2b3776b335bfc80ba95b8b3c845da5e1f5a6 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 3 Dec 2020 16:19:07 -0600 Subject: [PATCH 10/10] Updates apps.json and apps-alpha.json --- AltStore/Resources/apps-alpha.json | 8 ++++---- AltStore/Resources/apps.json | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AltStore/Resources/apps-alpha.json b/AltStore/Resources/apps-alpha.json index 9b8bd1b8..edf48a1e 100644 --- a/AltStore/Resources/apps-alpha.json +++ b/AltStore/Resources/apps-alpha.json @@ -36,10 +36,10 @@ "bundleIdentifier": "com.rileytestut.Delta.Alpha", "developerName": "Riley Testut", "subtitle": "Classic games in your pocket.", - "version": "1.3a2", - "versionDate": "2020-10-26T14:00:00-07:00", - "versionDescription": "This alpha fixes incorrect inputs when playing DS games with melonDS core.", - "downloadURL": "https://f000.backblazeb2.com/file/altstore/sources/alpha/delta/1_3_a2.ipa", + "version": "1.3a4", + "versionDate": "2020-11-30T20:00:00-06:00", + "versionDescription": "BUG FIXES\n• Fixes garbled audio during emulation\n• Fixes audio not playing over AirPlay
• Fixes crash when activating Siri\n• Fixes MelonDS crashing on devices that don't support JIT", + "downloadURL": "https://f000.backblazeb2.com/file/altstore/sources/alpha/delta/1_3_a4.ipa", "localizedDescription": "The next console for Delta is coming: this beta version of Delta brings support for playing DS games!\n\nDS support currently includes:\n• Playing DS games\n• Save States\n• Hold Button\n\nFeatures I'm still working on:\n• Fast Forward\n• Cheats\n• Controller skin (using placeholder controller skin for now)\n\nPlease report any issues you find to support@altstore.io. Thanks!", "iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png", "tintColor": "8A28F7", diff --git a/AltStore/Resources/apps.json b/AltStore/Resources/apps.json index 5936ae72..8c351cdf 100644 --- a/AltStore/Resources/apps.json +++ b/AltStore/Resources/apps.json @@ -92,14 +92,14 @@ "bundleIdentifier": "com.rileytestut.Delta.Beta", "developerName": "Riley Testut", "subtitle": "Classic games in your pocket.", - "version": "1.3b1", - "versionDate": "2020-11-25T11:00:00-06:00", - "versionDescription": "NEW FEATURES\n• [MelonDS] DSi emulation (requires additional DSi BIOS files)\n• [MelonDS] Just-in-time (JIT) compilation on A12 devices running iOS 14.2 or newer.\n• [MelonDS] Microphone support — use your device's microphone to emulate the DS microphone.\n• [MelonDS] Emulate closing/opening lid — sleep device to \"close\" DS lid, wake to \"open\".\n\nIMPROVEMENTS\n• Dark mode support throughout app (thanks @ericlewis!)", - "downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/delta/1_3_b1.ipa", + "version": "1.3b2", + "versionDate": "2020-12-02T12:30:00-06:00", + "versionDescription": "BUG FIXES\n• Fixes garbled audio during emulation\n• Fixes MelonDS core crashing on devices that don't support JIT\n• Fixes audio not playing over AirPlay
• Fixes crash when activating Siri\n\nPrevious update:\n\nNEW FEATURES\n• [MelonDS] DSi emulation (requires additional DSi BIOS files)\n• [MelonDS] Just-in-time (JIT) compilation on A12 devices running iOS 14.2 or newer.\n• [MelonDS] Microphone support — use your device's microphone to emulate the DS microphone.\n• [MelonDS] Emulate closing/opening lid — sleep device to \"close\" DS lid, wake to \"open\".\n\nIMPROVEMENTS\n• Dark mode support throughout app (thanks @ericlewis!)", + "downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/delta/1_3_b2.ipa", "localizedDescription": "The next console for Delta is coming: this beta version of Delta brings support for playing DS games!\n\nPlease report any issues you find to support@altstore.io. Thanks!", "iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png", "tintColor": "8A28F7", - "size": 18556391, + "size": 18558635, "beta": true, "permissions": [ {