diff --git a/AltServer/Categories/NSError+libimobiledevice.h b/AltServer/Categories/NSError+libimobiledevice.h new file mode 100644 index 00000000..5a711529 --- /dev/null +++ b/AltServer/Categories/NSError+libimobiledevice.h @@ -0,0 +1,25 @@ +// +// NSError+libimobiledevice.h +// AltServer +// +// Created by Riley Testut on 3/23/21. +// Copyright © 2021 Riley Testut. All rights reserved. +// + +#import +#import +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSError (libimobiledevice) + ++ (nullable instancetype)errorWithMobileImageMounterError:(mobile_image_mounter_error_t)error device:(nullable ALTDevice *)device; ++ (nullable instancetype)errorWithDebugServerError:(debugserver_error_t)error device:(nullable ALTDevice *)device; ++ (nullable instancetype)errorWithInstallationProxyError:(instproxy_error_t)error device:(nullable ALTDevice *)device; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AltServer/Categories/NSError+libimobiledevice.mm b/AltServer/Categories/NSError+libimobiledevice.mm new file mode 100644 index 00000000..cbeb3c22 --- /dev/null +++ b/AltServer/Categories/NSError+libimobiledevice.mm @@ -0,0 +1,86 @@ +// +// NSError+libimobiledevice.m +// AltServer +// +// Created by Riley Testut on 3/23/21. +// Copyright © 2021 Riley Testut. All rights reserved. +// + +#import "NSError+libimobiledevice.h" +#import "NSError+ALTServerError.h" + +@implementation NSError (libimobiledevice) + ++ (nullable instancetype)errorWithMobileImageMounterError:(mobile_image_mounter_error_t)error device:(nullable ALTDevice *)device +{ + NSMutableDictionary *userInfo = [@{ + ALTUnderlyingErrorDomainErrorKey: @"Mobile Image Mounter", + ALTUnderlyingErrorCodeErrorKey: [@(error) description], + } mutableCopy]; + + if (device) + { + userInfo[ALTDeviceNameErrorKey] = device.name; + } + + switch (error) + { + case MOBILE_IMAGE_MOUNTER_E_SUCCESS: return nil; + case MOBILE_IMAGE_MOUNTER_E_INVALID_ARG: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo]; + case MOBILE_IMAGE_MOUNTER_E_PLIST_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidResponse userInfo:userInfo]; + case MOBILE_IMAGE_MOUNTER_E_CONN_FAILED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUsbmuxd userInfo:userInfo]; + case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo]; + case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorDeviceLocked userInfo:userInfo]; + case MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:userInfo]; + } +} + ++ (nullable instancetype)errorWithDebugServerError:(debugserver_error_t)error device:(nullable ALTDevice *)device +{ + NSMutableDictionary *userInfo = [@{ + ALTUnderlyingErrorDomainErrorKey: @"Debug Server", + ALTUnderlyingErrorCodeErrorKey: [@(error) description], + } mutableCopy]; + + if (device) + { + userInfo[ALTDeviceNameErrorKey] = device.name; + } + + switch (error) + { + case DEBUGSERVER_E_SUCCESS: return nil; + case DEBUGSERVER_E_INVALID_ARG: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo]; + case DEBUGSERVER_E_MUX_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUsbmuxd userInfo:userInfo]; + case DEBUGSERVER_E_SSL_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorSSL userInfo:userInfo]; + case DEBUGSERVER_E_RESPONSE_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidResponse userInfo:userInfo]; + case DEBUGSERVER_E_TIMEOUT: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorTimedOut userInfo:userInfo]; + case DEBUGSERVER_E_UNKNOWN_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:userInfo]; + } +} + ++ (nullable instancetype)errorWithInstallationProxyError:(instproxy_error_t)error device:(nullable ALTDevice *)device +{ + NSMutableDictionary *userInfo = [@{ + ALTUnderlyingErrorDomainErrorKey: @"Installation Proxy", + ALTUnderlyingErrorCodeErrorKey: [@(error) description], + } mutableCopy]; + + if (device) + { + userInfo[ALTDeviceNameErrorKey] = device.name; + } + + switch (error) + { + case INSTPROXY_E_SUCCESS: return nil; + case INSTPROXY_E_INVALID_ARG: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo]; + case INSTPROXY_E_PLIST_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidResponse userInfo:userInfo]; + case INSTPROXY_E_CONN_FAILED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUsbmuxd userInfo:userInfo]; + case INSTPROXY_E_RECEIVE_TIMEOUT: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorTimedOut userInfo:userInfo]; +// case INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW: return [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorUnsupportediOSVersion userInfo:nil]; // Error message assumes we're installing AltStore + default: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:userInfo]; + } +} + +@end diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index a4acc286..7469a9a8 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -304,6 +304,7 @@ BFE60742231B07E6002B0E8E /* SettingsHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE60741231B07E6002B0E8E /* SettingsHeaderFooterView.swift */; }; BFE6325A22A83BEB00F30809 /* Authentication.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFE6325922A83BEB00F30809 /* Authentication.storyboard */; }; BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6326B22A86FF300F30809 /* AuthenticationOperation.swift */; }; + BFE972E3260A8B2700D0BDAC /* NSError+libimobiledevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = BFE972E2260A8B2700D0BDAC /* NSError+libimobiledevice.mm */; }; BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD44605241188C300EAB90A /* CodableServerError.swift */; }; BFECAC8024FD950B0077C41F /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF22485828200DD5981 /* ConnectionManager.swift */; }; BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */; }; @@ -761,6 +762,8 @@ BFE60741231B07E6002B0E8E /* SettingsHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHeaderFooterView.swift; sourceTree = ""; }; BFE6325922A83BEB00F30809 /* Authentication.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Authentication.storyboard; sourceTree = ""; }; BFE6326B22A86FF300F30809 /* AuthenticationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationOperation.swift; sourceTree = ""; }; + BFE972E1260A8B2700D0BDAC /* NSError+libimobiledevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSError+libimobiledevice.h"; sourceTree = ""; }; + BFE972E2260A8B2700D0BDAC /* NSError+libimobiledevice.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSError+libimobiledevice.mm"; sourceTree = ""; }; BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = ""; }; BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundRefreshAppsOperation.swift; sourceTree = ""; }; BFF00D332501BDCF00746320 /* IntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; @@ -965,6 +968,7 @@ BF703195229F36FF006E110F /* Devices */, BFD52BDC22A0A659000B7ED1 /* Connections */, BF055B4A233B528B0086DEA9 /* Extensions */, + BFE972E0260A8B0700D0BDAC /* Categories */, BF703194229F36F6006E110F /* Resources */, BF703196229F370F006E110F /* Supporting Files */, ); @@ -1616,6 +1620,15 @@ path = Authentication; sourceTree = ""; }; + BFE972E0260A8B0700D0BDAC /* Categories */ = { + isa = PBXGroup; + children = ( + BFE972E1260A8B2700D0BDAC /* NSError+libimobiledevice.h */, + BFE972E2260A8B2700D0BDAC /* NSError+libimobiledevice.mm */, + ); + path = Categories; + sourceTree = ""; + }; BFF00D2E2501BD4B00746320 /* Intents */ = { isa = PBXGroup; children = ( @@ -2276,6 +2289,7 @@ BFECAC9424FD98BA0077C41F /* NSError+ALTServerError.m in Sources */, BFECAC9324FD98BA0077C41F /* CFNotificationName+AltStore.m in Sources */, BFE48975238007CE003239E0 /* AnisetteDataManager.swift in Sources */, + BFE972E3260A8B2700D0BDAC /* NSError+libimobiledevice.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Shared/Categories/NSError+ALTServerError.h b/Shared/Categories/NSError+ALTServerError.h index 0fcf48d3..902c680e 100644 --- a/Shared/Categories/NSError+ALTServerError.h +++ b/Shared/Categories/NSError+ALTServerError.h @@ -10,9 +10,13 @@ extern NSErrorDomain const AltServerErrorDomain; extern NSErrorDomain const AltServerInstallationErrorDomain; +extern NSErrorDomain const AltServerConnectionErrorDomain; +extern NSErrorUserInfoKey const ALTUnderlyingErrorDomainErrorKey; extern NSErrorUserInfoKey const ALTUnderlyingErrorCodeErrorKey; extern NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey; +extern NSErrorUserInfoKey const ALTAppNameErrorKey; +extern NSErrorUserInfoKey const ALTDeviceNameErrorKey; typedef NS_ERROR_ENUM(AltServerErrorDomain, ALTServerError) { @@ -44,6 +48,17 @@ typedef NS_ERROR_ENUM(AltServerErrorDomain, ALTServerError) ALTServerErrorAppDeletionFailed = 16, }; +typedef NS_ERROR_ENUM(AltServerConnectionErrorDomain, ALTServerConnectionError) +{ + ALTServerConnectionErrorUnknown, + ALTServerConnectionErrorDeviceLocked, + ALTServerConnectionErrorInvalidRequest, + ALTServerConnectionErrorInvalidResponse, + ALTServerConnectionErrorUsbmuxd, + ALTServerConnectionErrorSSL, + ALTServerConnectionErrorTimedOut, +}; + NS_ASSUME_NONNULL_BEGIN @interface NSError (ALTServerError) diff --git a/Shared/Categories/NSError+ALTServerError.m b/Shared/Categories/NSError+ALTServerError.m index 9591acdf..4d7e0a24 100644 --- a/Shared/Categories/NSError+ALTServerError.m +++ b/Shared/Categories/NSError+ALTServerError.m @@ -10,9 +10,13 @@ NSErrorDomain const AltServerErrorDomain = @"com.rileytestut.AltServer"; NSErrorDomain const AltServerInstallationErrorDomain = @"com.rileytestut.AltServer.Installation"; +NSErrorDomain const AltServerConnectionErrorDomain = @"com.rileytestut.AltServer.Connection"; +NSErrorUserInfoKey const ALTUnderlyingErrorDomainErrorKey = @"underlyingErrorDomain"; NSErrorUserInfoKey const ALTUnderlyingErrorCodeErrorKey = @"underlyingErrorCode"; NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey = @"bundleIdentifier"; +NSErrorUserInfoKey const ALTAppNameErrorKey = @"appName"; +NSErrorUserInfoKey const ALTDeviceNameErrorKey = @"deviceName"; @implementation NSError (ALTServerError) @@ -23,14 +27,26 @@ NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey = @"bundleIdenti { return [error altserver_localizedFailureReason]; } - - if ([userInfoKey isEqualToString:NSLocalizedRecoverySuggestionErrorKey]) + else if ([userInfoKey isEqualToString:NSLocalizedRecoverySuggestionErrorKey]) { return [error altserver_localizedRecoverySuggestion]; } return nil; }]; + + [NSError setUserInfoValueProviderForDomain:AltServerConnectionErrorDomain provider:^id _Nullable(NSError * _Nonnull error, NSErrorUserInfoKey _Nonnull userInfoKey) { + if ([userInfoKey isEqualToString:NSLocalizedDescriptionKey]) + { + return [error altserver_connection_localizedDescription]; + } + else if ([userInfoKey isEqualToString:NSLocalizedRecoverySuggestionErrorKey]) + { + return [error altserver_connection_localizedRecoverySuggestion]; + } + + return nil; + }]; } - (nullable NSString *)altserver_localizedFailureReason @@ -146,5 +162,88 @@ NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey = @"bundleIdenti return localizedDescription; } + +#pragma mark - AltServerConnectionErrorDomain - + +- (nullable NSString *)altserver_connection_localizedDescription +{ + switch ((ALTServerConnectionError)self.code) + { + case ALTServerConnectionErrorUnknown: + { + NSString *underlyingErrorDomain = self.userInfo[ALTUnderlyingErrorDomainErrorKey]; + NSString *underlyingErrorCode = self.userInfo[ALTUnderlyingErrorCodeErrorKey]; + + if (underlyingErrorDomain != nil && underlyingErrorCode != nil) + { + return [NSString stringWithFormat:NSLocalizedString(@"%@ error %@.", @""), underlyingErrorDomain, underlyingErrorCode]; + } + else if (underlyingErrorCode != nil) + { + return [NSString stringWithFormat:NSLocalizedString(@"Connection error code: %@", @""), underlyingErrorCode]; + } + + return nil; + } + + case ALTServerConnectionErrorDeviceLocked: + { + NSString *deviceName = self.userInfo[ALTDeviceNameErrorKey] ?: NSLocalizedString(@"The device", @""); + return [NSString stringWithFormat:NSLocalizedString(@"%@ is currently locked.", @""), deviceName]; + } + + case ALTServerConnectionErrorInvalidRequest: + { + NSString *deviceName = self.userInfo[ALTDeviceNameErrorKey] ?: NSLocalizedString(@"The device", @""); + return [NSString stringWithFormat:NSLocalizedString(@"%@ received an invalid request from AltServer.", @""), deviceName]; + } + + case ALTServerConnectionErrorInvalidResponse: + { + NSString *deviceName = self.userInfo[ALTDeviceNameErrorKey] ?: NSLocalizedString(@"the device", @""); + return [NSString stringWithFormat:NSLocalizedString(@"AltServer received an invalid response from %@.", @""), deviceName]; + } + + case ALTServerConnectionErrorUsbmuxd: + { + return NSLocalizedString(@"There was an issue communicating with the usbmuxd daemon.", @""); + } + + case ALTServerConnectionErrorSSL: + { + NSString *deviceName = self.userInfo[ALTDeviceNameErrorKey] ?: NSLocalizedString(@"the device", @""); + return [NSString stringWithFormat:NSLocalizedString(@"AltServer could not establish a secure connection to %@.", @""), deviceName]; + } + + case ALTServerConnectionErrorTimedOut: + { + NSString *deviceName = self.userInfo[ALTDeviceNameErrorKey] ?: NSLocalizedString(@"the device", @""); + return [NSString stringWithFormat:NSLocalizedString(@"AltServer's connection to %@ timed out.", @""), deviceName]; + } + } + + return nil; +} + +- (nullable NSString *)altserver_connection_localizedRecoverySuggestion +{ + switch ((ALTServerConnectionError)self.code) + { + case ALTServerConnectionErrorDeviceLocked: + { + return NSLocalizedString(@"Please unlock the device with your passcode and try again.", @""); + } + + case ALTServerConnectionErrorUnknown: + case ALTServerConnectionErrorInvalidRequest: + case ALTServerConnectionErrorInvalidResponse: + case ALTServerConnectionErrorUsbmuxd: + case ALTServerConnectionErrorSSL: + case ALTServerConnectionErrorTimedOut: + { + return nil; + } + } +} @end diff --git a/Shared/Extensions/ALTServerError+Conveniences.swift b/Shared/Extensions/ALTServerError+Conveniences.swift index 71cfe623..4e1b2e97 100644 --- a/Shared/Extensions/ALTServerError+Conveniences.swift +++ b/Shared/Extensions/ALTServerError+Conveniences.swift @@ -15,6 +15,7 @@ public extension ALTServerError switch error { case let error as ALTServerError: self = error + case let error as ALTServerConnectionError: self = ALTServerError(.connectionFailed, underlyingError: error) case is DecodingError: self = ALTServerError(.invalidRequest, underlyingError: error) case is EncodingError: self = ALTServerError(.invalidResponse, underlyingError: error) case let error as NSError: