Merge remote-tracking branch 'origin/develop' into fabianthdev/feature/SwiftUI

This commit is contained in:
naturecodevoid
2023-04-06 20:43:10 -07:00
41 changed files with 1080 additions and 1505 deletions

View File

@@ -14,7 +14,7 @@
<key>ALTPairingFile</key>
<string>&lt;insert pairing file here&gt;</string>
<key>ALTAnisetteURL</key>
<string>https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx</string>
<string>https://ani.sidestore.io</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDocumentTypes</key>

View File

@@ -15,6 +15,8 @@ import minimuxer
import AltStoreCore
import UniformTypeIdentifiers
let pairingFileName = "ALTPairingFile.mobiledevicepairing"
final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDelegate
{
private var didFinishLaunching = false
@@ -83,7 +85,7 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
} else {
// Show an alert explaining the pairing file
// Create new Alert
let dialogMessage = UIAlertController(title: "Pairing File", message: "Select the pairing file for your device. For more information, go to https://youtu.be/dQw4w9WgXcQ", preferredStyle: .alert)
let dialogMessage = UIAlertController(title: "Pairing File", message: "Select the pairing file for your device. For more information, go to https://wiki.sidestore.io/guides/install#pairing-process", preferredStyle: .alert)
// Create OK button with action handler
let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
@@ -129,14 +131,11 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
}
// Save to a file for next launch
let filename = "ALTPairingFile.mobiledevicepairing"
let fm = FileManager.default
let documentsPath = fm.documentsDirectory.appendingPathComponent("/\(filename)")
try pairing_string?.write(to: documentsPath, atomically: true, encoding: String.Encoding.utf8)
let pairingFile = FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)")
try pairing_string?.write(to: pairingFile, atomically: true, encoding: String.Encoding.utf8)
// Start minimuxer now that we have a file
start_minimuxer_threads(pairing_string!)
} catch {
displayError("Unable to read pairing file")
}
@@ -152,22 +151,15 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
}
func start_minimuxer_threads(_ pairing_file: String) {
set_usbmuxd_socket()
#if false // Retries
var res = start_minimuxer(pairing_file: pairing_file)
var attempts = 10
while (attempts != 0 && res != 0) {
print("start_minimuxer `res` != 0, retry #\(attempts)")
res = start_minimuxer(pairing_file: pairing_file)
attempts -= 1
target_minimuxer_address()
let documentsDirectory = FileManager.default.documentsDirectory.absoluteString
do {
try start(pairing_file, documentsDirectory)
} catch {
try! FileManager.default.removeItem(at: FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)"))
displayError("minimuxer failed to start, please restart SideStore. \(minimuxerToOperationError(error).failureReason ?? "UNKNOWN ERROR!!!!!! REPORT TO GITHUB ISSUES!")")
}
#else
let res = start_minimuxer(pairing_file: pairing_file)
#endif
if res != 0 {
displayError("minimuxer failed to start. Incorrect arguments were passed.")
}
auto_mount_dev_image()
start_auto_mounter(documentsDirectory)
}
}

View File

@@ -392,7 +392,8 @@ extension AppManager
func fetchAppIDs(completionHandler: @escaping (Result<([AppID], NSManagedObjectContext), Error>) -> Void)
{
let authenticationOperation = self.authenticate(presentingViewController: nil) { (result) in
print("Authenticated for fetching App IDs with result:", result)
// result contains name, email, auth token, OTP and other possibly personal/account specific info. we don't want this logged
//print("Authenticated for fetching App IDs with result:", result)
}
let fetchAppIDsOperation = FetchAppIDsOperation(context: authenticationOperation.context)
@@ -873,7 +874,11 @@ private extension AppManager
// Check if backup app is installed in place of real app.
let uti = UTTypeCopyDeclaration(app.installedBackupAppUTI as CFString)?.takeRetainedValue() as NSDictionary?
if app.certificateSerialNumber != group.context.certificate?.serialNumber ||
// for some reason, `app.certificateSerialNumber != group.context.certificate?.serialNumber` is true on first SideStore refresh
// in most cases, the first refresh gets stuck since it is a full reinstall, and to fix it you must exit to home screen
// which finishes it but removes all app data
// so we want to ensure we don't reinstall for SideStore if it's true (it will still reinstall if needsResign is true)
if (app.certificateSerialNumber != group.context.certificate?.serialNumber && app.bundleIdentifier != StoreApp.altstoreAppID) ||
uti != nil ||
app.needsResign
{

View File

@@ -44,14 +44,9 @@ final class DeactivateAppOperation: ResultOperation<InstalledApp>
for profile in allIdentifiers {
do {
let res = try remove_provisioning_profile(id: profile)
if case Uhoh.Bad(let code) = res {
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
try remove_provisioning_profile(profile)
} catch {
self.finish(.failure(ALTServerError(.unknownResponse)))
return self.finish(.failure(minimuxerToOperationError(error)))
}
}

View File

@@ -45,23 +45,13 @@ final class EnableJITOperation<Context: EnableJITContext>: ResultOperation<Void>
guard let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) }
installedApp.managedObjectContext?.perform {
let v = minimuxer_to_operation(code: 1)
do {
var x = try debug_app(app_id: installedApp.resignedBundleIdentifier)
switch x {
case .Good:
self.finish(.success(()))
case .Bad(let code):
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
try debug_app(installedApp.resignedBundleIdentifier)
} catch {
self.finish(.failure(OperationError.unknown))
return self.finish(.failure(minimuxerToOperationError(error)))
}
self.finish(.success(()))
}
}
}

