diff --git a/AltServer/Connections/ConnectionManager.swift b/AltServer/Connections/ConnectionManager.swift index ae89edaa..6c5203a9 100644 --- a/AltServer/Connections/ConnectionManager.swift +++ b/AltServer/Connections/ConnectionManager.swift @@ -445,12 +445,10 @@ private extension ConnectionManager func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: ClientConnection) { - let removeInactiveProfiles = (request.activeProfiles != nil) - ALTDeviceManager.shared.installProvisioningProfiles(request.provisioningProfiles, toDeviceWithUDID: request.udid, activeProvisioningProfiles: request.activeProfiles, removeInactiveProvisioningProfiles: removeInactiveProfiles) { (errors) in - - if let error = errors.values.first + ALTDeviceManager.shared.installProvisioningProfiles(request.provisioningProfiles, toDeviceWithUDID: request.udid, activeProvisioningProfiles: request.activeProfiles) { (success, error) in + if let error = error, !success { - print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", errors) + print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", error) let errorResponse = ErrorResponse(error: ALTServerError(error)) connection.send(errorResponse, shouldDisconnect: true) { (result) in @@ -471,10 +469,10 @@ private extension ConnectionManager func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: ClientConnection) { - ALTDeviceManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers, fromDeviceWithUDID: request.udid) { (errors) in - if let error = errors.values.first + ALTDeviceManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers, fromDeviceWithUDID: request.udid) { (success, error) in + if let error = error, !success { - print("Failed to remove profiles \(request.bundleIdentifiers):", errors) + print("Failed to remove profiles \(request.bundleIdentifiers):", error) let errorResponse = ErrorResponse(error: ALTServerError(error)) connection.send(errorResponse, shouldDisconnect: true) { (result) in diff --git a/AltServer/Devices/ALTDeviceManager.h b/AltServer/Devices/ALTDeviceManager.h index 3cb90f1f..8d27d9f3 100644 --- a/AltServer/Devices/ALTDeviceManager.h +++ b/AltServer/Devices/ALTDeviceManager.h @@ -29,8 +29,8 @@ extern NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification /* App Installation */ - (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; +- (void)installProvisioningProfiles:(NSSet *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; +- (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler; /* Connections */ - (void)startWiredConnectionToDevice:(ALTDevice *)device completionHandler:(void (^)(ALTWiredConnection *_Nullable connection, NSError *_Nullable error))completionHandler; diff --git a/AltServer/Devices/ALTDeviceManager.mm b/AltServer/Devices/ALTDeviceManager.mm index 1181e30c..851626b0 100644 --- a/AltServer/Devices/ALTDeviceManager.mm +++ b/AltServer/Devices/ALTDeviceManager.mm @@ -176,6 +176,20 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT return finish([NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadCorruptFileError userInfo:@{NSURLErrorKey: fileURL}]); } + 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]; + } + } + /* Find Device */ if (idevice_new(&device, udid.UTF8String) != IDEVICE_E_SUCCESS) { @@ -279,76 +293,39 @@ 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 */ - if (activeProvisioningProfiles != nil) + BOOL shouldManageProfiles = (activeProvisioningProfiles != nil || [application.provisioningProfile isFreeProvisioningProfile]); + if (shouldManageProfiles) { + // Free developer account was used to sign this app, so we need to remove all + // provisioning profiles in order to remain under sideloaded app limit. + NSError *error = nil; - NSArray *provisioningProfiles = [self copyProvisioningProfilesWithClient:mis error:&error]; - if (provisioningProfiles == nil) + NSDictionary *removedProfiles = [self removeAllFreeProfilesExcludingBundleIdentifiers:nil misagent:mis error:&error]; + if (removedProfiles == nil) { return finish(error); } - - for (ALTProvisioningProfile *provisioningProfile in provisioningProfiles) - { - if (![provisioningProfile isFreeProvisioningProfile]) + + [removedProfiles enumerateKeysAndObjectsUsingBlock:^(NSString *bundleID, ALTProvisioningProfile *profile, BOOL * _Nonnull stop) { + if (activeProvisioningProfiles != nil) { - continue; - } - - BOOL installingProfile = NO; - for (ALTProvisioningProfile *installedProfile in installedProfiles) - { - if ([installedProfile.bundleIdentifier isEqualToString:provisioningProfile.bundleIdentifier]) + if ([activeProvisioningProfiles containsObject:bundleID]) { - installingProfile = YES; - break; + // Only cache active profiles to reinstall afterwards. + cachedProfiles[bundleID] = profile; } } - - if ([activeProvisioningProfiles containsObject:provisioningProfile.bundleIdentifier] && !installingProfile) + else { - // 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; - } + // Cache all profiles to reinstall afterwards if we didn't provide activeProvisioningProfiles. + cachedProfiles[bundleID] = profile; } - - if (![self removeProvisioningProfile:provisioningProfile misagent:mis error:&error]) - { - return finish(error); - } - } - - lockdownd_client_free(client); - client = NULL; + }]; } + lockdownd_client_free(client); + client = NULL; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); NSProgress *installationProgress = [NSProgress progressWithTotalUnitCount:100 parent:progress pendingUnitCount:1]; @@ -514,7 +491,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT #pragma mark - Provisioning Profiles - -- (void)installProvisioningProfiles:(NSSet *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(NSSet *)activeProvisioningProfiles removeInactiveProvisioningProfiles:(BOOL)removeInactiveProvisioningProfiles completionHandler:(void (^)(NSDictionary *errors))completionHandler; +- (void)installProvisioningProfiles:(NSSet *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *error))completionHandler { dispatch_async(self.installationQueue, ^{ __block idevice_t device = NULL; @@ -523,109 +500,86 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT __block misagent_client_t mis = NULL; __block lockdownd_service_descriptor_t service = NULL; - void (^finish)(NSDictionary *, NSError *) = ^(NSDictionary *installationErrors, NSError *error) { + void (^finish)(NSError *_Nullable) = ^(NSError *error) { lockdownd_service_descriptor_free(service); misagent_client_free(mis); afc_client_free(afc); lockdownd_client_free(client); idevice_free(device); - if (installationErrors) - { - completionHandler(installationErrors); - } - else - { - NSMutableDictionary *installationErrors = [NSMutableDictionary dictionary]; - for (ALTProvisioningProfile *profile in provisioningProfiles) - { - installationErrors[profile] = error; - } - - completionHandler(installationErrors); - } + completionHandler(error == nil, error); }; /* Find Device */ if (idevice_new(&device, udid.UTF8String) != IDEVICE_E_SUCCESS) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]); } /* Connect to Device */ if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); } /* Connect to Misagent */ if (lockdownd_start_service(client, "com.apple.misagent", &service) != LOCKDOWN_E_SUCCESS || service == NULL) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); } if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); } NSError *error = nil; - NSArray *installedProvisioningProfiles = [self copyProvisioningProfilesWithClient:mis error:&error]; - if (provisioningProfiles == nil) + + if (activeProvisioningProfiles != nil) { - return finish(nil, error); + // Remove all non-active free provisioning profiles. + + NSMutableSet *excludedBundleIdentifiers = [activeProvisioningProfiles mutableCopy]; + for (ALTProvisioningProfile *provisioningProfile in provisioningProfiles) + { + // Ensure we DO remove old versions of profiles we're about to install, even if they are active. + [excludedBundleIdentifiers removeObject:provisioningProfile.bundleIdentifier]; + } + + if (![self removeAllFreeProfilesExcludingBundleIdentifiers:excludedBundleIdentifiers misagent:mis error:&error]) + { + return finish(error); + } } - - for (ALTProvisioningProfile *provisioningProfile in installedProvisioningProfiles) + else { - if (![provisioningProfile isFreeProvisioningProfile]) + // Remove only older versions of provisioning profiles we're about to install. + + NSMutableSet *bundleIdentifiers = [NSMutableSet set]; + for (ALTProvisioningProfile *provisioningProfile in provisioningProfiles) { - continue; + [bundleIdentifiers addObject:provisioningProfile.bundleIdentifier]; } - BOOL removeProfile = NO; - - for (ALTProvisioningProfile *profile in provisioningProfiles) + if (![self removeProvisioningProfilesForBundleIdentifiers:bundleIdentifiers misagent:mis error:&error]) { - if ([profile.bundleIdentifier isEqualToString:provisioningProfile.bundleIdentifier]) - { - // Remove previous provisioning profile before installing new one. - removeProfile = YES; - break; - } + return finish(error); } - - if (removeInactiveProvisioningProfiles && ![activeProvisioningProfiles containsObject:provisioningProfile.bundleIdentifier]) - { - // Remove all non-active provisioning profiles to remain under 3 app limit for free developer accounts. - removeProfile = YES; - } - - if (removeProfile) - { - if (![self removeProvisioningProfile:provisioningProfile misagent:mis error:&error]) - { - return finish(nil, error); - } - } } - - NSMutableDictionary *profileErrors = [NSMutableDictionary dictionary]; - + for (ALTProvisioningProfile *provisioningProfile in provisioningProfiles) { - NSError *error = nil; if (![self installProvisioningProfile:provisioningProfile misagent:mis error:&error]) { - profileErrors[provisioningProfile] = error; + return finish(error); } } - finish(profileErrors, nil); + finish(nil); }); } -- (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(NSDictionary *errors))completionHandler +- (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *error))completionHandler { dispatch_async(self.installationQueue, ^{ __block idevice_t device = NULL; @@ -634,79 +588,112 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT __block misagent_client_t mis = NULL; __block lockdownd_service_descriptor_t service = NULL; - void (^finish)(NSDictionary *, NSError *) = ^(NSDictionary *installationErrors, NSError *error) { + void (^finish)(NSError *_Nullable) = ^(NSError *error) { lockdownd_service_descriptor_free(service); misagent_client_free(mis); afc_client_free(afc); lockdownd_client_free(client); idevice_free(device); - if (installationErrors) - { - completionHandler(installationErrors); - } - else - { - NSMutableDictionary *installationErrors = [NSMutableDictionary dictionary]; - for (NSString *bundleID in bundleIdentifiers) - { - installationErrors[bundleID] = error; - } - - completionHandler(installationErrors); - } + completionHandler(error == nil, error); }; /* Find Device */ if (idevice_new(&device, udid.UTF8String) != IDEVICE_E_SUCCESS) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]); } /* Connect to Device */ if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); } /* Connect to Misagent */ if (lockdownd_start_service(client, "com.apple.misagent", &service) != LOCKDOWN_E_SUCCESS || service == NULL) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); } if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) { - return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); + return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]); } NSError *error = nil; - NSArray *provisioningProfiles = [self copyProvisioningProfilesWithClient:mis error:&error]; - if (provisioningProfiles == nil) + if (![self removeProvisioningProfilesForBundleIdentifiers:bundleIdentifiers misagent:mis error:&error]) { - return finish(nil, error); + return finish(error); } - NSMutableDictionary *profileErrors = [NSMutableDictionary dictionary]; - - /* Remove provisioning profiles */ - for (ALTProvisioningProfile *provisioningProfile in provisioningProfiles) - { - if (![bundleIdentifiers containsObject:provisioningProfile.bundleIdentifier]) - { - continue; - } - - if (![self removeProvisioningProfile:provisioningProfile misagent:mis error:&error]) - { - profileErrors[provisioningProfile.bundleIdentifier] = error; - } - } - - finish(profileErrors, nil); + finish(nil); }); } +- (NSDictionary *)removeProvisioningProfilesForBundleIdentifiers:(NSSet *)bundleIdentifiers misagent:(misagent_client_t)mis error:(NSError **)error +{ + return [self removeAllProfilesForBundleIdentifiers:bundleIdentifiers excludingBundleIdentifiers:nil limitedToFreeProfiles:NO misagent:mis error:error]; +} + +- (NSDictionary *)removeAllFreeProfilesExcludingBundleIdentifiers:(nullable NSSet *)bundleIdentifiers misagent:(misagent_client_t)mis error:(NSError **)error +{ + return [self removeAllProfilesForBundleIdentifiers:nil excludingBundleIdentifiers:bundleIdentifiers limitedToFreeProfiles:YES misagent:mis error:error]; +} + +- (NSDictionary *)removeAllProfilesForBundleIdentifiers:(nullable NSSet *)includedBundleIdentifiers + excludingBundleIdentifiers:(nullable NSSet *)excludedBundleIdentifiers + limitedToFreeProfiles:(BOOL)limitedToFreeProfiles + misagent:(misagent_client_t)mis + error:(NSError **)error +{ + NSMutableDictionary *cachedProfiles = [NSMutableDictionary dictionary]; + + NSArray *provisioningProfiles = [self copyProvisioningProfilesWithClient:mis error:error]; + if (provisioningProfiles == nil) + { + return nil; + } + + for (ALTProvisioningProfile *provisioningProfile in provisioningProfiles) + { + if (limitedToFreeProfiles && ![provisioningProfile isFreeProvisioningProfile]) + { + continue; + } + + if (includedBundleIdentifiers != nil && ![includedBundleIdentifiers containsObject:provisioningProfile.bundleIdentifier]) + { + continue; + } + + if (excludedBundleIdentifiers != nil && [excludedBundleIdentifiers containsObject:provisioningProfile.bundleIdentifier]) + { + continue; + } + + 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]) + { + return nil; + } + } + + return cachedProfiles; +} + - (BOOL)installProvisioningProfile:(ALTProvisioningProfile *)provisioningProfile misagent:(misagent_client_t)mis error:(NSError **)error { plist_t pdata = plist_new_data((const char *)provisioningProfile.data.bytes, provisioningProfile.data.length);