mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-20 12:13:26 +01:00
[AltServer] Supports installing apps with app extensions
This commit is contained in:
@@ -117,51 +117,18 @@ extension ALTDeviceManager
|
|||||||
let anisetteData = try result.get()
|
let anisetteData = try result.get()
|
||||||
session.anisetteData = anisetteData
|
session.anisetteData = anisetteData
|
||||||
|
|
||||||
self.registerAppID(name: "AltStore", identifier: application.bundleIdentifier, team: team, session: session) { (result) in
|
self.prepareAllProvisioningProfiles(for: application, team: team, session: session) { (result) in
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let appID = try result.get()
|
let profiles = try result.get()
|
||||||
|
|
||||||
self.updateFeatures(for: appID, app: application, team: team, session: session) { (result) in
|
self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in
|
||||||
do
|
finish(result.error, title: "Failed to Install AltStore")
|
||||||
{
|
|
||||||
let appID = try result.get()
|
|
||||||
|
|
||||||
self.updateAppGroups(for: appID, app: application, team: team, session: session) { (result) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let appID = try result.get()
|
|
||||||
|
|
||||||
self.fetchProvisioningProfile(for: appID, team: team, session: session) { (result) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let provisioningProfile = try result.get()
|
|
||||||
|
|
||||||
self.install(application, to: device, team: team, appID: appID, certificate: certificate, profile: provisioningProfile) { (result) in
|
|
||||||
finish(result.error, title: "Failed to Install AltStore")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
finish(error, title: "Failed to Fetch Provisioning Profile")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
finish(error, title: "Failed to Update App Groups")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
finish(error, title: "Failed to Update App ID")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Register App")
|
finish(error, title: "Failed to Fetch Provisioning Profiles")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,6 +407,92 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareAllProvisioningProfiles(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession,
|
||||||
|
completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void)
|
||||||
|
{
|
||||||
|
self.prepareProvisioningProfile(for: application, team: team, session: session) { (result) in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let profile = try result.get()
|
||||||
|
|
||||||
|
var profiles = [application.bundleIdentifier: profile]
|
||||||
|
var error: Error?
|
||||||
|
|
||||||
|
let dispatchGroup = DispatchGroup()
|
||||||
|
|
||||||
|
for appExtension in application.appExtensions
|
||||||
|
{
|
||||||
|
dispatchGroup.enter()
|
||||||
|
|
||||||
|
self.prepareProvisioningProfile(for: appExtension, team: team, session: session) { (result) in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let e): error = e
|
||||||
|
case .success(let profile): profiles[appExtension.bundleIdentifier] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchGroup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchGroup.notify(queue: .global()) {
|
||||||
|
if let error = error
|
||||||
|
{
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completion(.success(profiles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareProvisioningProfile(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
|
||||||
|
{
|
||||||
|
self.registerAppID(name: application.name, identifier: application.bundleIdentifier, team: team, session: session) { (result) in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let appID = try result.get()
|
||||||
|
|
||||||
|
self.updateFeatures(for: appID, app: application, team: team, session: session) { (result) in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let appID = try result.get()
|
||||||
|
|
||||||
|
self.updateAppGroups(for: appID, app: application, team: team, session: session) { (result) in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let appID = try result.get()
|
||||||
|
|
||||||
|
self.fetchProvisioningProfile(for: appID, team: team, session: session) { (result) in
|
||||||
|
completionHandler(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func registerAppID(name appName: String, identifier: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
|
func registerAppID(name appName: String, identifier: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
|
||||||
{
|
{
|
||||||
let bundleID = "com.\(team.identifier).\(identifier)"
|
let bundleID = "com.\(team.identifier).\(identifier)"
|
||||||
@@ -629,18 +682,35 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func install(_ application: ALTApplication, to device: ALTDevice, team: ALTTeam, appID: ALTAppID, certificate: ALTCertificate, profile: ALTProvisioningProfile, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
func install(_ application: ALTApplication, to device: ALTDevice, team: ALTTeam, certificate: ALTCertificate, profiles: [String: ALTProvisioningProfile], completionHandler: @escaping (Result<Void, Error>) -> Void)
|
||||||
{
|
{
|
||||||
|
func prepare(_ bundle: Bundle, additionalInfoDictionaryValues: [String: Any] = [:]) throws
|
||||||
|
{
|
||||||
|
guard let identifier = bundle.bundleIdentifier else { throw ALTError(.missingAppBundle) }
|
||||||
|
guard let profile = profiles[identifier] else { throw ALTError(.missingProvisioningProfile) }
|
||||||
|
guard var infoDictionary = bundle.completeInfoDictionary else { throw ALTError(.missingInfoPlist) }
|
||||||
|
|
||||||
|
infoDictionary[kCFBundleIdentifierKey as String] = profile.bundleIdentifier
|
||||||
|
infoDictionary[Bundle.Info.altBundleID] = identifier
|
||||||
|
|
||||||
|
for (key, value) in additionalInfoDictionaryValues
|
||||||
|
{
|
||||||
|
infoDictionary[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if let appGroups = profile.entitlements[.appGroups] as? [String]
|
||||||
|
{
|
||||||
|
infoDictionary[Bundle.Info.appGroups] = appGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
try (infoDictionary as NSDictionary).write(to: bundle.infoPlistURL)
|
||||||
|
}
|
||||||
|
|
||||||
DispatchQueue.global().async {
|
DispatchQueue.global().async {
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let infoPlistURL = application.fileURL.appendingPathComponent("Info.plist")
|
guard let appBundle = Bundle(url: application.fileURL) else { throw ALTError(.missingAppBundle) }
|
||||||
|
guard let infoDictionary = appBundle.completeInfoDictionary else { throw ALTError(.missingInfoPlist) }
|
||||||
guard var infoDictionary = NSDictionary(contentsOf: infoPlistURL) as? [String: Any] else { throw ALTError(.missingInfoPlist) }
|
|
||||||
infoDictionary[kCFBundleIdentifierKey as String] = profile.bundleIdentifier
|
|
||||||
infoDictionary[Bundle.Info.deviceID] = device.identifier
|
|
||||||
infoDictionary[Bundle.Info.serverID] = UserDefaults.standard.serverID
|
|
||||||
infoDictionary[Bundle.Info.certificateID] = certificate.serialNumber
|
|
||||||
|
|
||||||
let openAppURL = URL(string: "altstore-" + application.bundleIdentifier + "://")!
|
let openAppURL = URL(string: "altstore-" + application.bundleIdentifier + "://")!
|
||||||
|
|
||||||
@@ -652,30 +722,35 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
|||||||
"CFBundleURLSchemes": [openAppURL.scheme!]] as [String : Any]
|
"CFBundleURLSchemes": [openAppURL.scheme!]] as [String : Any]
|
||||||
allURLSchemes.append(altstoreURLScheme)
|
allURLSchemes.append(altstoreURLScheme)
|
||||||
|
|
||||||
infoDictionary[Bundle.Info.urlTypes] = allURLSchemes
|
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes]
|
||||||
|
additionalValues[Bundle.Info.deviceID] = device.identifier
|
||||||
if let appGroups = profile.entitlements[.appGroups] as? [String]
|
additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID
|
||||||
{
|
|
||||||
infoDictionary[Bundle.Info.appGroups] = appGroups
|
|
||||||
}
|
|
||||||
|
|
||||||
try (infoDictionary as NSDictionary).write(to: infoPlistURL)
|
|
||||||
|
|
||||||
if
|
if
|
||||||
let machineIdentifier = certificate.machineIdentifier,
|
let machineIdentifier = certificate.machineIdentifier,
|
||||||
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
|
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
|
||||||
{
|
{
|
||||||
|
additionalValues[Bundle.Info.certificateID] = certificate.serialNumber
|
||||||
|
|
||||||
let certificateURL = application.fileURL.appendingPathComponent("ALTCertificate.p12")
|
let certificateURL = application.fileURL.appendingPathComponent("ALTCertificate.p12")
|
||||||
try encryptedData.write(to: certificateURL, options: .atomic)
|
try encryptedData.write(to: certificateURL, options: .atomic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try prepare(appBundle, additionalInfoDictionaryValues: additionalValues)
|
||||||
|
|
||||||
|
for appExtension in application.appExtensions
|
||||||
|
{
|
||||||
|
guard let bundle = Bundle(url: appExtension.fileURL) else { throw ALTError(.missingAppBundle) }
|
||||||
|
try prepare(bundle)
|
||||||
|
}
|
||||||
|
|
||||||
let resigner = ALTSigner(team: team, certificate: certificate)
|
let resigner = ALTSigner(team: team, certificate: certificate)
|
||||||
resigner.signApp(at: application.fileURL, provisioningProfiles: [profile]) { (success, error) in
|
resigner.signApp(at: application.fileURL, provisioningProfiles: Array(profiles.values)) { (success, error) in
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try Result(success, error).get()
|
try Result(success, error).get()
|
||||||
|
|
||||||
let activeProfiles: Set<String>? = (team.type == .free) ? [profile.bundleIdentifier] : nil
|
let activeProfiles: Set<String>? = (team.type == .free) ? 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))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user