View File

@@ -384,6 +384,16 @@ extension FetchProvisioningProfilesOperation
if app.isAltStoreApp
{
print("Application groups before modifying for SideStore: \(applicationGroups)")
// Remove app groups that contain AltStore since they can be problematic (cause SideStore to expire early)
for (index, group) in applicationGroups.enumerated() {
if group.contains("AltStore") {
print("Removing application group: \(group)")
applicationGroups.remove(at: index)
}
}
// Potentially updating app groups for this specific AltStore.
// Find the (unique) AltStore app group, then replace it
// with the correct "base" app group ID.
@@ -397,6 +407,7 @@ extension FetchProvisioningProfilesOperation
applicationGroups.append(Bundle.baseAltStoreAppGroupID)
}
}
print("Application groups: \(applicationGroups)")
// Dispatch onto global queue to prevent appGroupsLock deadlock.
DispatchQueue.global().async {
@@ -478,10 +489,13 @@ extension FetchProvisioningProfilesOperation
ALTAppleAPI.shared.delete(profile, for: team, session: session) { (success, error) in
switch Result(success, error)
{
case .failure(let error): completionHandler(.failure(error))
case .success:
case .failure:
// As of March 20, 2023, the free provisioning profile is re-generated each fetch, and you can no longer delete it.
// So instead, we just return the fetched profile from above.
completionHandler(.success(profile))
// Fetch new provisiong profile
case .success:
// Fetch new provisioning profile
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in
completionHandler(Result(profile, error))
}

View File

@@ -11,6 +11,7 @@ import Network
import AltStoreCore
import AltSign
import Roxas
import minimuxer
@objc(InstallAppOperation)
final class InstallAppOperation: ResultOperation<InstalledApp>
@@ -148,17 +149,14 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
})
}
let ns_bundle = NSString(string: installedApp.bundleIdentifier)
let ns_bundle_ptr = UnsafeMutablePointer<CChar>(mutating: ns_bundle.utf8String)
let res = minimuxer_install_ipa(ns_bundle_ptr)
if res == 0 {
installedApp.refreshedDate = Date()
self.finish(.success(installedApp))
} else {
self.finish(.failure(minimuxer_to_operation(code: res)))
do {
try install_ipa(installedApp.bundleIdentifier)
} catch {
return self.finish(.failure(minimuxerToOperationError(error)))
}
installedApp.refreshedDate = Date()
self.finish(.success(installedApp))
}
}
@@ -174,10 +172,11 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
do
{
try FileManager.default.removeItem(at: fileURL)
print("Removed refreshed IPA")
}
catch
{
print("Failed to remove refreshed .ipa:", error)
print("Failed to remove refreshed .ipa: \(error)")
}
}

View File

