diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 4a46b7e7c..b19208bdb 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ B3C395F7284F362400DA9E2F /* AppCenterAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F6284F362400DA9E2F /* AppCenterAnalytics */; }; B3C395F9284F362400DA9E2F /* AppCenterCrashes in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F8284F362400DA9E2F /* AppCenterCrashes */; }; B3EE16B62925E27D00B3B1F5 /* AnisetteManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EE16B52925E27D00B3B1F5 /* AnisetteManager.swift */; }; + B5847002294F7811002A854E /* AnisetteServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5847001294F7811002A854E /* AnisetteServerViewController.swift */; }; BF02419622F2199300129732 /* RefreshAttemptsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02419522F2199300129732 /* RefreshAttemptsViewController.swift */; }; BF08858322DE795100DE9F1E /* MyAppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF08858222DE795100DE9F1E /* MyAppsViewController.swift */; }; BF08858522DE7EC800DE9F1E /* UpdateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF08858422DE7EC800DE9F1E /* UpdateCollectionViewCell.swift */; }; @@ -540,6 +541,7 @@ B3C3960E284F4F9100DA9E2F /* AltStoreCore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStoreCore.xcconfig; sourceTree = ""; }; B3C3960F284F53E900DA9E2F /* AltBackup.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltBackup.xcconfig; sourceTree = ""; }; B3EE16B52925E27D00B3B1F5 /* AnisetteManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnisetteManager.swift; sourceTree = ""; }; + B5847001294F7811002A854E /* AnisetteServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnisetteServerViewController.swift; sourceTree = ""; }; BF02419522F2199300129732 /* RefreshAttemptsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAttemptsViewController.swift; sourceTree = ""; }; BF08858222DE795100DE9F1E /* MyAppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewController.swift; sourceTree = ""; }; BF08858422DE7EC800DE9F1E /* UpdateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCollectionViewCell.swift; sourceTree = ""; }; @@ -1651,6 +1653,7 @@ BFE6073B231AE1E7002B0E8E /* SettingsHeaderFooterView.xib */, BF02419522F2199300129732 /* RefreshAttemptsViewController.swift */, BFF0B68D23219520007A79E1 /* PatreonViewController.swift */, + B5847001294F7811002A854E /* AnisetteServerViewController.swift */, BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */, BFF0B6912321A305007A79E1 /* AboutPatreonHeaderView.xib */, BFF0B695232242D3007A79E1 /* LicensesViewController.swift */, @@ -2460,6 +2463,7 @@ BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */, BFE00A202503097F00EB4D0C /* INInteraction+AltStore.swift in Sources */, BFDB6A0B22AAEDB7007EA6D6 /* Operation.swift in Sources */, + B5847002294F7811002A854E /* AnisetteServerViewController.swift in Sources */, BF770E6722BD57C4002A40FE /* BackgroundTaskManager.swift in Sources */, BF44EEFC246B4550002A52F2 /* RemoveAppOperation.swift in Sources */, BF3D64B022E8D4B800E9056B /* AppContentViewControllerCells.swift in Sources */, diff --git a/AltStore/Info.plist b/AltStore/Info.plist index 2b4b48de4..d62e895d6 100644 --- a/AltStore/Info.plist +++ b/AltStore/Info.plist @@ -2,6 +2,8 @@ + ALTAnisetteURL + http://ani.sidestore.io/ ALTAppGroups group.$(APP_GROUP_IDENTIFIER) @@ -9,12 +11,10 @@ ALTDeviceID 00008101-000129D63698001E - ALTServerID - 1F7D5B55-79CE-4546-A029-D4DDC4AF3B6D ALTPairingFile <insert pairing file here> - ALTAnisetteURL - https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx + ALTServerID + 1F7D5B55-79CE-4546-A029-D4DDC4AF3B6D CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDocumentTypes @@ -44,8 +44,6 @@ $(PRODUCT_NAME) CFBundlePackageType APPL - LSSupportsOpeningDocumentsInPlace - CFBundleShortVersionString $(MARKETING_VERSION) CFBundleURLTypes @@ -93,6 +91,11 @@ LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSBonjourServices _altserver._tcp @@ -133,11 +136,6 @@ UILaunchStoryboardName LaunchScreen - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - UIMainStoryboardFile Main UIRequiredDeviceCapabilities diff --git a/AltStore/Operations/FetchAnisetteDataOperation.swift b/AltStore/Operations/FetchAnisetteDataOperation.swift index 4a1217518..82d5b9b9e 100644 --- a/AltStore/Operations/FetchAnisetteDataOperation.swift +++ b/AltStore/Operations/FetchAnisetteDataOperation.swift @@ -32,30 +32,121 @@ final class FetchAnisetteDataOperation: ResultOperation return } - let url = AnisetteManager.currentURL - DLOG("Anisette URL: %@", url.absoluteString) + do { + let fm = FileManager.default + let documentsPath = fm.documentsDirectory.appendingPathComponent("adi.pb") + print("ADI Path: \(documentsPath)") + + if !fm.fileExists(atPath: documentsPath.absoluteString) { + self.fetchADIFile(session: URLSession.shared) + } + + let url = AnisetteManager.currentURL + DLOG("Anisette URL: %@", url.absoluteString) + + let session = URLSession.shared + + var postData = Data() + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("multipart/form-data; boundary=ebd46b494f8b3f6926db4f30a3f371ae", forHTTPHeaderField: "Content-Type") + + // Get the raw data from the file. + let rawData: Data = try Data(contentsOf: documentsPath) + DLOG("adi.pb exists") + postData.append("--ebd46b494f8b3f6926db4f30a3f371ae\r\n".data(using: .utf8)!) + postData.append("Content-Disposition: form-data; name=\"adi.pb\"; filename=\"adi.pb\"\r\n\r\n".data(using: .utf8)!) + for byte in rawData { + postData.append(byte) + } + postData.append("\r\n--ebd46b494f8b3f6926db4f30a3f371ae\r\n".data(using: .utf8)!) + let task = session.uploadTask(with: request, from: postData, completionHandler: { data, response, error in + if let data = data { + do { + print(data) + // make sure this JSON is in the format we expect + // convert data to json + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] { + // try to read out a dictionary + //for some reason serial number isn't needed but it doesn't work unless it has a value + let formattedJSON: [String: String] = ["machineID": json["X-Apple-I-MD-M"]!, "oneTimePassword": json["X-Apple-I-MD"]!, "localUserID": json["X-Apple-I-MD-LU"]!, "routingInfo": json["X-Apple-I-MD-RINFO"]!, "deviceUniqueIdentifier": json["X-Mme-Device-Id"]!, "deviceDescription": json["X-MMe-Client-Info"]!, "date": json["X-Apple-I-Client-Time"]!, "locale": json["X-Apple-Locale"]!, "timeZone": json["X-Apple-I-TimeZone"]!, "deviceSerialNumber": "1"] + + if let anisette = ALTAnisetteData(json: formattedJSON) { + self.finish(.success(anisette)) + } + } + } catch let error as NSError { + print("Failed to load: \(error.localizedDescription)") + self.finish(.failure(error)) + } + } + }) + task.resume() + } catch let error as NSError { + self.fetchADIFile(session: URLSession.shared) + return self.finish(.failure(error)) + + } + } + + func fetchADIFile(session: URLSession) { - let task = URLSession.shared.dataTask(with: url) { data, response, error in + let fm = FileManager.default + let documentsPath = fm.documentsDirectory.appendingPathComponent("adi.pb") + + print("ADI URL: \(AnisetteManager.currentURL.appendingPathComponent("adi_file").absoluteString)") + + let task = URLSession.shared.dataTask(with: AnisetteManager.currentURL.appendingPathComponent("adi_file")) { data, response, error in guard let data = data, error == nil else { return } do { - // make sure this JSON is in the format we expect - // convert data to json if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] { - // try to read out a dictionary - //for some reason serial number isn't needed but it doesn't work unless it has a value let formattedJSON: [String: String] = ["machineID": json["X-Apple-I-MD-M"]!, "oneTimePassword": json["X-Apple-I-MD"]!, "localUserID": json["X-Apple-I-MD-LU"]!, "routingInfo": json["X-Apple-I-MD-RINFO"]!, "deviceUniqueIdentifier": json["X-Mme-Device-Id"]!, "deviceDescription": json["X-MMe-Client-Info"]!, "date": json["X-Apple-I-Client-Time"]!, "locale": json["X-Apple-Locale"]!, "timeZone": json["X-Apple-I-TimeZone"]!, "deviceSerialNumber": "1"] - if let anisette = ALTAnisetteData(json: formattedJSON) { - DLOG("Anisette used: %@", formattedJSON) - self.finish(.success(anisette)) + DLOG("Found anisette data instead of adi.pb file, fallback initiated") + + if let trustedURL = UserDefaults.shared.trustedServerURL { + print(trustedURL) + print(AnisetteManager.currentURLString) + if trustedURL == AnisetteManager.currentURLString { + return self.finish(.success(anisette)) + } + } + + let alert = UIAlertController(title: "WARNING!", message: "We've detected you are using an older anisette server, using this server has a higher likelihood of locking your account, do you still want to continue?", preferredStyle: UIAlertController.Style.alert) + alert.addAction(UIAlertAction(title: "Continue", style: UIAlertAction.Style.destructive, handler: {action in + DLOG("Using older anisette method") + UserDefaults.shared.trustedServerURL = AnisetteManager.currentURLString + return self.finish(.success(anisette)) + })) + alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: {action in + DLOG("Cancelled the fallback operation") + return + })) + + let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first + + DispatchQueue.main.async { + if let presentingController = keyWindow?.rootViewController?.presentedViewController { + presentingController.present(alert, animated: true) + } else { + keyWindow?.rootViewController?.present(alert, animated: true) + } + } } } - } catch let error as NSError { - print("Failed to load: \(error.localizedDescription)") - self.finish(.failure(error)) + } catch _ as NSError { + do { + try data.write(to: documentsPath) + DLOG("Wrote adi.pb file") + return + } catch let error as NSError { + DLOG("ADI Write Error: %@", error.domain) + return self.finish(.failure(error)) + } + } - } task.resume() diff --git a/AltStore/Operations/InstallAppOperation.swift b/AltStore/Operations/InstallAppOperation.swift index f10c12725..d8e628ddf 100644 --- a/AltStore/Operations/InstallAppOperation.swift +++ b/AltStore/Operations/InstallAppOperation.swift @@ -198,6 +198,7 @@ private extension InstallAppOperation } catch { + self.didCleanUp = false print("Failed to remove temporary directory.", error) } } diff --git a/AltStore/Settings/AnisetteManager.swift b/AltStore/Settings/AnisetteManager.swift index 4edc46176..ff0eb8519 100644 --- a/AltStore/Settings/AnisetteManager.swift +++ b/AltStore/Settings/AnisetteManager.swift @@ -8,8 +8,29 @@ import Foundation +struct PublicAnisetteServer { + let label: String + let urlString: String +} + public struct AnisetteManager { + static var publicServers: [PublicAnisetteServer] { + // TODO: Pull these servers from somewhere, also let users edit list + return [ + PublicAnisetteServer(label: "Jawshoedan", urlString: "https://anisette.jawshoeadan.me"), + PublicAnisetteServer(label: "Buh", urlString: "http://191.101.206.188:6969"), + PublicAnisetteServer(label: "Macley (US)", urlString: "http://us1.sternserv.tech"), + PublicAnisetteServer(label: "Macley (DE)", urlString: "http://de1.sternserv.tech"), + PublicAnisetteServer(label: "DrPudding", urlString: "https://sign.rheaa.xyz"), + PublicAnisetteServer(label: "jkcoxson (AltServer)", urlString: "http://jkcoxson.com:2095"), + PublicAnisetteServer(label: "jkcoxson (Provision)", urlString: "http://jkcoxson.com:2052"), + PublicAnisetteServer(label: "Sideloadly", urlString: "https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx"), + PublicAnisetteServer(label: "Nythepegasus", urlString: "http://45.33.29.114"), + PublicAnisetteServer(label: "crystall1nedev", urlString: "https://anisette.crystall1ne.software/") + ] + } + /// User defined URL from Settings/UserDefaults static var userURL: String? { var urlString: String? diff --git a/AltStore/Settings/AnisetteServerViewController.swift b/AltStore/Settings/AnisetteServerViewController.swift new file mode 100644 index 000000000..404ba6681 --- /dev/null +++ b/AltStore/Settings/AnisetteServerViewController.swift @@ -0,0 +1,71 @@ +// +// AnisetteServerViewController.swift +// SideStore +// +// Created by Nicholas Yoder on 12/18/22. +// Copyright © 2022 SideStore. All rights reserved. +// + +import UIKit + +import AltStoreCore +import Roxas + + + +class AnisetteServerViewController: UITableViewController +{ + @IBOutlet var anisetteServers: UIButton! + override func viewDidLoad() + { + super.viewDidLoad() + +// self.prepareServers() + } + + override func viewWillAppear(_ animated: Bool) + { + super.viewWillAppear(animated) + } + + func prepareServers() -> UIMenu { + var menuItems: [UIAction] = [] + for server in AnisetteManager.publicServers { + let action = UIAction(title: server.label, identifier: UIAction.Identifier(server.urlString), handler: { (_) in }) + if action.title == "Sideloadly" { + action.attributes = .destructive + } + menuItems.append(action) + } + var anisetteMenu: UIMenu { + return UIMenu(title: "Anisette Servers", image: nil, identifier: nil, options: [], children: menuItems) + } + + return anisetteMenu + } +} + +extension AnisetteServerViewController +{ + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + self.tableView.deselectRow(at: indexPath, animated: true) + if indexPath[1] == 0 { + // reset adi.pb + let toast = ToastView(text: "Deleted adi.pb file!", detailText: "You will need to verify your 2FA!") + toast.show(in: self) + let fm = FileManager.default + let documentsPath = fm.documentsDirectory.appendingPathComponent("adi.pb") + print("ADI Path: \(documentsPath)") + + if fm.fileExists(atPath: documentsPath.path) { + do { + try fm.removeItem(at: documentsPath.absoluteURL) + print("DELETED ADI") + } catch let error as NSError { + print("REMOVE ERROR: \(error.domain)") + } + } + } + } +} diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard index ccf9515cc..3c86706b4 100644 --- a/AltStore/Settings/Settings.storyboard +++ b/AltStore/Settings/Settings.storyboard @@ -359,22 +359,22 @@ -