diff --git a/AltKit/ServerProtocol.swift b/AltKit/ServerProtocol.swift index b3530f07..8ff666d3 100644 --- a/AltKit/ServerProtocol.swift +++ b/AltKit/ServerProtocol.swift @@ -262,11 +262,15 @@ public struct PrepareAppRequest: ServerMessageProtocol public struct BeginInstallationRequest: ServerMessageProtocol { - public var version = 1 + public var version = 2 public var identifier = "BeginInstallationRequest" - public init() + // If activeProfiles is non-nil, then AltServer should remove all profiles except active ones. + public var activeProfiles: Set? + + public init(activeProfiles: Set?) { + self.activeProfiles = activeProfiles } } diff --git a/AltServer/Connections/ConnectionManager.swift b/AltServer/Connections/ConnectionManager.swift index 519b2e2f..ae89edaa 100644 --- a/AltServer/Connections/ConnectionManager.swift +++ b/AltServer/Connections/ConnectionManager.swift @@ -351,10 +351,10 @@ private extension ConnectionManager switch result { case .failure(let error): finish(.failure(error)) - case .success(.beginInstallation): + case .success(.beginInstallation(let installRequest)): print("Installing to device \(request.udid)...") - self.installApp(at: fileURL, toDeviceWithUDID: request.udid, connection: connection) { (result) in + self.installApp(at: fileURL, toDeviceWithUDID: request.udid, activeProvisioningProfiles: installRequest.activeProfiles, connection: connection) { (result) in print("Installed to device with result:", result) switch result { @@ -403,14 +403,14 @@ private extension ConnectionManager } } - func installApp(at fileURL: URL, toDeviceWithUDID udid: String, connection: ClientConnection, completionHandler: @escaping (Result) -> Void) + func installApp(at fileURL: URL, toDeviceWithUDID udid: String, activeProvisioningProfiles: Set?, connection: ClientConnection, completionHandler: @escaping (Result) -> Void) { let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default) var isSending = false var observation: NSKeyValueObservation? - let progress = ALTDeviceManager.shared.installApp(at: fileURL, toDeviceWithUDID: udid) { (success, error) in + let progress = ALTDeviceManager.shared.installApp(at: fileURL, toDeviceWithUDID: udid, activeProvisioningProfiles: activeProvisioningProfiles) { (success, error) in print("Installed app with result:", error == nil ? "Success" : error!.localizedDescription) if let error = error.map({ $0 as? ALTServerError ?? ALTServerError(.unknown) }) diff --git a/AltServer/Devices/ALTDeviceManager+Installation.swift b/AltServer/Devices/ALTDeviceManager+Installation.swift index d92b5503..a4dee469 100644 --- a/AltServer/Devices/ALTDeviceManager+Installation.swift +++ b/AltServer/Devices/ALTDeviceManager+Installation.swift @@ -536,7 +536,8 @@ To prevent this from happening, feel free to try again with another Apple ID to { try Result(success, error).get() - ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier) { (success, error) in + let activeProfiles: Set? = (team.type == .free) ? [profile.bundleIdentifier] : nil + ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier, activeProvisioningProfiles: activeProfiles) { (success, error) in completionHandler(Result(success, error)) } } diff --git a/AltServer/Devices/ALTDeviceManager.h b/AltServer/Devices/ALTDeviceManager.h index 39a46ebd..3cb90f1f 100644 --- a/AltServer/Devices/ALTDeviceManager.h +++ b/AltServer/Devices/ALTDeviceManager.h @@ -27,7 +27,7 @@ extern NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification - (void)start; /* App Installation */ -- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; +- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; - (void)installProvisioningProfiles:(NSSet *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles removeInactiveProvisioningProfiles:(BOOL)removeInactiveProvisioningProfiles completionHandler:(void (^)(NSDictionary *errors))completionHandler; - (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(NSDictionary *errors))completionHandler; diff --git a/AltServer/Devices/ALTDeviceManager.mm b/AltServer/Devices/ALTDeviceManager.mm index 14cf5224..1181e30c 100644 --- a/AltServer/Devices/ALTDeviceManager.mm +++ b/AltServer/Devices/ALTDeviceManager.mm @@ -71,7 +71,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT #pragma mark - App Installation - -- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler +- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler { NSProgress *progress = [NSProgress discreteProgressWithTotalUnitCount:4]; @@ -88,41 +88,42 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT __block misagent_client_t mis = NULL; __block lockdownd_service_descriptor_t service = NULL; - NSURL *removedProfilesDirectoryURL = [[[NSFileManager defaultManager] temporaryDirectory] URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]]; - NSMutableDictionary *preferredProfiles = [NSMutableDictionary dictionary]; + NSMutableDictionary *cachedProfiles = [NSMutableDictionary dictionary]; + NSMutableSet *installedProfiles = [NSMutableSet set]; - void (^finish)(NSError *error) = ^(NSError *error) { + void (^finish)(NSError *error) = ^(NSError *e) { + __block NSError *error = e; - if ([[NSFileManager defaultManager] fileExistsAtPath:removedProfilesDirectoryURL.path isDirectory:nil]) + if (activeProvisioningProfiles != nil) { - // Reinstall all provisioning profiles we removed before installation. + // Remove installed provisioning profiles if they're not active. - NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:removedProfilesDirectoryURL.path error:nil]; - for (NSString *filename in contents) + for (ALTProvisioningProfile *installedProfile in installedProfiles) { - NSURL *fileURL = [removedProfilesDirectoryURL URLByAppendingPathComponent:filename]; - - ALTProvisioningProfile *provisioningProfile = [[ALTProvisioningProfile alloc] initWithURL:fileURL]; - if (provisioningProfile == nil) + if (![activeProvisioningProfiles containsObject:installedProfile.bundleIdentifier]) { - continue; + NSError *removeError = nil; + if (![self removeProvisioningProfile:installedProfile misagent:mis error:&removeError]) + { + if (error == nil) + { + error = removeError; + } + } } - - ALTProvisioningProfile *preferredProfile = preferredProfiles[provisioningProfile.bundleIdentifier]; - if (![preferredProfile isEqual:provisioningProfile]) - { - continue; - } - - NSError *installError = nil; - if (![self installProvisioningProfile:preferredProfile misagent:mis error:&installError]) + } + } + + [cachedProfiles enumerateKeysAndObjectsUsingBlock:^(NSString *bundleID, ALTProvisioningProfile *profile, BOOL * _Nonnull stop) { + NSError *installError = nil; + if (![self installProvisioningProfile:profile misagent:mis error:&installError]) + { + if (error == nil) { error = installError; } } - - [[NSFileManager defaultManager] removeItemAtURL:removedProfilesDirectoryURL error:nil]; - } + }]; instproxy_client_free(ipc); afc_client_free(afc); @@ -278,17 +279,24 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT service = NULL; } + ALTApplication *application = [[ALTApplication alloc] initWithFileURL:appBundleURL]; + if (application.provisioningProfile) + { + [installedProfiles addObject:application.provisioningProfile]; + } + + for (ALTApplication *appExtension in application.appExtensions) + { + if (appExtension.provisioningProfile) + { + [installedProfiles addObject:appExtension.provisioningProfile]; + } + } + /* Provisioning Profiles */ - NSURL *provisioningProfileURL = [appBundleURL URLByAppendingPathComponent:@"embedded.mobileprovision"]; - ALTProvisioningProfile *installationProvisioningProfile = [[ALTProvisioningProfile alloc] initWithURL:provisioningProfileURL]; - if (installationProvisioningProfile != nil) + if (activeProvisioningProfiles != nil) { NSError *error = nil; - if (![[NSFileManager defaultManager] createDirectoryAtURL:removedProfilesDirectoryURL withIntermediateDirectories:YES attributes:nil error:&error]) - { - return finish(error); - } - NSArray *provisioningProfiles = [self copyProvisioningProfilesWithClient:mis error:&error]; if (provisioningProfiles == nil) { @@ -302,27 +310,33 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT continue; } - ALTProvisioningProfile *preferredProfile = preferredProfiles[provisioningProfile.bundleIdentifier]; - if (preferredProfile != nil) + BOOL installingProfile = NO; + for (ALTProvisioningProfile *installedProfile in installedProfiles) { - if ([provisioningProfile.expirationDate compare:preferredProfile.expirationDate] == NSOrderedDescending) + if ([installedProfile.bundleIdentifier isEqualToString:provisioningProfile.bundleIdentifier]) { - preferredProfiles[provisioningProfile.bundleIdentifier] = provisioningProfile; + installingProfile = YES; + break; } } - else + + if ([activeProvisioningProfiles containsObject:provisioningProfile.bundleIdentifier] && !installingProfile) { - preferredProfiles[provisioningProfile.bundleIdentifier] = provisioningProfile; - } - - NSString *filename = [NSString stringWithFormat:@"%@.mobileprovision", [[NSUUID UUID] UUIDString]]; - NSURL *fileURL = [removedProfilesDirectoryURL URLByAppendingPathComponent:filename]; - - NSError *copyError = nil; - if (![provisioningProfile.data writeToURL:fileURL options:NSDataWritingAtomic error:©Error]) - { - NSLog(@"Failed to copy profile to temporary URL. %@", copyError); - continue; + // We're not installing this provisioning profile, but it is active, + // so we'll cache it to install it again after installing this app. + + ALTProvisioningProfile *preferredProfile = cachedProfiles[provisioningProfile.bundleIdentifier]; + if (preferredProfile != nil) + { + if ([provisioningProfile.expirationDate compare:preferredProfile.expirationDate] == NSOrderedDescending) + { + cachedProfiles[provisioningProfile.bundleIdentifier] = provisioningProfile; + } + } + else + { + cachedProfiles[provisioningProfile.bundleIdentifier] = provisioningProfile; + } } if (![self removeProvisioningProfile:provisioningProfile misagent:mis error:&error])