Merge branch 'develop' of github.com:rileytestut/AltStore into develop

This commit is contained in:
Riley Testut
2020-12-03 16:25:32 -06:00
13 changed files with 338 additions and 189 deletions

View File

@@ -13,6 +13,12 @@ import AltSign
import LaunchAtLogin 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 @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
@@ -26,6 +32,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet private var appMenu: NSMenu! @IBOutlet private var appMenu: NSMenu!
@IBOutlet private var connectedDevicesMenu: NSMenu! @IBOutlet private var connectedDevicesMenu: NSMenu!
@IBOutlet private var sideloadIPAConnectedDevicesMenu: NSMenu!
@IBOutlet private var launchAtLoginMenuItem: NSMenuItem! @IBOutlet private var launchAtLoginMenuItem: NSMenuItem!
@IBOutlet private var installMailPluginMenuItem: NSMenuItem! @IBOutlet private var installMailPluginMenuItem: NSMenuItem!
@@ -48,6 +55,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self.appMenu.delegate = self self.appMenu.delegate = self
self.connectedDevicesMenu.delegate = self self.connectedDevicesMenu.delegate = self
self.sideloadIPAConnectedDevicesMenu.delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
guard success else { return } guard success else { return }
@@ -81,8 +89,30 @@ private extension AppDelegate
{ {
@objc func installAltStore(_ item: NSMenuItem) @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() let alert = NSAlert()
alert.messageText = NSLocalizedString("Please enter your Apple ID and password.", comment: "") 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: "") 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 username = appleIDTextField.stringValue
let password = passwordTextField.stringValue let password = passwordTextField.stringValue
let device = self.connectedDevices[index]
func install() 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 switch result
{ {
case .success: case .success(let application):
let content = UNMutableNotificationContent() let content = UNMutableNotificationContent()
content.title = NSLocalizedString("Installation Succeeded", comment: "") 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) let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request) UNUserNotificationCenter.current().add(request)
@@ -272,7 +300,7 @@ extension AppDelegate: NSMenuDelegate
{ {
guard menu == self.appMenu else { return } guard menu == self.appMenu else { return }
self.connectedDevices = ALTDeviceManager.shared.connectedDevices self.connectedDevices = ALTDeviceManager.shared.availableDevices
self.launchAtLoginMenuItem.target = self self.launchAtLoginMenuItem.target = self
self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:)) self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:))
@@ -296,14 +324,14 @@ extension AppDelegate: NSMenuDelegate
func numberOfItems(in menu: NSMenu) -> Int 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 return self.connectedDevices.isEmpty ? 1 : self.connectedDevices.count
} }
func menu(_ menu: NSMenu, update item: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool 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 if self.connectedDevices.isEmpty
{ {
@@ -318,7 +346,7 @@ extension AppDelegate: NSMenuDelegate
item.title = device.name item.title = device.name
item.isEnabled = true item.isEnabled = true
item.target = self item.target = self
item.action = #selector(AppDelegate.installAltStore) item.action = (menu == self.connectedDevicesMenu) ? #selector(AppDelegate.installAltStore(_:)) : #selector(AppDelegate.sideloadIPA(_:))
item.tag = index item.tag = index
} }

View File

@@ -64,6 +64,7 @@
<outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/> <outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/>
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/> <outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/> <outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
<outlet property="sideloadIPAConnectedDevicesMenu" destination="IuI-bV-fTY" id="QQw-St-HfG"/>
</connections> </connections>
</customObject> </customObject>
<customObject id="Arf-IC-5eb" customClass="SUUpdater"/> <customObject id="Arf-IC-5eb" customClass="SUUpdater"/>
@@ -97,6 +98,22 @@
</connections> </connections>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Sideload .ipa" id="x0e-zI-0A2" userLabel="Install .ipa">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Sideload .ipa" systemMenu="recentDocuments" id="IuI-bV-fTY">
<items>
<menuItem title="No Connected Devices" id="in5-an-MD0">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="aUE-On-axK"/>
</connections>
</menuItem>
</items>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="N3K-su-XV6"/>
</connections>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="1ZZ-BB-xHy"/> <menuItem isSeparatorItem="YES" id="1ZZ-BB-xHy"/>
<menuItem title="Launch at Login" id="IyR-FQ-upe" userLabel="Launch At Login"> <menuItem title="Launch at Login" id="IyR-FQ-upe" userLabel="Launch At Login">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>

View File

@@ -10,12 +10,6 @@ import Cocoa
import UserNotifications import UserNotifications
import ObjectiveC 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() private let appGroupsLock = NSLock()
enum InstallError: LocalizedError enum InstallError: LocalizedError
@@ -38,21 +32,14 @@ enum InstallError: LocalizedError
extension ALTDeviceManager extension ALTDeviceManager
{ {
func installAltStore(to device: ALTDevice, appleID: String, password: String, completion: @escaping (Result<Void, Error>) -> Void) func installApplication(at url: URL, to device: ALTDevice, appleID: String, password: String, completion: @escaping (Result<ALTApplication, Error>) -> Void)
{ {
let destinationDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) let destinationDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
func finish(_ error: Error?, title: String = "") func finish(_ result: Result<ALTApplication, Error>, title: String = "")
{ {
DispatchQueue.main.async { DispatchQueue.main.async {
if let error = error completion(result)
{
completion(.failure(error))
}
else
{
completion(.success(()))
}
} }
try? FileManager.default.removeItem(at: destinationDirectoryURL) try? FileManager.default.removeItem(at: destinationDirectoryURL)
@@ -83,14 +70,13 @@ extension ALTDeviceManager
{ {
let certificate = try result.get() let certificate = try result.get()
let content = UNMutableNotificationContent() if !url.isFileURL
content.title = String(format: NSLocalizedString("Installing AltStore to %@...", comment: ""), device.name) {
content.body = NSLocalizedString("This may take a few seconds.", comment: "") // Show alert before downloading remote .ipa.
self.showInstallationAlert(appName: NSLocalizedString("AltStore", comment: ""), deviceName: device.name)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) }
UNUserNotificationCenter.current().add(request)
self.downloadApp(from: url) { (result) in
self.downloadApp { (result) in
do do
{ {
let fileURL = try result.get() let fileURL = try result.get()
@@ -98,18 +84,14 @@ extension ALTDeviceManager
try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil) try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil)
let appBundleURL = try FileManager.default.unzipAppBundle(at: fileURL, toDirectory: destinationDirectoryURL) 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) } 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. // Refresh anisette data to prevent session timeouts.
AnisetteDataManager.shared.requestAnisetteData { (result) in AnisetteDataManager.shared.requestAnisetteData { (result) in
do do
@@ -117,71 +99,79 @@ extension ALTDeviceManager
let anisetteData = try result.get() let anisetteData = try result.get()
session.anisetteData = anisetteData 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 do
{ {
let profiles = try result.get() let profiles = try result.get()
self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in 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 catch
{ {
finish(error, title: "Failed to Fetch Provisioning Profiles") finish(.failure(error), title: "Failed to Fetch Provisioning Profiles")
} }
} }
} }
catch catch
{ {
finish(error, title: "Failed to Refresh Anisette Data") finish(.failure(error), title: "Failed to Refresh Anisette Data")
} }
} }
} }
catch catch
{ {
finish(error, title: "Failed to Download AltStore") finish(.failure(error), title: "Failed to Download AltStore")
} }
} }
} }
catch catch
{ {
finish(error, title: "Failed to Fetch Certificate") finish(.failure(error), title: "Failed to Fetch Certificate")
} }
} }
} }
catch catch
{ {
finish(error, title: "Failed to Register Device") finish(.failure(error), title: "Failed to Register Device")
} }
} }
} }
catch catch
{ {
finish(error, title: "Failed to Fetch Team") finish(.failure(error), title: "Failed to Fetch Team")
} }
} }
} }
catch catch
{ {
finish(error, title: "Failed to Authenticate") finish(.failure(error), title: "Failed to Authenticate")
} }
} }
} }
catch catch
{ {
finish(error, title: "Failed to Fetch Anisette Data") finish(.failure(error), title: "Failed to Fetch Anisette Data")
} }
} }
} }
}
func downloadApp(completionHandler: @escaping (Result<URL, Error>) -> Void)
private extension ALTDeviceManager
{
func downloadApp(from url: URL, completionHandler: @escaping (Result<URL, Error>) -> 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 do
{ {
let (fileURL, _) = try Result((fileURL, response), error).get() let (fileURL, _) = try Result((fileURL, response), error).get()
completionHandler(.success(fileURL)) completionHandler(.success(fileURL))
do { try FileManager.default.removeItem(at: fileURL) }
catch { print("Failed to remove downloaded .ipa.", error) }
} }
catch catch
{ {
@@ -244,26 +234,91 @@ extension ALTDeviceManager
func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Error>) -> Void) func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Error>) -> Void)
{ {
func finish(_ result: Result<ALTTeam, Error>) ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in
{ do
switch result
{ {
case .failure(let error): let teams = try Result(teams, error).get()
completionHandler(.failure(error))
case .success(let team): if let team = teams.first(where: { $0.type == .individual })
{
return completionHandler(.success(team))
}
else if let team = teams.first(where: { $0.type == .free })
{
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<ALTCertificate, Error>) -> 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 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 if team.type != .free
{ {
DispatchQueue.main.sync { DispatchQueue.main.sync {
let alert = NSAlert() 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(""" 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. 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: "") """, comment: "")
alert.addButton(withTitle: NSLocalizedString("Continue", comment: "")) alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
@@ -278,78 +333,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
} }
} }
if isCancelled guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) }
{
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<ALTCertificate, Error>) -> 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))
}
} }
if let certificate = certificates.first if let certificate = certificates.first
@@ -386,6 +370,14 @@ To prevent this from happening, feel free to try again with another Apple ID to
certificate.privateKey = privateKey certificate.privateKey = privateKey
completionHandler(.success(certificate)) 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 catch
{ {
@@ -407,10 +399,10 @@ To prevent this from happening, feel free to try again with another Apple ID to
} }
} }
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) completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void)
{ {
self.prepareProvisioningProfile(for: application, team: team, session: session) { (result) in self.prepareProvisioningProfile(for: application, parentApp: nil, device: device, team: team, session: session) { (result) in
do do
{ {
let profile = try result.get() let profile = try result.get()
@@ -424,7 +416,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
{ {
dispatchGroup.enter() dispatchGroup.enter()
self.prepareProvisioningProfile(for: appExtension, team: team, session: session) { (result) in self.prepareProvisioningProfile(for: appExtension, parentApp: application, device: device, team: team, session: session) { (result) in
switch result switch result
{ {
case .failure(let e): error = e case .failure(let e): error = e
@@ -453,9 +445,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<ALTProvisioningProfile, Error>) -> Void) func prepareProvisioningProfile(for application: ALTApplication, parentApp: ALTApplication?, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> 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 do
{ {
let appID = try result.get() let appID = try result.get()
@@ -470,7 +488,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
{ {
let appID = try result.get() 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) completionHandler(result)
} }
} }
@@ -493,10 +511,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<ALTAppID, Error>) -> Void) func registerAppID(name appName: String, bundleID: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
{ {
let bundleID = "com.\(team.identifier).\(identifier)"
ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in
do do
{ {
@@ -652,7 +668,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
func register(_ device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> Void) func register(_ device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> 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 do
{ {
let devices = try Result(devices, error).get() let devices = try Result(devices, error).get()
@@ -663,7 +679,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
} }
else 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)) completionHandler(Result(device, error))
} }
} }
@@ -675,9 +691,9 @@ To prevent this from happening, feel free to try again with another Apple ID to
} }
} }
func fetchProvisioningProfile(for appID: ALTAppID, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void) func fetchProvisioningProfile(for appID: ALTAppID, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> 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)) completionHandler(Result(profile, error))
} }
} }
@@ -723,17 +739,21 @@ To prevent this from happening, feel free to try again with another Apple ID to
allURLSchemes.append(altstoreURLScheme) allURLSchemes.append(altstoreURLScheme)
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes] var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes]
additionalValues[Bundle.Info.deviceID] = device.identifier
additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID
if if application.isAltStoreApp
let machineIdentifier = certificate.machineIdentifier,
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
{ {
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") if
try encryptedData.write(to: certificateURL, options: .atomic) 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) try prepare(appBundle, additionalInfoDictionaryValues: additionalValues)
@@ -750,7 +770,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
{ {
try Result(success, error).get() try Result(success, error).get()
let activeProfiles: Set<String>? = (team.type == .free) ? Set(profiles.values.map(\.bundleIdentifier)) : nil let activeProfiles: Set<String>? = (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 ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier, activeProvisioningProfiles: activeProfiles) { (success, error) in
completionHandler(Result(success, error)) completionHandler(Result(success, error))
} }
@@ -769,6 +789,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 private var securityCodeAlertKey = 0

View File

@@ -297,6 +297,9 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
NSError *writeError = nil; NSError *writeError = nil;
if (![self writeDirectory:appBundleURL toDestinationURL:destinationURL client:afc progress:nil error:&writeError]) 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); return finish(writeError);
} }
@@ -430,7 +433,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
else else
{ {
NSURL *destinationFileURL = [destinationURL URLByAppendingPathComponent:fileURL.lastPathComponent isDirectory:NO]; 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; return NO;
} }
@@ -442,7 +445,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
return YES; 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]; NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileURL.path];
if (fileHandle == nil) if (fileHandle == nil)
@@ -458,8 +461,16 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
NSData *data = [fileHandle readDataToEndOfFile]; NSData *data = [fileHandle readDataToEndOfFile];
uint64_t af = 0; 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) if (error)
{ {
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}]; *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}];
@@ -475,10 +486,12 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
{ {
uint32_t count = 0; 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) if (error)
{ {
NSLog(@"Failed writing file with error: %@ (%@ %@)", @(writeResult), fileURL, destinationURL);
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}]; *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}];
} }
@@ -493,6 +506,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
{ {
if (error) 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}]; *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}];
} }
@@ -1113,13 +1127,51 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
continue; 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); lockdownd_client_free(client);
idevice_free(device); idevice_free(device);
NSString *name = [NSString stringWithCString:device_name encoding:NSUTF8StringEncoding]; NSString *name = [NSString stringWithCString:device_name encoding:NSUTF8StringEncoding];
NSString *identifier = [NSString stringWithCString:udid 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]; [connectedDevices addObject:altDevice];
if (device_name != NULL) if (device_name != NULL)

