diff --git a/AltStore/AppDelegate.swift b/AltStore/AppDelegate.swift index 34e1b073..64d08e7c 100644 --- a/AltStore/AppDelegate.swift +++ b/AltStore/AppDelegate.swift @@ -61,6 +61,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { // Register default settings before doing anything else. UserDefaults.registerDefaults() + + DatabaseManager.shared.start { (error) in if let error = error { diff --git a/AltStore/LaunchViewController.swift b/AltStore/LaunchViewController.swift index e5544c96..fd932a6a 100644 --- a/AltStore/LaunchViewController.swift +++ b/AltStore/LaunchViewController.swift @@ -49,6 +49,43 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) + if #available(iOS 17, *), !UserDefaults.standard.sidejitenable { + DispatchQueue.global().async { + self.isSideJITServerDetected() { result in + DispatchQueue.main.async { + switch result { + case .success(): + let dialogMessage = UIAlertController(title: "SideJITServer Detected", message: "Would you like to enable SideJITServer", preferredStyle: .alert) + + // Create OK button with action handler + let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in + UserDefaults.standard.sidejitenable = true + }) + + let cancel = UIAlertAction(title: "Cancel", style: .cancel) + //Add OK button to a dialog message + dialogMessage.addAction(ok) + dialogMessage.addAction(cancel) + + // Present Alert to + self.present(dialogMessage, animated: true, completion: nil) + case .failure(_): + print("Cannot find sideJITServer") + } + } + } + } + } + + if #available(iOS 17, *), UserDefaults.standard.sidejitenable { + DispatchQueue.global().async { + self.askfornetwork() + } + print("SideJITServer Enabled") + } + + + #if !targetEnvironment(simulator) start_em_proxy(bind_addr: Consts.Proxy.serverURL) @@ -60,6 +97,46 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg #endif } + func askfornetwork() { + let address = UserDefaults.standard.textInputSideJITServerurl ?? "" + + var SJSURL = address + + if (UserDefaults.standard.textInputSideJITServerurl ?? "").isEmpty { + SJSURL = "http://sidejitserver._http._tcp.local:8080" + } + + // Create a network operation at launch to Refresh SideJITServer + let url = URL(string: "\(SJSURL)/re/")! + let task = URLSession.shared.dataTask(with: url) { (data, response, error) in + print(data) + } + task.resume() + } + + func isSideJITServerDetected(completion: @escaping (Result) -> Void) { + let address = UserDefaults.standard.textInputSideJITServerurl ?? "" + + var SJSURL = address + + if (UserDefaults.standard.textInputSideJITServerurl ?? "").isEmpty { + SJSURL = "http://sidejitserver._http._tcp.local:8080" + } + + // Create a network operation at launch to Refresh SideJITServer + let url = URL(string: SJSURL)! + let task = URLSession.shared.dataTask(with: url) { (data, response, error) in + if let error = error { + print("No SideJITServer on Network") + completion(.failure(error)) + return + } + completion(.success(())) + } + task.resume() + return + } + func fetchPairingFile() -> String? { let filename = "ALTPairingFile.mobiledevicepairing" let fm = FileManager.default diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift index a5eac9ac..61a12c99 100644 --- a/AltStore/My Apps/MyAppsViewController.swift +++ b/AltStore/My Apps/MyAppsViewController.swift @@ -1350,16 +1350,17 @@ private extension MyAppsViewController @available(iOS 14, *) func enableJIT(for installedApp: InstalledApp) { - if #available(iOS 17, *) { + if #available(iOS 17, *), !UserDefaults.standard.sidejitenable { let toastView = ToastView(error: OperationError.tooNewError) toastView.show(in: self) return } - if !minimuxer.ready() { + if #unavailable(iOS 17), !minimuxer.ready() { let toastView = ToastView(error: MinimuxerError.NoConnection) toastView.show(in: self) return } + AppManager.shared.enableJIT(for: installedApp) { result in DispatchQueue.main.async { switch result diff --git a/AltStore/Operations/BackgroundRefreshAppsOperation.swift b/AltStore/Operations/BackgroundRefreshAppsOperation.swift index ccf596f5..88a714a1 100644 --- a/AltStore/Operations/BackgroundRefreshAppsOperation.swift +++ b/AltStore/Operations/BackgroundRefreshAppsOperation.swift @@ -107,8 +107,7 @@ final class BackgroundRefreshAppsOperation: ResultOperation<[String: Result: ResultOperation } guard let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) } - - installedApp.managedObjectContext?.perform { - var retries = 3 - while (retries > 0){ - do { - try debug_app(installedApp.resignedBundleIdentifier) - self.finish(.success(())) - retries = 0 - } catch { - retries -= 1 - if (retries <= 0){ - self.finish(.failure(error)) + if #available(iOS 17, *) { + let sideJITenabled = UserDefaults.standard.sidejitenable + let SideJITIP = UserDefaults.standard.textInputSideJITServerurl ?? "" + + if sideJITenabled { + installedApp.managedObjectContext?.perform { + EnableJITSideJITServer(serverurl: SideJITIP, installedapp: installedApp) { result in + switch result { + case .failure(let error): + switch error { + case .invalidURL: + self.finish(.failure(OperationError.unabletoconnectSideJIT)) + case .errorConnecting: + self.finish(.failure(OperationError.unabletoconnectSideJIT)) + case .deviceNotFound: + self.finish(.failure(OperationError.unabletoconSideJITDevice)) + case .other(let message): + if let startRange = message.range(of: "

"), + let endRange = message.range(of: "

", range: startRange.upperBound.. 0){ + do { + try debug_app(installedApp.resignedBundleIdentifier) + self.finish(.success(())) + retries = 0 + } catch { + retries -= 1 + if (retries <= 0){ + self.finish(.failure(error)) + } } } } } } } + +@available(iOS 17, *) +func EnableJITSideJITServer(serverurl: String, installedapp: InstalledApp, completion: @escaping (Result) -> Void) { + guard let udid = fetch_udid()?.toString() else { + completion(.failure(.other("Unable to get UDID"))) + return + } + + var SJSURL = serverurl + + if (UserDefaults.standard.textInputSideJITServerurl ?? "").isEmpty { + SJSURL = "http://sidejitserver._http._tcp.local:8080" + } + + if !SJSURL.hasPrefix("http") { + completion(.failure(.invalidURL)) + return + } + + let fullurl = SJSURL + "/\(udid)/" + installedapp.resignedBundleIdentifier + + let url = URL(string: fullurl)! + + let task = URLSession.shared.dataTask(with: url) {(data, response, error) in + if let error = error { + completion(.failure(.errorConnecting)) + return + } + + guard let data = data, let datastring = String(data: data, encoding: .utf8) else { return } + + if datastring == "Enabled JIT for '\(installedapp.name)'!" { + let content = UNMutableNotificationContent() + content.title = "JIT Successfully Enabled" + content.subtitle = "JIT Enabled For \(installedapp.name)" + content.sound = UNNotificationSound.default + + let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false) + let request = UNNotificationRequest(identifier: "EnabledJIT", content: content, trigger: nil) + + UNUserNotificationCenter.current().add(request) + completion(.success(())) + } else { + let errorType: SideJITServerErrorType = datastring == "Could not find device!" ? .deviceNotFound : .other(datastring) + completion(.failure(errorType)) + } + } + + task.resume() +} diff --git a/AltStore/Operations/OperationError.swift b/AltStore/Operations/OperationError.swift index 4f33f6ed..d05ea749 100644 --- a/AltStore/Operations/OperationError.swift +++ b/AltStore/Operations/OperationError.swift @@ -15,6 +15,11 @@ enum OperationError: LocalizedError static let domain = OperationError.unknown._domain case unknown + case unabletoconnectSideJIT + case unabletoconSideJITDevice + case wrongIP + case SideJITIssue(error: String) + case refreshsidejit case unknownResult case cancelled case timedOut @@ -58,11 +63,16 @@ enum OperationError: LocalizedError case .missingAppGroup: return NSLocalizedString("SideStore's shared app group could not be found.", comment: "") case .maximumAppIDLimitReached: return NSLocalizedString("Cannot register more than 10 App IDs.", comment: "") case .noWiFi: return NSLocalizedString("You do not appear to be connected to WiFi!\nSideStore will never be able to install or refresh applications without WiFi.", comment: "") - case .tooNewError: return NSLocalizedString("iOS 17 has changed how JIT is enabled therefore SideStore cannot enable it at this time, sorry for any inconvenience.\nWe will let everyone know once we have a solution!", comment: "") + 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 .unabletoconSideJITDevice: 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 .wrongIP: return NSLocalizedString("Incorrect SideJITServer IP Please make sure that you are on the Samw Wifi as SideJITServer", comment: "") + case .refreshsidejit: return NSLocalizedString("Unable to find App Please try Refreshing SideJITServer from Settings", comment: "") + case .tooNewError: return NSLocalizedString("iOS 17 has changed how JIT is enabled therefore SideStore cannot enable it without the use of SideJITServer at this time, sorry for any inconvenience.\nWe will let everyone know once we have a solution!", comment: "") case .anisetteV1Error(let message): return String(format: NSLocalizedString("An error occurred when getting anisette data from a V1 server: %@. Try using another anisette server.", comment: ""), message) case .provisioningError(let result, let message): return String(format: NSLocalizedString("An error occurred when provisioning: %@%@. Please try again. If the issue persists, report it on GitHub Issues!", comment: ""), result, message != nil ? (" (" + message! + ")") : "") case .anisetteV3Error(let message): return String(format: 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: ""), message) case .cacheClearError(let errors): return String(format: NSLocalizedString("An error occurred while clearing cache: %@", comment: ""), errors.joined(separator: "\n")) + case .SideJITIssue(let errors): return NSLocalizedString("SideJITServer Error: \(errors)", comment: "") } } diff --git a/AltStore/Settings.bundle/Root.plist b/AltStore/Settings.bundle/Root.plist index 0be05152..026148e0 100644 --- a/AltStore/Settings.bundle/Root.plist +++ b/AltStore/Settings.bundle/Root.plist @@ -72,6 +72,40 @@ KeyboardType URL + + Type + PSGroupSpecifier + Title + SideJITServer + FooterText + If you disable the toggle then the app will not be able to access and use SideJITServer on iOS 17+. "SideJITServer IP" is not needed but is recommended for stability. + + + Type + PSToggleSwitchSpecifier + Title + Enable SideJIT Support + Key + sidejitenable + DefaultValue + + FooterText + chicken + + + Type + PSTextFieldSpecifier + Title + SideJITServer IP + Key + textInputSideJITServerurl + AutocapitalizationType + None + AutocorrectionType + No + KeyboardType + URL + diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard index 321a6280..02c8f716 100644 --- a/AltStore/Settings/Settings.storyboard +++ b/AltStore/Settings/Settings.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,7 +21,7 @@ diff --git a/AltStore/Settings/SettingsViewController.swift b/AltStore/Settings/SettingsViewController.swift index bbffa483..00822e50 100644 --- a/AltStore/Settings/SettingsViewController.swift +++ b/AltStore/Settings/SettingsViewController.swift @@ -54,6 +54,7 @@ extension SettingsViewController case sendFeedback case refreshAttempts case errorLog + case refreshSideJITServer case clearCache case resetPairingFile case resetAdiPb @@ -78,6 +79,8 @@ final class SettingsViewController: UITableViewController @IBOutlet private var backgroundRefreshSwitch: UISwitch! @IBOutlet private var noIdleTimeoutSwitch: UISwitch! + @IBOutlet private var refreshSideJITServer: UILabel! + @IBOutlet private var versionLabel: UILabel! override var preferredStatusBarStyle: UIStatusBarStyle { @@ -581,8 +584,64 @@ extension SettingsViewController toastView.show(in: self) } - case .clearCache: self.clearCache() + case .refreshSideJITServer: + if #available(iOS 17, *) { + let alertController = UIAlertController( + title: NSLocalizedString("Are you sure to Refresh SideJITServer?", comment: ""), + message: NSLocalizedString("if you do not have SideJITServer setup this will do nothing", comment: ""), + preferredStyle: UIAlertController.Style.actionSheet) + alertController.addAction(UIAlertAction(title: NSLocalizedString("Refresh", comment: ""), style: .destructive){ _ in + if UserDefaults.standard.sidejitenable { + var SJSURL = "" + if (UserDefaults.standard.textInputSideJITServerurl ?? "").isEmpty { + SJSURL = "http://sidejitserver._http._tcp.local:8080" + } else { + SJSURL = UserDefaults.standard.textInputSideJITServerurl ?? "" + } // replace with your URL + + + let url = URL(string: SJSURL + "/re/")! + + let task = URLSession.shared.dataTask(with: url) { (data, response, error) in + if let error = error { + print("Error: \(error)") + } else { + // Do nothing with data or response + } + } + + task.resume() + } + }) + + alertController.addAction(.cancel) + //Fix crash on iPad + alertController.popoverPresentationController?.sourceView = self.tableView + alertController.popoverPresentationController?.sourceRect = self.tableView.rectForRow(at: indexPath) + self.present(alertController, animated: true) + self.tableView.deselectRow(at: indexPath, animated: true) + } else { + let alertController = UIAlertController( + title: NSLocalizedString("You are not on iOS 17+ This will not work", comment: ""), + message: NSLocalizedString("This is meant for 'SideJITServer' and it only works on iOS 17+ ", comment: ""), + preferredStyle: UIAlertController.Style.actionSheet) + + alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .destructive){ _ in + print("Not on iOS 17") + }) + + alertController.addAction(.cancel) + //Fix crash on iPad + alertController.popoverPresentationController?.sourceView = self.tableView + alertController.popoverPresentationController?.sourceRect = self.tableView.rectForRow(at: indexPath) + self.present(alertController, animated: true) + self.tableView.deselectRow(at: indexPath, animated: true) + } + + + case .clearCache: self.clearCache() + case .resetPairingFile: let filename = "ALTPairingFile.mobiledevicepairing" let fm = FileManager.default diff --git a/AltStoreCore/Extensions/UserDefaults+AltStore.swift b/AltStoreCore/Extensions/UserDefaults+AltStore.swift index 96b2a5eb..a08f5437 100644 --- a/AltStoreCore/Extensions/UserDefaults+AltStore.swift +++ b/AltStoreCore/Extensions/UserDefaults+AltStore.swift @@ -22,6 +22,8 @@ public extension UserDefaults @NSManaged var firstLaunch: Date? @NSManaged var requiresAppGroupMigration: Bool @NSManaged var textServer: Bool + @NSManaged var sidejitenable: Bool + @NSManaged var textInputSideJITServerurl: String? @NSManaged var textInputAnisetteURL: String? @NSManaged var customAnisetteURL: String? @NSManaged var preferredServerID: String?