From 425425b64cd5ae53810184770c2f01179de30fee 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 de2ec2814c69a871e711bb39a8aa210865c39488 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 0205c3e969ea5386a1069a674a104ebea6f0c8c2 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 2dfb740f779da2e08a2af7272e8cca6aadc5daf7 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 1448616f77ea066c63c9516e6c3c0bb1bb7ea8cf 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 ba5f2b618652b37bcaaf66e3d45f18550d81ae24 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 2309367843ee5e0beafc3c0fb1a4c2a58430aeb6 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 4863a15f357132e6fd294fd2a0add8ead02d8e68 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 57672b9d2f7c2fd274834adbfa7852664a45fc09 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 4d5e5cec70b28d600ece6dc2d7fee7cfc79fc5ee 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": [ {