View File

@@ -335,6 +335,8 @@
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; }; BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; };
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6972322CAB8007A79E1 /* InstructionsViewController.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 */; }; 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 */; }; BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; };
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; }; BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; };
BFF7C90F257844C900E55F36 /* AltXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = BFF7C904257844C900E55F36 /* AltXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; BFF7C90F257844C900E55F36 /* AltXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = BFF7C904257844C900E55F36 /* AltXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -769,6 +771,7 @@
BFF0B695232242D3007A79E1 /* LicensesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesViewController.swift; sourceTree = "<group>"; }; BFF0B695232242D3007A79E1 /* LicensesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesViewController.swift; sourceTree = "<group>"; };
BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsViewController.swift; sourceTree = "<group>"; }; BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsViewController.swift; sourceTree = "<group>"; };
BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+CompactHeight.swift"; sourceTree = "<group>"; }; BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+CompactHeight.swift"; sourceTree = "<group>"; };
BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTApplication+AltStoreApp.swift"; sourceTree = "<group>"; };
BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WirelessConnectionHandler.swift; sourceTree = "<group>"; }; BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WirelessConnectionHandler.swift; sourceTree = "<group>"; };
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTServerError+Conveniences.swift"; sourceTree = "<group>"; }; BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTServerError+Conveniences.swift"; sourceTree = "<group>"; };
BFF767CD2489ABE90097E58C /* NetworkConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = "<group>"; }; BFF767CD2489ABE90097E58C /* NetworkConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = "<group>"; };
@@ -1629,6 +1632,7 @@
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */, BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */, BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */,
BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */, BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */,
BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2260,6 +2264,7 @@
BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */, BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */,
BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */, BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */,
BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */, BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */,
BFF435D9255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */,
BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */, BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */,
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */, BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */,
BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */, BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */,
@@ -2501,6 +2506,7 @@
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */, BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */,
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */, BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */,
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */, BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */,
BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */, BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */,
BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */, BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */,
BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */, BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */,

