2019-06-10 15:03:47 -07:00
//
// O p e r a t i o n E r r o r . s w i f t
// A l t S t o r e
//
// C r e a t e d b y R i l e y T e s t u t o n 6 / 7 / 1 9 .
// C o p y r i g h t © 2 0 1 9 R i l e y T e s t u t . A l l r i g h t s r e s e r v e d .
//
import Foundation
2019-07-25 14:04:26 -07:00
import AltSign
2024-08-06 10:43:52 +09:00
import AltStoreCore
2023-04-01 16:02:12 -07:00
import minimuxer
2019-06-10 15:03:47 -07:00
2024-08-06 10:43:52 +09:00
extension OperationError
2019-06-10 15:03:47 -07:00
{
2024-08-06 10:43:52 +09:00
enum Code : Int , ALTErrorCode , CaseIterable {
typealias Error = OperationError
2024-12-07 17:45:09 +05:30
2024-08-06 10:43:52 +09:00
// G e n e r a l
case unknown = 1000
2023-04-04 17:25:18 -05:00
case unknownResult = 1001
2024-12-08 02:50:40 +05:30
// c a s e c a n c e l l e d = 1 0 0 2
2023-04-04 17:25:18 -05:00
case timedOut = 1003
case notAuthenticated = 1004
case appNotFound = 1005
case unknownUDID = 1006
case invalidApp = 1007
case invalidParameters = 1008
case maximumAppIDLimitReached = 1009
case noSources = 1010
case openAppFailed = 1011
case missingAppGroup = 1012
case forbidden = 1013
2024-01-23 16:43:05 -06:00
case sourceNotAdded = 1014
2024-12-07 17:45:09 +05:30
// C o n n e c t i o n
2023-04-04 16:55:55 -05:00
/* C o n n e c t i o n */
case serverNotFound = 1200
2023-04-04 17:25:18 -05:00
case connectionFailed = 1201
case connectionDropped = 1202
2023-11-30 14:28:57 -06:00
/* P l e d g e s */
case pledgeRequired = 1401
case pledgeInactive = 1402
2024-12-07 17:45:09 +05:30
/* S i d e S t o r e O n l y */
case unableToConnectSideJIT
case unableToRespondSideJITDevice
case wrongSideJITIP
case SideJITIssue // ( e r r o r : S t r i n g )
case refreshsidejit
case refreshAppFailed
case tooNewError
case anisetteV1Error // ( m e s s a g e : S t r i n g )
case provisioningError // ( r e s u l t : S t r i n g , m e s s a g e : S t r i n g ? )
case anisetteV3Error // ( m e s s a g e : S t r i n g )
case cacheClearError // ( e r r o r s : [ S t r i n g ] )
case noWiFi
2025-01-13 07:20:44 +05:30
case invalidOperationContext
2024-08-06 10:43:52 +09:00
}
2023-05-11 17:15:33 -05:00
static var cancelled : CancellationError { CancellationError ( ) }
2024-08-06 10:43:52 +09:00
static let unknownResult : OperationError = . init ( code : . unknownResult )
static let timedOut : OperationError = . init ( code : . timedOut )
static let unableToConnectSideJIT : OperationError = . init ( code : . unableToConnectSideJIT )
static let unableToRespondSideJITDevice : OperationError = . init ( code : . unableToRespondSideJITDevice )
static let wrongSideJITIP : OperationError = . init ( code : . wrongSideJITIP )
static let notAuthenticated : OperationError = . init ( code : . notAuthenticated )
static let unknownUDID : OperationError = . init ( code : . unknownUDID )
static let invalidApp : OperationError = . init ( code : . invalidApp )
static let noSources : OperationError = . init ( code : . noSources )
static let missingAppGroup : OperationError = . init ( code : . missingAppGroup )
2024-12-07 17:45:09 +05:30
2024-08-06 10:43:52 +09:00
static let noWiFi : OperationError = . init ( code : . noWiFi )
static let tooNewError : OperationError = . init ( code : . tooNewError )
static let provisioningError : OperationError = . init ( code : . provisioningError )
static let anisetteV1Error : OperationError = . init ( code : . anisetteV1Error )
static let anisetteV3Error : OperationError = . init ( code : . anisetteV3Error )
2024-12-07 17:45:09 +05:30
2024-08-06 10:43:52 +09:00
static let cacheClearError : OperationError = . init ( code : . cacheClearError )
static func unknown ( failureReason : String ? = nil , file : String = # fileID , line : UInt = #line ) -> OperationError {
OperationError ( code : . unknown , failureReason : failureReason , sourceFile : file , sourceLine : line )
}
static func appNotFound ( name : String ? ) -> OperationError {
OperationError ( code : . appNotFound , appName : name )
}
static func openAppFailed ( name : String ? ) -> OperationError {
OperationError ( code : . openAppFailed , appName : name )
}
2024-11-10 02:54:18 +05:30
static let domain = OperationError ( code : . unknown ) . _domain
2023-05-18 01:30:18 -07:00
2024-08-06 10:43:52 +09:00
static func SideJITIssue ( error : String ? ) -> OperationError {
var o = OperationError ( code : . SideJITIssue )
o . errorFailure = error
return o
}
2023-11-26 13:50:45 +09:00
2024-08-06 10:43:52 +09:00
static func maximumAppIDLimitReached ( appName : String , requiredAppIDs : Int , availableAppIDs : Int , expirationDate : Date ) -> OperationError {
OperationError ( code : . maximumAppIDLimitReached , appName : appName , requiredAppIDs : requiredAppIDs , availableAppIDs : availableAppIDs , expirationDate : expirationDate )
}
static func provisioningError ( result : String , message : String ? ) -> OperationError {
var o = OperationError ( code : . provisioningError , failureReason : result )
o . errorTitle = message
return o
}
static func cacheClearError ( errors : [ String ] ) -> OperationError {
OperationError ( code : . cacheClearError , failureReason : errors . joined ( separator : " \n " ) )
}
static func anisetteV1Error ( message : String ) -> OperationError {
OperationError ( code : . anisetteV1Error , failureReason : message )
}
static func anisetteV3Error ( message : String ) -> OperationError {
OperationError ( code : . anisetteV3Error , failureReason : message )
}
2024-11-04 14:35:13 +05:30
static func refreshAppFailed ( message : String ) -> OperationError {
OperationError ( code : . refreshAppFailed , failureReason : message )
}
2024-11-09 14:35:18 +05:30
static func invalidParameters ( _ message : String ? = nil ) -> OperationError {
OperationError ( code : . invalidParameters , failureReason : message )
2024-12-07 17:45:09 +05:30
}
2023-04-04 16:55:55 -05:00
2025-01-13 07:20:44 +05:30
static func invalidOperationContext ( _ message : String ? = nil ) -> OperationError {
OperationError ( code : . invalidOperationContext , failureReason : message )
}
2023-04-04 16:55:55 -05:00
static func forbidden ( failureReason : String ? = nil , file : String = # fileID , line : UInt = #line ) -> OperationError {
OperationError ( code : . forbidden , failureReason : failureReason , sourceFile : file , sourceLine : line )
2024-11-09 14:35:18 +05:30
}
2023-11-30 14:28:57 -06:00
2024-01-23 16:43:05 -06:00
static func sourceNotAdded ( @ Managed _ source : Source , file : String = # fileID , line : UInt = #line ) -> OperationError {
OperationError ( code : . sourceNotAdded , sourceName : $ source . name , sourceFile : file , sourceLine : line )
}
2023-11-30 14:28:57 -06:00
static func pledgeRequired ( appName : String , file : String = # fileID , line : UInt = #line ) -> OperationError {
OperationError ( code : . pledgeRequired , appName : appName , sourceFile : file , sourceLine : line )
}
static func pledgeInactive ( appName : String , file : String = # fileID , line : UInt = #line ) -> OperationError {
OperationError ( code : . pledgeInactive , appName : appName , sourceFile : file , sourceLine : line )
}
2024-08-06 10:43:52 +09:00
}
struct OperationError : ALTLocalizedError {
let code : Code
var errorTitle : String ?
var errorFailure : String ?
2024-01-23 16:43:05 -06:00
@ UserInfoValue
2024-08-06 10:43:52 +09:00
var appName : String ?
2024-01-23 16:43:05 -06:00
@ UserInfoValue
var sourceName : String ?
2024-08-06 10:43:52 +09:00
var requiredAppIDs : Int ?
var availableAppIDs : Int ?
var expirationDate : Date ?
var sourceFile : String ?
var sourceLine : UInt ?
private var _failureReason : String ?
private init ( code : Code , failureReason : String ? = nil ,
2024-12-07 17:45:09 +05:30
appName : String ? = nil , sourceName : String ? = nil , requiredAppIDs : Int ? = nil ,
availableAppIDs : Int ? = nil , expirationDate : Date ? = nil , sourceFile : String ? = nil , sourceLine : UInt ? = nil ) {
2024-08-06 10:43:52 +09:00
self . code = code
self . _failureReason = failureReason
self . appName = appName
2024-01-23 16:43:05 -06:00
self . sourceName = sourceName
2024-08-06 10:43:52 +09:00
self . requiredAppIDs = requiredAppIDs
self . availableAppIDs = availableAppIDs
self . expirationDate = expirationDate
self . sourceFile = sourceFile
self . sourceLine = sourceLine
}
var errorFailureReason : String {
switch self . code {
case . unknown :
var failureReason = self . _failureReason ? ? NSLocalizedString ( " An unknown error occurred. " , comment : " " )
guard let sourceFile , let sourceLine else { return failureReason }
failureReason += " ( \( sourceFile ) line \( sourceLine ) "
return failureReason
2019-06-10 15:03:47 -07:00
case . unknownResult : return NSLocalizedString ( " The operation returned an unknown result. " , comment : " " )
2020-05-15 15:11:17 -07:00
case . timedOut : return NSLocalizedString ( " The operation timed out. " , comment : " " )
2019-06-10 15:03:47 -07:00
case . notAuthenticated : return NSLocalizedString ( " You are not signed in. " , comment : " " )
2024-08-06 10:43:52 +09:00
case . unknownUDID : return NSLocalizedString ( " SideStore could not determine this device's UDID. " , comment : " " )
case . invalidApp : return NSLocalizedString ( " The app is in an invalid format. " , comment : " " )
case . maximumAppIDLimitReached : return NSLocalizedString ( " Cannot register more than 10 App IDs within a 7 day period. " , comment : " " )
2022-11-05 23:50:07 -07:00
case . noSources : return NSLocalizedString ( " There are no SideStore sources. " , comment : " " )
2024-08-06 10:43:52 +09:00
case . missingAppGroup : return NSLocalizedString ( " SideStore's shared app group could not be accessed. " , comment : " " )
2023-04-04 16:55:55 -05:00
case . forbidden :
guard let failureReason = self . _failureReason else { return NSLocalizedString ( " The operation is forbidden. " , comment : " " ) }
return failureReason
2024-01-23 16:43:05 -06:00
case . sourceNotAdded :
let sourceName = self . sourceName . map { String ( format : NSLocalizedString ( " The source “%@” " , comment : " " ) , $0 ) } ? ? NSLocalizedString ( " The source " , comment : " " )
2024-12-14 18:23:33 -05:00
return String ( format : NSLocalizedString ( " %@ is not added to SideStore. " , comment : " " ) , sourceName )
2023-04-04 16:55:55 -05:00
2024-08-06 10:43:52 +09:00
case . appNotFound :
let appName = self . appName ? ? NSLocalizedString ( " The app " , comment : " " )
return String ( format : NSLocalizedString ( " %@ could not be found. " , comment : " " ) , appName )
case . openAppFailed :
let appName = self . appName ? ? NSLocalizedString ( " The app " , comment : " " )
return String ( format : NSLocalizedString ( " SideStore was denied permission to launch %@. " , comment : " " ) , appName )
case . noWiFi : return NSLocalizedString ( " You do not appear to be connected to WiFi and/or the WireGuard VPN! \n SideStore will never be able to install or refresh applications without WiFi and the WireGuard VPN. " , comment : " " )
2024-08-15 09:58:26 +10:00
case . tooNewError : return NSLocalizedString ( " iOS 17 has changed how JIT is enabled therefore SideStore cannot enable it without SideJITServer at this time, sorry for any inconvenience. \n We will let everyone know once we have a solution! " , comment : " " )
2024-08-06 10:43:52 +09:00
case . unableToConnectSideJIT : return NSLocalizedString ( " Unable to connect to SideJITServer Please check that you are on the Same Wi-Fi and your Firewall has been set correctly " , comment : " " )
case . unableToRespondSideJITDevice : return NSLocalizedString ( " SideJITServer is unable to connect to your iDevice Please make sure you have paired your Device by doing 'SideJITServer -y' or try Refreshing SideJITServer from Settings " , comment : " " )
case . wrongSideJITIP : return NSLocalizedString ( " Incorrect SideJITServer IP Please make sure that you are on the Samw Wifi as SideJITServer " , comment : " " )
2024-06-17 09:43:25 +10:00
case . refreshsidejit : return NSLocalizedString ( " Unable to find App Please try Refreshing SideJITServer from Settings " , comment : " " )
2024-08-06 10:43:52 +09:00
case . anisetteV1Error : return NSLocalizedString ( " An error occurred when getting anisette data from a V1 server: %@. Try using another anisette server. " , comment : " " )
case . provisioningError : return NSLocalizedString ( " An error occurred when provisioning: %@ %@. Please try again. If the issue persists, report it on GitHub Issues! " , comment : " " )
case . anisetteV3Error : return NSLocalizedString ( " An error occurred when getting anisette data from a V3 server: %@. Please try again. If the issue persists, report it on GitHub Issues! " , comment : " " )
case . cacheClearError : return NSLocalizedString ( " An error occurred while clearing cache: %@ " , comment : " " )
case . SideJITIssue : return NSLocalizedString ( " An error occurred while using SideJIT: %@ " , comment : " " )
2024-11-04 14:35:13 +05:30
case . refreshAppFailed :
let message = self . _failureReason ? ? " "
return String ( format : NSLocalizedString ( " Unable to refresh App \n %@ " , comment : " " ) , message )
2024-11-09 14:35:18 +05:30
case . invalidParameters :
let message = self . _failureReason . map { " : \n \( $0 ) " } ? ? " . "
2025-01-13 07:20:44 +05:30
return String ( format : NSLocalizedString ( " Invalid parameters \n %@ " , comment : " " ) , message )
case . invalidOperationContext :
let message = self . _failureReason . map { " : \n \( $0 ) " } ? ? " . "
return String ( format : NSLocalizedString ( " Invalid Operation Context \n %@ " , comment : " " ) , message )
2023-11-30 14:28:57 -06:00
case . serverNotFound : return NSLocalizedString ( " AltServer could not be found. " , comment : " " )
case . connectionFailed : return NSLocalizedString ( " A connection to AltServer could not be established. " , comment : " " )
case . connectionDropped : return NSLocalizedString ( " The connection to AltServer was dropped. " , comment : " " )
case . pledgeRequired :
let appName = self . appName ? ? NSLocalizedString ( " This app " , comment : " " )
return String ( format : NSLocalizedString ( " %@ requires an active pledge in order to be installed. " , comment : " " ) , appName )
case . pledgeInactive :
let appName = self . appName ? ? NSLocalizedString ( " this app " , comment : " " )
return String ( format : NSLocalizedString ( " Your pledge is no longer active. Please renew it to continue using %@ normally. " , comment : " " ) , appName )
2020-01-24 14:14:08 -08:00
}
2024-11-09 14:35:18 +05:30
2020-01-24 14:14:08 -08:00
}
var recoverySuggestion : String ? {
2024-08-06 10:43:52 +09:00
switch self . code
2020-01-24 14:14:08 -08:00
{
2024-08-06 10:43:52 +09:00
case . noWiFi : return NSLocalizedString ( " Make sure the VPN is toggled on and you are connected to any WiFi network! " , comment : " " )
2024-01-23 14:21:37 -06:00
case . serverNotFound : return NSLocalizedString ( " Make sure you're on the same Wi-Fi network as a computer running AltServer, or try connecting this device to your computer via USB. " , comment : " " )
2024-08-06 10:43:52 +09:00
case . maximumAppIDLimitReached :
2020-02-10 16:30:54 -08:00
let baseMessage = NSLocalizedString ( " Delete sideloaded apps to free up App ID slots. " , comment : " " )
2024-08-06 10:43:52 +09:00
guard let appName , let requiredAppIDs , let availableAppIDs , let expirationDate else { return baseMessage }
var message : String
2020-02-10 16:30:54 -08:00
if requiredAppIDs > 1
{
let availableText : String
2020-01-24 14:14:08 -08:00
2020-02-10 16:30:54 -08:00
switch availableAppIDs
2020-01-24 14:14:08 -08:00
{
2020-02-10 16:30:54 -08:00
case 0 : availableText = NSLocalizedString ( " none are available " , comment : " " )
case 1 : availableText = NSLocalizedString ( " only 1 is available " , comment : " " )
default : availableText = String ( format : NSLocalizedString ( " only %@ are available " , comment : " " ) , NSNumber ( value : availableAppIDs ) )
2020-01-24 14:14:08 -08:00
}
2024-08-06 10:43:52 +09:00
let prefixMessage = String ( format : NSLocalizedString ( " %@ requires %@ App IDs, but %@. " , comment : " " ) , appName , NSNumber ( value : requiredAppIDs ) , availableText )
message = prefixMessage + " " + baseMessage + " \n \n "
2020-02-10 16:30:54 -08:00
}
else
{
2024-08-06 10:43:52 +09:00
message = baseMessage + " "
2020-01-24 14:14:08 -08:00
}
2024-08-06 10:43:52 +09:00
let dateComponents = Calendar . current . dateComponents ( [ . day , . hour , . minute ] , from : Date ( ) , to : expirationDate )
let dateFormatter = DateComponentsFormatter ( )
dateFormatter . maximumUnitCount = 1
dateFormatter . unitsStyle = . full
let remainingTime = dateFormatter . string ( from : dateComponents ) !
message += String ( format : NSLocalizedString ( " You can register another App ID in %@. " , comment : " " ) , remainingTime )
2020-01-24 14:14:08 -08:00
return message
default : return nil
2019-06-10 15:03:47 -07:00
}
}
}
2022-11-02 17:58:59 -07:00
2023-04-11 21:04:07 -07:00
extension MinimuxerError : LocalizedError {
public var failureReason : String ? {
switch self {
case . NoDevice :
return NSLocalizedString ( " Cannot fetch the device from the muxer " , comment : " " )
case . NoConnection :
2024-08-15 11:21:50 +09:00
return NSLocalizedString ( " Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi. This could mean an invalid pairing. " , comment : " " )
2023-04-11 21:04:07 -07:00
case . PairingFile :
return NSLocalizedString ( " Invalid pairing file. Your pairing file either didn't have a UDID, or it wasn't a valid plist. Please use jitterbugpair to generate it " , comment : " " )
case . CreateDebug :
return self . createService ( name : " debug " )
case . LookupApps :
return self . getFromDevice ( name : " installed apps " )
case . FindApp :
return self . getFromDevice ( name : " path to the app " )
case . BundlePath :
return self . getFromDevice ( name : " bundle path " )
case . MaxPacket :
return self . setArgument ( name : " max packet " )
case . WorkingDirectory :
return self . setArgument ( name : " working directory " )
case . Argv :
return self . setArgument ( name : " argv " )
case . LaunchSuccess :
return self . getFromDevice ( name : " launch success " )
case . Detach :
return NSLocalizedString ( " Unable to detach from the app's process " , comment : " " )
case . Attach :
return NSLocalizedString ( " Unable to attach to the app's process " , comment : " " )
case . CreateInstproxy :
return self . createService ( name : " instproxy " )
case . CreateAfc :
return self . createService ( name : " AFC " )
case . RwAfc :
2024-08-15 11:21:50 +09:00
return NSLocalizedString ( " AFC was unable to manage files on the device. This usually means an invalid pairing. " , comment : " " )
2023-09-17 10:37:49 -07:00
case . InstallApp ( let message ) :
return NSLocalizedString ( " Unable to install the app: \( message . toString ( ) ) " , comment : " " )
2023-04-11 21:04:07 -07:00
case . UninstallApp :
return NSLocalizedString ( " Unable to uninstall the app " , comment : " " )
case . CreateMisagent :
return self . createService ( name : " misagent " )
case . ProfileInstall :
return NSLocalizedString ( " Unable to manage profiles on the device " , comment : " " )
case . ProfileRemove :
return NSLocalizedString ( " Unable to manage profiles on the device " , comment : " " )
}
}
fileprivate func createService ( name : String ) -> String {
return String ( format : NSLocalizedString ( " Cannot start a %@ server on the device. " , comment : " " ) , name )
}
fileprivate func getFromDevice ( name : String ) -> String {
return String ( format : NSLocalizedString ( " Cannot fetch %@ from the device. " , comment : " " ) , name )
}
fileprivate func setArgument ( name : String ) -> String {
return String ( format : NSLocalizedString ( " Cannot set %@ on the device. " , comment : " " ) , name )
2022-11-02 17:58:59 -07:00
}
}