[AltServer] Supports “remove app” requests

Improves support for removing apps
This commit is contained in:
Riley Testut
2020-05-14 10:29:06 -07:00
parent fe62d6f80f
commit f5fc64be44
6 changed files with 197 additions and 3 deletions

View File

@@ -39,7 +39,9 @@ typedef NS_ERROR_ENUM(AltServerErrorDomain, ALTServerError)
ALTServerErrorInvalidAnisetteData = 13,
ALTServerErrorPluginNotFound = 14,
ALTServerErrorProfileNotFound = 15
ALTServerErrorProfileNotFound = 15,
ALTServerErrorAppDeletionFailed = 16,
};
NS_ASSUME_NONNULL_BEGIN

View File

@@ -95,6 +95,9 @@ NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey = @"bundleIdenti
case ALTServerErrorProfileNotFound:
return [self profileErrorLocalizedDescriptionWithBaseDescription:NSLocalizedString(@"Could not find profile", "")];
case ALTServerErrorAppDeletionFailed:
return NSLocalizedString(@"An error occured while removing the app.", @"");
}
}

View File

@@ -24,6 +24,7 @@ public enum ServerRequest: Decodable
case beginInstallation(BeginInstallationRequest)
case installProvisioningProfiles(InstallProvisioningProfilesRequest)
case removeProvisioningProfiles(RemoveProvisioningProfilesRequest)
case removeApp(RemoveAppRequest)
case unknown(identifier: String, version: Int)
var identifier: String {
@@ -34,6 +35,7 @@ public enum ServerRequest: Decodable
case .beginInstallation(let request): return request.identifier
case .installProvisioningProfiles(let request): return request.identifier
case .removeProvisioningProfiles(let request): return request.identifier
case .removeApp(let request): return request.identifier
case .unknown(let identifier, _): return identifier
}
}
@@ -46,6 +48,7 @@ public enum ServerRequest: Decodable
case .beginInstallation(let request): return request.version
case .installProvisioningProfiles(let request): return request.version
case .removeProvisioningProfiles(let request): return request.version
case .removeApp(let request): return request.version
case .unknown(_, let version): return version
}
}
@@ -85,6 +88,10 @@ public enum ServerRequest: Decodable
let request = try RemoveProvisioningProfilesRequest(from: decoder)
self = .removeProvisioningProfiles(request)
case "RemoveAppRequest":
let request = try RemoveAppRequest(from: decoder)
self = .removeApp(request)
default:
self = .unknown(identifier: identifier, version: version)
}
@@ -97,6 +104,7 @@ public enum ServerResponse: Decodable
case installationProgress(InstallationProgressResponse)
case installProvisioningProfiles(InstallProvisioningProfilesResponse)
case removeProvisioningProfiles(RemoveProvisioningProfilesResponse)
case removeApp(RemoveAppResponse)
case error(ErrorResponse)
case unknown(identifier: String, version: Int)
@@ -107,6 +115,7 @@ public enum ServerResponse: Decodable
case .installationProgress(let response): return response.identifier
case .installProvisioningProfiles(let response): return response.identifier
case .removeProvisioningProfiles(let response): return response.identifier
case .removeApp(let response): return response.identifier
case .error(let response): return response.identifier
case .unknown(let identifier, _): return identifier
}
@@ -119,6 +128,7 @@ public enum ServerResponse: Decodable
case .installationProgress(let response): return response.version
case .installProvisioningProfiles(let response): return response.version
case .removeProvisioningProfiles(let response): return response.version
case .removeApp(let response): return response.version
case .error(let response): return response.version
case .unknown(_, let version): return version
}
@@ -155,6 +165,10 @@ public enum ServerResponse: Decodable
let response = try RemoveProvisioningProfilesResponse(from: decoder)
self = .removeProvisioningProfiles(response)
case "RemoveAppResponse":
let response = try RemoveAppResponse(from: decoder)
self = .removeApp(response)
case "ErrorResponse":
let response = try ErrorResponse(from: decoder)
self = .error(response)
@@ -379,3 +393,28 @@ public struct RemoveProvisioningProfilesResponse: ServerMessageProtocol
{
}
}
public struct RemoveAppRequest: ServerMessageProtocol
{
public var version = 1
public var identifier = "RemoveAppRequest"
public var udid: String
public var bundleIdentifier: String
public init(udid: String, bundleIdentifier: String)
{
self.udid = udid
self.bundleIdentifier = bundleIdentifier
}
}
public struct RemoveAppResponse: ServerMessageProtocol
{
public var version = 1
public var identifier = "RemoveAppResponse"
public init()
{
}
}

View File