View File

@@ -432,11 +432,11 @@ private extension AuthenticationOperation
{ {
func selectTeam(from teams: [ALTTeam]) 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)) 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)) return completionHandler(.success(team))
} }
@@ -582,7 +582,7 @@ private extension AuthenticationOperation
return completionHandler(.failure(OperationError.unknownUDID)) 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 do
{ {
let devices = try Result(devices, error).get() let devices = try Result(devices, error).get()
@@ -593,7 +593,7 @@ private extension AuthenticationOperation
} }
else 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)) completionHandler(Result(device, error))
} }
} }

View File

@@ -133,7 +133,7 @@ extension FetchProvisioningProfilesOperation
#if DEBUG #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. // Use legacy bundle ID format for AltStore.
preferredBundleID = "com.\(team.identifier).\(app.bundleIdentifier)" preferredBundleID = "com.\(team.identifier).\(app.bundleIdentifier)"
@@ -178,7 +178,7 @@ extension FetchProvisioningProfilesOperation
let parentBundleID = parentApp?.bundleIdentifier ?? app.bundleIdentifier let parentBundleID = parentApp?.bundleIdentifier ?? app.bundleIdentifier
let updatedParentBundleID: String 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). // Use legacy bundle ID format for AltStore (and its extensions).
updatedParentBundleID = "com.\(team.identifier).\(parentBundleID)" updatedParentBundleID = "com.\(team.identifier).\(parentBundleID)"
@@ -463,7 +463,7 @@ extension FetchProvisioningProfilesOperation
func fetchProvisioningProfile(for appID: ALTAppID, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void) func fetchProvisioningProfile(for appID: ALTAppID, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> 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) switch Result(profile, error)
{ {
case .failure(let error): completionHandler(.failure(error)) case .failure(let error): completionHandler(.failure(error))
@@ -477,7 +477,7 @@ extension FetchProvisioningProfilesOperation
case .success: case .success:
// Fetch new provisiong profile // 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)) completionHandler(Result(profile, error))
} }
} }