@@ -8,6 +8,7 @@
import Foundation
import AltSign
import minimuxer
enum OperationError: LocalizedError
{
@@ -42,9 +43,11 @@ enum OperationError: LocalizedError
case uninstall
case lookupApps
case detach
case attach
case functionArguments
case profileInstall
case profileManage
case noConnection
case invalidPairingFile
var failureReason: String? {
switch self {
@@ -70,9 +73,11 @@ enum OperationError: LocalizedError
case .uninstall: return NSLocalizedString("Unable to uninstall the app", comment: "")
case .lookupApps: return NSLocalizedString("Unable to fetch apps from the device", comment: "")
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 .functionArguments: return NSLocalizedString("A function was passed invalid arguments", comment: "")
case .profileInstall: return NSLocalizedString("Unable to manage profiles on the device", comment: "")
case .profileManage: return NSLocalizedString("Unable to manage profiles on the device", comment: "")
case .noConnection: return NSLocalizedString("Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi", comment: "")
case .invalidPairingFile: 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: "")
}
}
@@ -118,49 +123,50 @@ enum OperationError: LocalizedError
}
}
func minimuxer_to_operation(code: Int32) -> OperationError {
switch code {
case -1:
/// crashes if error is not a MinimuxerError
func minimuxerToOperationError(_ error: Error) -> OperationError {
switch error as! MinimuxerError {
case .NoDevice:
return OperationError.noDevice
case -2:
return OperationError.createService(name: "debug")
case -3:
return OperationError.createService(name: "instproxy")
case -4:
return OperationError.getFromDevice(name: "installed apps")
case -5:
return OperationError.getFromDevice(name: "path to the app")
case -6:
return OperationError.getFromDevice(name: "bundle path")
case -7:
return OperationError.setArgument(name: "max packet")
case -8:
return OperationError.setArgument(name: "working directory")
case -9:
return OperationError.setArgument(name: "argv")
case -10:
return OperationError.getFromDevice(name: "launch success")
case -11:
return OperationError.detach
case -12:
return OperationError.functionArguments
case -13:
return OperationError.createService(name: "AFC")
case -14:
return OperationError.afc
case -15:
return OperationError.install
case -16:
return OperationError.uninstall
case -17:
return OperationError.createService(name: "misagent")
case -18:
return OperationError.profileInstall
case -19:
return OperationError.profileInstall
case -20:
case .NoConnection:
return OperationError.noConnection
default:
return OperationError.unknown
case .PairingFile:
return OperationError.invalidPairingFile
case .CreateDebug:
return OperationError.createService(name: "debug")
case .CreateInstproxy:
return OperationError.createService(name: "instproxy")
case .LookupApps:
return OperationError.getFromDevice(name: "installed apps")
case .FindApp:
return OperationError.getFromDevice(name: "path to the app")
case .BundlePath:
return OperationError.getFromDevice(name: "bundle path")
case .MaxPacket:
return OperationError.setArgument(name: "max packet")
case .WorkingDirectory:
return OperationError.setArgument(name: "working directory")
case .Argv:
return OperationError.setArgument(name: "argv")
case .LaunchSuccess:
return OperationError.getFromDevice(name: "launch success")
case .Detach:
return OperationError.detach
case .Attach:
return OperationError.attach
case .CreateAfc:
return OperationError.createService(name: "AFC")
case .RwAfc:
return OperationError.afc
case .InstallApp:
return OperationError.install
case .UninstallApp:
return OperationError.uninstall
case .CreateMisagent:
return OperationError.createService(name: "misagent")
case .ProfileInstall:
return OperationError.profileManage
case .ProfileRemove:
return OperationError.profileManage
}
}

View File

@@ -49,15 +49,12 @@ final class RefreshAppOperation: ResultOperation<InstalledApp>
for p in profiles {
do {
let x = try install_provisioning_profile(plist: p.value.data)
if case .Bad(let code) = x {
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
let bytes = p.value.data.toRustByteSlice()
try install_provisioning_profile(bytes.forRust())
} catch {
self.finish(.failure(OperationError.unknown))
return self.finish(.failure(minimuxerToOperationError(error)))
}
self.progress.completedUnitCount += 1
let predicate = NSPredicate(format: "%K == %@", #keyPath(InstalledApp.bundleIdentifier), app.bundleIdentifier)

View File

@@ -39,15 +39,11 @@ final class RemoveAppOperation: ResultOperation<InstalledApp>
let resignedBundleIdentifier = installedApp.resignedBundleIdentifier
do {
let res = try remove_app(app_id: resignedBundleIdentifier)
if case Uhoh.Bad(let code) = res {
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
try remove_app(resignedBundleIdentifier)
} catch {
self.finish(.failure(ALTServerError(.appDeletionFailed)))
return self.finish(.failure(minimuxerToOperationError(error)))
}
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
self.progress.completedUnitCount += 1

View File

@@ -61,6 +61,7 @@ final class ResignAppOperation: ResultOperation<ALTApplication>
{
let destinationURL = InstalledApp.refreshedIPAURL(for: app)
try FileManager.default.copyItem(at: resignedURL, to: destinationURL, shouldReplace: true)
print("Successfully resigned app to \(destinationURL.absoluteString)")
// Use appBundleURL since we need an app bundle, not .ipa.
guard let resignedApplication = ALTApplication(fileURL: appBundleURL) else { throw OperationError.invalidApp }

View File

@@ -9,6 +9,7 @@ import Foundation
import Network
import AltStoreCore
import minimuxer
@objc(SendAppOperation)
final class SendAppOperation: ResultOperation<()>
@@ -44,24 +45,18 @@ final class SendAppOperation: ResultOperation<()>
print("AFC App `fileURL`: \(fileURL.absoluteString)")
let ns_bundle = NSString(string: app.bundleIdentifier)
let ns_bundle_ptr = UnsafeMutablePointer<CChar>(mutating: ns_bundle.utf8String)
if let data = NSData(contentsOf: fileURL) {
let pls = UnsafeMutablePointer<UInt8>.allocate(capacity: data.length)
for (index, data) in data.enumerated() {
pls[index] = data
}
let res = minimuxer_yeet_app_afc(ns_bundle_ptr, pls, UInt(data.length))
if res == 0 {
print("minimuxer_yeet_app_afc `res` == \(res)")
self.progress.completedUnitCount += 1
self.finish(.success(()))
} else {
self.finish(.failure(minimuxer_to_operation(code: res)))
do {
let bytes = Data(data).toRustByteSlice()
try yeet_app_afc(app.bundleIdentifier, bytes.forRust())
} catch {
return self.finish(.failure(minimuxerToOperationError(error)))
}
self.progress.completedUnitCount += 1
self.finish(.success(()))
} else {
print("IPA doesn't exist????")
self.finish(.failure(ALTServerError(.underlyingError)))
}
}

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>A72ZC8AJ5X.com.SideStore.AltStore</string>
<string>A72ZC8AJ5X.com.SideStore.SideStore</string>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.siri</key>
@@ -12,9 +12,9 @@
<string>A72ZC8AJ5X</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.SideStore.AltStore</string>
<string>group.com.SideStore.SideStore</string>
</array>
<key>get-task-allow</key>
<true/>
</dict>
</plist>
</plist>

View File

@@ -16,15 +16,13 @@
<key>Key</key>
<string>customAnisetteURL</string>
<key>DefaultValue</key>
<string>http://ani.sidestore.io</string>
<string>https://ani.sidestore.io</string>
<key>Titles</key>
<array>
<string>SideStore</string>
<string>Macley (US)</string>
<string>Macley (DE)</string>
<string>DrPudding</string>
<string>jkcoxson (AltServer)</string>
<string>jkcoxson (Provision)</string>
<string>Sideloadly</string>
<string>Nick</string>
<string>Jawshoeadan</string>
@@ -32,12 +30,10 @@
</array>
<key>Values</key>
<array>
<string>http://ani.sidestore.io</string>
<string>https://ani.sidestore.io</string>
<string>http://us1.sternserv.tech</string>
<string>http://de1.sternserv.tech</string>
<string>https://sign.rheaa.xyz</string>
<string>http://jkcoxson.com:2095</string>
<string>http://jkcoxson.com:2052</string>
<string>https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx</string>
<string>http://45.33.29.114</string>
<string>https://anisette.jawshoeadan.me</string>

View File

@@ -381,13 +381,12 @@ private extension SourcesViewController
dispatchGroup.notify(queue: .main) {
if let error = fetchError
{
finish(.failure(error))
}
else
{
let sources = featuredSourceURLs.compactMap { sourcesByURL[$0] }
finish(.success(sources))
print(error)
// 1 error doesn't mean all trusted sources failed to load! Riley, why did you do this???????
// finish(.failure(error))
}
let sources = featuredSourceURLs.compactMap { sourcesByURL[$0] }
finish(.success(sources))
}
}
}