@@ -273,6 +273,9 @@ private extension ConnectionManager
case .success(.removeProvisioningProfiles(let request)):
self.handleRemoveProvisioningProfilesRequest(request, for: connection)
case .success(.removeApp(let request)):
self.handleRemoveAppRequest(request, for: connection)
case .success(.unknown):
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
connection.send(response, shouldDisconnect: true) { (result) in
@@ -485,7 +488,31 @@ private extension ConnectionManager
let response = RemoveProvisioningProfilesResponse()
connection.send(response, shouldDisconnect: true) { (result) in
print("Sent remove profiles error response to \(connection) with result:", result)
print("Sent remove profiles success response to \(connection) with result:", result)
}
}
}
}
func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: ClientConnection)
{
ALTDeviceManager.shared.removeApp(forBundleIdentifier: request.bundleIdentifier, fromDeviceWithUDID: request.udid) { (success, error) in
if let error = error, !success
{
print("Failed to remove app \(request.bundleIdentifier):", error)
let errorResponse = ErrorResponse(error: ALTServerError(error))
connection.send(errorResponse, shouldDisconnect: true) { (result) in
print("Sent remove a[[ error response with result:", result)
}
}
else
{
print("Removed app:", request.bundleIdentifier)
let response = RemoveAppResponse()
connection.send(response, shouldDisconnect: true) { (result) in
print("Sent remove app success response to \(connection) with result:", result)
}
}
}

View File

@@ -28,6 +28,7 @@ extern NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification
/* App Installation */
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)removeAppForBundleIdentifier:(NSString *)bundleIdentifier fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)installProvisioningProfiles:(NSSet<ALTProvisioningProfile *> *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet<NSString *> *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;

View File

@@ -20,6 +20,7 @@
#include <libimobiledevice/misagent.h>
void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *udid);
void ALTDeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void *uuid);
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data);
NSNotificationName const ALTDeviceManagerDeviceDidConnectNotification = @"ALTDeviceManagerDeviceDidConnectNotification";
@@ -28,6 +29,8 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
@interface ALTDeviceManager ()
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, void (^)(NSError *)> *installationCompletionHandlers;
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, void (^)(NSError *)> *deletionCompletionHandlers;
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, NSProgress *> *installationProgress;
@property (nonatomic, readonly) dispatch_queue_t installationQueue;
@@ -54,8 +57,9 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
if (self)
{
_installationCompletionHandlers = [NSMutableDictionary dictionary];
_installationProgress = [NSMutableDictionary dictionary];
_deletionCompletionHandlers = [NSMutableDictionary dictionary];
_installationProgress = [NSMutableDictionary dictionary];
_installationQueue = dispatch_queue_create("com.rileytestut.AltServer.InstallationQueue", DISPATCH_QUEUE_SERIAL);
_cachedDevices = [NSMutableSet set];
@@ -498,6 +502,87 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
return success;
}
- (void)removeAppForBundleIdentifier:(NSString *)bundleIdentifier fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler
{
__block idevice_t device = NULL;
__block lockdownd_client_t client = NULL;
__block instproxy_client_t ipc = NULL;
__block lockdownd_service_descriptor_t service = NULL;
void (^finish)(NSError *error) = ^(NSError *e) {
__block NSError *error = e;
lockdownd_service_descriptor_free(service);
instproxy_client_free(ipc);
lockdownd_client_free(client);
idevice_free(device);
if (error != nil)
{
completionHandler(NO, error);
}
else
{
completionHandler(YES, nil);
}
};
/* Find Device */
if (idevice_new(&device, udid.UTF8String) != IDEVICE_E_SUCCESS)
{
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([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
}
/* Connect to Installation Proxy */
if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy", &service) != LOCKDOWN_E_SUCCESS) || service == NULL)
{
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
}
if (instproxy_client_new(device, service, &ipc) != INSTPROXY_E_SUCCESS)
{
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
}
if (service)
{
lockdownd_service_descriptor_free(service);
service = NULL;
}
NSUUID *UUID = [NSUUID UUID];
__block char *uuidString = (char *)malloc(UUID.UUIDString.length + 1);
strncpy(uuidString, (const char *)UUID.UUIDString.UTF8String, UUID.UUIDString.length);
uuidString[UUID.UUIDString.length] = '\0';
self.deletionCompletionHandlers[UUID] = ^(NSError *error) {
if (error != nil)
{
NSString *localizedFailure = [NSString stringWithFormat:NSLocalizedString(@"Could not remove “%@”.", @""), bundleIdentifier];
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
userInfo[NSLocalizedFailureErrorKey] = localizedFailure;
NSError *localizedError = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
finish(localizedError);
}
else
{
finish(nil);
}
free(uuidString);
};
instproxy_uninstall(ipc, bundleIdentifier.UTF8String, NULL, ALTDeviceManagerUpdateAppDeletionStatus, uuidString);
}
#pragma mark - Provisioning Profiles -
- (void)installProvisioningProfiles:(NSSet<ALTProvisioningProfile *> *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *error))completionHandler
@@ -1117,6 +1202,43 @@ void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *uuid)
}
}
void ALTDeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void *uuid)
{
NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:[NSString stringWithUTF8String:(const char *)uuid]];
char *statusName = NULL;
instproxy_status_get_name(status, &statusName);
char *errorName = NULL;
char *errorDescription = NULL;
uint64_t code = 0;
instproxy_status_get_error(status, &errorName, &errorDescription, &code);
if ([@(statusName) isEqualToString:@"Complete"] || code != 0 || errorName != NULL)
{
void (^completionHandler)(NSError *) = ALTDeviceManager.sharedManager.deletionCompletionHandlers[UUID];
if (completionHandler != nil)
{
if (code != 0 || errorName != NULL)
{
NSLog(@"Error removing app. %@ (%@). %@", @(code), @(errorName ?: ""), @(errorDescription ?: ""));
NSError *underlyingError = [NSError errorWithDomain:AltServerInstallationErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: @(errorDescription ?: "")}];
NSError *error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorAppDeletionFailed userInfo:@{NSUnderlyingErrorKey: underlyingError}];
completionHandler(error);
}
else
{
NSLog(@"Finished removing app!");
completionHandler(nil);
}
ALTDeviceManager.sharedManager.deletionCompletionHandlers[UUID] = nil;
}
}
}
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data)
{
ALTDevice * (^deviceForUDID)(NSString *, NSArray<ALTDevice *> *) = ^ALTDevice *(NSString *udid, NSArray<ALTDevice *> *devices) {