View File

@@ -169,7 +169,7 @@ private extension ResignAppOperation
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes] 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 } guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { throw OperationError.unknownUDID }
additionalValues[Bundle.Info.deviceID] = udid additionalValues[Bundle.Info.deviceID] = udid

View File

@@ -36,10 +36,10 @@
"bundleIdentifier": "com.rileytestut.Delta.Alpha", "bundleIdentifier": "com.rileytestut.Delta.Alpha",
"developerName": "Riley Testut", "developerName": "Riley Testut",
"subtitle": "Classic games in your pocket.", "subtitle": "Classic games in your pocket.",
"version": "1.3a2", "version": "1.3a4",
"versionDate": "2020-10-26T14:00:00-07:00", "versionDate": "2020-11-30T20:00:00-06:00",
"versionDescription": "This alpha fixes incorrect inputs when playing DS games with melonDS core.", "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_a2.ipa", "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!", "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", "iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
"tintColor": "8A28F7", "tintColor": "8A28F7",

View File

@@ -92,14 +92,14 @@
"bundleIdentifier": "com.rileytestut.Delta.Beta", "bundleIdentifier": "com.rileytestut.Delta.Beta",
"developerName": "Riley Testut", "developerName": "Riley Testut",
"subtitle": "Classic games in your pocket.", "subtitle": "Classic games in your pocket.",
"version": "1.2b3", "version": "1.3b2",
"versionDate": "2020-10-12T12:30:00-07:00", "versionDate": "2020-12-02T12:30:00-06:00",
"versionDescription": "**Delta 1.2 GM**\n\nIMPROVEMENTS\n• Improves sync error messages when a game is missing or cant 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 🙂", "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_2_b3.ipa", "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\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!", "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", "iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
"tintColor": "8A28F7", "tintColor": "8A28F7",
"size": 18456630, "size": 18558635,
"beta": true, "beta": true,
"permissions": [ "permissions": [
{ {
@@ -274,6 +274,6 @@
} }
], ],
"userInfo": { "userInfo": {
"patreonAccessToken": "vfuSOfSYTUC8pEq6TM9r6uoL-T-uTJEI85hFescT4AM" "patreonAccessToken": "dRUfeocZhNq6K3LWfu2poRE9aVkSTcFp2xBviRoOlcY"
} }
} }

View File

@@ -16,13 +16,10 @@ public extension StoreApp
{ {
#if ALPHA #if ALPHA
static let altstoreAppID = "com.rileytestut.AltStore.Alpha" static let altstoreAppID = "com.rileytestut.AltStore.Alpha"
static let alternativeAltStoreAppIDs: Set<String> = ["com.rileytestut.AltStore", "com.rileytestut.AltStore.Beta"]
#elseif BETA #elseif BETA
static let altstoreAppID = "com.rileytestut.AltStore.Beta" static let altstoreAppID = "com.rileytestut.AltStore.Beta"
static let alternativeAltStoreAppIDs: Set<String> = ["com.rileytestut.AltStore", "com.rileytestut.AltStore.Alpha"]
#else #else
static let altstoreAppID = "com.rileytestut.AltStore" static let altstoreAppID = "com.rileytestut.AltStore"
static let alternativeAltStoreAppIDs: Set<String> = ["com.rileytestut.AltStore.Beta", "com.rileytestut.AltStore.Alpha"]
#endif #endif
static let dolphinAppID = "me.oatmealdome.dolphinios-njb" static let dolphinAppID = "me.oatmealdome.dolphinios-njb"

View File

@@ -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
}
}