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 )
2025-07-18 19:24:55 -06:00
case . noWiFi : return NSLocalizedString ( " You do not appear to be connected to Wi-Fi and/or StosVPN! \n SideStore will never be able to install or refresh applications without Wi-Fi and StosVPN. " , comment : " " )
case . tooNewError : return NSLocalizedString ( " iOS 17 has changed how JIT is enabled, therefore, SideStore cannot enable JIT without SideJITServer, StikDebug, or SideStore-nightly at this time, sorry for any inconvenience. " , comment : " " )
case . unableToConnectSideJIT : return NSLocalizedString ( " Unable to connect to SideJITServer. Please check that you are on the same Wi-Fi of and your Firewall has been set correctly on your server " , comment : " " )
case . unableToRespondSideJITDevice : return NSLocalizedString ( " SideJITServer is unable to connect to your iDevice. Please make sure you have paired your Device by running 'SideJITServer -y', or try refreshing SideJITServer from Settings " , comment : " " )
case . wrongSideJITIP : return NSLocalizedString ( " Incorrect SideJITServer IP. Please make sure that you are on the same Wi-Fi as SideJITServer " , comment : " " )
case . refreshsidejit : return NSLocalizedString ( " Unable to find app; Please try refreshing SideJITServer from Settings " , comment : " " )
case . anisetteV1Error : return NSLocalizedString ( " An error occurred while getting anisette data from a V1 server: %@. Try using another anisette server. " , comment : " " )
case . provisioningError : return NSLocalizedString ( " An error occurred while provisioning: %@ %@. Please try again. If the issue persists, report it on GitHub Issues! " , comment : " " )
case . anisetteV3Error : return NSLocalizedString ( " An error occurred while 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 the cache: %@ " , comment : " " )
2024-08-06 10:43:52 +09:00
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-29 02:03:17 +05:30
return String ( format : NSLocalizedString ( " Invalid parameters%@ " , comment : " " ) , message )
2025-01-13 07:20:44 +05:30
case . invalidOperationContext :
let message = self . _failureReason . map { " : \n \( $0 ) " } ? ? " . "
2025-01-29 02:03:17 +05:30
return String ( format : NSLocalizedString ( " Invalid Operation Context%@ " , 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
{
2025-07-18 19:24:55 -06:00
case . noWiFi : return NSLocalizedString ( " Make sure StosVPN is toggled on and you are connected to any Wi-Fi 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 :
2025-07-18 19:24:55 -06:00
return NSLocalizedString ( " Unable to connect to the device, make sure StosVPN is enabled and you're connected to Wi-Fi. This could mean an invalid pairing. " , comment : " " )
2023-04-11 21:04:07 -07:00
case . PairingFile :
2025-05-24 19:04:25 -06:00
return NSLocalizedString ( " Invalid pairing file. Your pairing file either didn't have a UDID, or it wasn't a valid plist. Please use idevice_pair to regenerate it " , comment : " " )
2023-04-11 21:04:07 -07:00
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 : " " )
2025-04-14 10:54:56 -06:00
case . CreateLockdown :
return NSLocalizedString ( " Unable to connect to lockdown " , comment : " " )
case . CreateCoreDevice :
return NSLocalizedString ( " Unable to connect to core device proxy " , comment : " " )
case . CreateSoftwareTunnel :
return NSLocalizedString ( " Unable to create software tunnel " , comment : " " )
case . CreateRemoteServer :
return NSLocalizedString ( " Unable to connect to remote server " , comment : " " )
case . CreateProcessControl :
return NSLocalizedString ( " Unable to connect to process control " , comment : " " )
case . GetLockdownValue :
return NSLocalizedString ( " Unable to get value from lockdown " , comment : " " )
case . Connect :
return NSLocalizedString ( " Unable to connect to TCP port " , comment : " " )
case . Close :
return NSLocalizedString ( " Unable to close TCP port " , comment : " " )
case . XpcHandshake :
return NSLocalizedString ( " Unable to get services from XPC " , comment : " " )
case . NoService :
return NSLocalizedString ( " Device did not contain service " , comment : " " )
case . InvalidProductVersion :
return NSLocalizedString ( " Service version was in an unexpected format " , comment : " " )
case . CreateFolder :
return NSLocalizedString ( " Unable to create DDI folder " , comment : " " )
case . DownloadImage :
return NSLocalizedString ( " Unable to download DDI " , comment : " " )
case . ImageLookup :
return NSLocalizedString ( " Unable to lookup DDI images " , comment : " " )
case . ImageRead :
return NSLocalizedString ( " Unable to read images to memory " , comment : " " )
case . Mount :
return NSLocalizedString ( " Mount failed " , comment : " " )
2023-04-11 21:04:07 -07:00
}
}
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
}
}