mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-11 15:53:30 +01:00
Logs sideloading-related events with OSLog
This commit is contained in:
@@ -355,6 +355,7 @@
|
||||
D51AD27E29356B7B00967AAA /* ALTWrappedError.h in Headers */ = {isa = PBXBuildFile; fileRef = D51AD27C29356B7B00967AAA /* ALTWrappedError.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D51AD27F29356B7B00967AAA /* ALTWrappedError.m in Sources */ = {isa = PBXBuildFile; fileRef = D51AD27D29356B7B00967AAA /* ALTWrappedError.m */; };
|
||||
D51AD28029356B8000967AAA /* ALTWrappedError.m in Sources */ = {isa = PBXBuildFile; fileRef = D51AD27D29356B7B00967AAA /* ALTWrappedError.m */; };
|
||||
D52A2F972ACB40F700BDF8E3 /* Logger+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A2F962ACB40F700BDF8E3 /* Logger+AltStore.swift */; };
|
||||
D52C08EE28AEC37A006C4AE5 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52C08ED28AEC37A006C4AE5 /* AppVersion.swift */; };
|
||||
D52DD35E2AAA89A600A7F2B6 /* AltSign-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = D52DD35D2AAA89A600A7F2B6 /* AltSign-Dynamic */; };
|
||||
D52EF2BE2A0594550096C377 /* AppDetailCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52EF2BD2A0594550096C377 /* AppDetailCollectionViewController.swift */; };
|
||||
@@ -957,6 +958,7 @@
|
||||
D5189C002A01BC6800F44625 /* UserInfoValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoValue.swift; sourceTree = "<group>"; };
|
||||
D51AD27C29356B7B00967AAA /* ALTWrappedError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTWrappedError.h; sourceTree = "<group>"; };
|
||||
D51AD27D29356B7B00967AAA /* ALTWrappedError.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTWrappedError.m; sourceTree = "<group>"; };
|
||||
D52A2F962ACB40F700BDF8E3 /* Logger+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+AltStore.swift"; sourceTree = "<group>"; };
|
||||
D52C08ED28AEC37A006C4AE5 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = "<group>"; };
|
||||
D52E988928D002D30032BE6B /* AltStore 11.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 11.xcdatamodel"; sourceTree = "<group>"; };
|
||||
D52EF2BD2A0594550096C377 /* AppDetailCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailCollectionViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -1581,6 +1583,7 @@
|
||||
BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */,
|
||||
D5F48B4729CCF21B002B52A4 /* AltStore+Async.swift */,
|
||||
D5893F7E2A14183200E767CD /* NSManagedObjectContext+Conveniences.swift */,
|
||||
D52A2F962ACB40F700BDF8E3 /* Logger+AltStore.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -3001,6 +3004,7 @@
|
||||
BFAECC572501B0A400528F27 /* ConnectionManager.swift in Sources */,
|
||||
BF66EE9D2501AEC1007EE018 /* AppProtocol.swift in Sources */,
|
||||
D519AD46292D665B004B12F9 /* Managed.swift in Sources */,
|
||||
D52A2F972ACB40F700BDF8E3 /* Logger+AltStore.swift in Sources */,
|
||||
BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */,
|
||||
D5CA0C4B280E141900469595 /* ManagedPatron.swift in Sources */,
|
||||
D5708417292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift in Sources */,
|
||||
|
||||
@@ -202,7 +202,11 @@ class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, ALTAppl
|
||||
{
|
||||
guard !self.isFinished else { return }
|
||||
|
||||
print("Finished authenticating with result:", result.error?.localizedDescription ?? "success")
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): Logger.sideload.error("Failed to authenticate account. \(error.localizedDescription, privacy: .public)")
|
||||
case .success((let team, _, _)): Logger.sideload.notice("Authenticated account for team \(team.identifier, privacy: .public).")
|
||||
}
|
||||
|
||||
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||
context.perform {
|
||||
@@ -347,6 +351,8 @@ private extension AuthenticationOperation
|
||||
|
||||
if let appleID = Keychain.shared.appleIDEmailAddress, let password = Keychain.shared.appleIDPassword
|
||||
{
|
||||
Logger.sideload.notice("Authenticating Apple ID...")
|
||||
|
||||
self.authenticate(appleID: appleID, password: password) { (result) in
|
||||
switch result
|
||||
{
|
||||
|
||||
@@ -103,7 +103,7 @@ class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<InstalledA
|
||||
}
|
||||
|
||||
self.managedObjectContext.perform {
|
||||
print("Apps to refresh:", self.installedApps.map(\.bundleIdentifier))
|
||||
Logger.sideload.notice("Refreshing apps in background: \(self.installedApps.map(\.bundleIdentifier), privacy: .public)")
|
||||
|
||||
self.startListeningForRunningApps()
|
||||
|
||||
@@ -114,7 +114,10 @@ class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<InstalledA
|
||||
self.managedObjectContext.perform {
|
||||
|
||||
let filteredApps = self.installedApps.filter { !self.runningApplications.contains($0.bundleIdentifier) }
|
||||
print("Filtered Apps to Refresh:", filteredApps.map { $0.bundleIdentifier })
|
||||
if !self.runningApplications.isEmpty
|
||||
{
|
||||
Logger.sideload.notice("Skipping refreshing running apps: \(self.runningApplications, privacy: .public)")
|
||||
}
|
||||
|
||||
let group = AppManager.shared.refresh(filteredApps, presentingViewController: nil)
|
||||
group.beginInstallationHandler = { (installedApp) in
|
||||
@@ -225,7 +228,7 @@ private extension BackgroundRefreshAppsOperation
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to refresh apps in background.", error)
|
||||
Logger.sideload.error("Failed to refresh apps in background. \(error.localizedDescription, privacy: .public)")
|
||||
|
||||
content.title = NSLocalizedString("Failed to Refresh Apps", comment: "")
|
||||
content.body = error.localizedDescription
|
||||
@@ -269,7 +272,7 @@ private extension BackgroundRefreshAppsOperation
|
||||
_ = RefreshAttempt(identifier: self.refreshIdentifier, result: result, context: context)
|
||||
|
||||
do { try context.save() }
|
||||
catch { print("Failed to save refresh attempt.", error) }
|
||||
catch { Logger.sideload.error("Failed to save refresh attempt. \(error.localizedDescription, privacy: .public)") }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,8 +87,8 @@ class BackupAppOperation: ResultOperation<Void>
|
||||
// Failed too quickly for human to respond to alert, possibly still finalizing installation.
|
||||
// Try again in a couple seconds.
|
||||
|
||||
print("Failed too quickly, retrying after a few seconds...")
|
||||
|
||||
Logger.sideload.error("Failed to open app too quickly, retrying after a few seconds...")
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||
UIApplication.shared.open(openURL, options: [:]) { (success) in
|
||||
if success
|
||||
|
||||
@@ -39,12 +39,14 @@ class DeactivateAppOperation: ResultOperation<InstalledApp>
|
||||
guard let server = self.context.server else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||
guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { return self.finish(.failure(OperationError.unknownUDID)) }
|
||||
|
||||
Logger.sideload.notice("Deactivating app \(self.app.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
ServerManager.shared.connect(to: server) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .success(let connection):
|
||||
print("Sending deactivate app request...")
|
||||
Logger.sideload.notice("Sending deactivate app request...")
|
||||
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
let installedApp = context.object(with: self.app.objectID) as! InstalledApp
|
||||
@@ -54,21 +56,28 @@ class DeactivateAppOperation: ResultOperation<InstalledApp>
|
||||
|
||||
let request = RemoveProvisioningProfilesRequest(udid: udid, bundleIdentifiers: Set(allIdentifiers))
|
||||
connection.send(request) { (result) in
|
||||
print("Sent deactive app request!")
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to send deactivate app request. \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
case .success:
|
||||
print("Waiting for deactivate app response...")
|
||||
Logger.sideload.debug("Waiting for deactivate app response...")
|
||||
connection.receiveResponse() { (result) in
|
||||
print("Receiving deactivate app response:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .success(.error(let response)): self.finish(.failure(response.error))
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to receive deactivate app response. \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
case .success(.error(let response)):
|
||||
Logger.sideload.error("Failed to deactivate app. \(response.error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(response.error))
|
||||
|
||||
case .success(.removeProvisioningProfiles):
|
||||
Logger.sideload.notice("Successfully deactivated app \(self.app.bundleIdentifier, privacy: .public)!")
|
||||
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
self.progress.completedUnitCount += 1
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class DownloadAppOperation: ResultOperation<ALTApplication>
|
||||
return
|
||||
}
|
||||
|
||||
print("Downloading App:", self.bundleIdentifier)
|
||||
Logger.sideload.notice("Downloading app \(self.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
// Set _after_ checking self.context.error to prevent overwriting localized failure for previous errors.
|
||||
self.localizedFailure = String(format: NSLocalizedString("%@ could not be downloaded.", comment: ""), self.appName)
|
||||
@@ -108,7 +108,7 @@ class DownloadAppOperation: ResultOperation<ALTApplication>
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to remove DownloadAppOperation temporary directory: \(self.temporaryDirectory).", error)
|
||||
Logger.sideload.error("Failed to remove DownloadAppOperation temporary directory: \(self.temporaryDirectory, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
|
||||
super.finish(result)
|
||||
@@ -172,6 +172,9 @@ private extension DownloadAppOperation
|
||||
try FileManager.default.copyItem(at: application.fileURL, to: self.destinationURL, shouldReplace: true)
|
||||
|
||||
guard let copiedApplication = ALTApplication(fileURL: self.destinationURL) else { throw OperationError.invalidApp }
|
||||
|
||||
Logger.sideload.notice("Downloaded app \(copiedApplication.bundleIdentifier, privacy: .public) from \(sourceURL, privacy: .public)")
|
||||
|
||||
self.finish(.success(copiedApplication))
|
||||
|
||||
self.progress.completedUnitCount += 1
|
||||
|
||||
@@ -34,32 +34,42 @@ class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>
|
||||
|
||||
guard let server = self.context.server else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||
|
||||
Logger.sideload.notice("Fetching anisette data...")
|
||||
|
||||
ServerManager.shared.connect(to: server) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
self.finish(.failure(error))
|
||||
case .success(let connection):
|
||||
print("Sending anisette data request...")
|
||||
Logger.sideload.debug("Sending anisette data request...")
|
||||
|
||||
let request = AnisetteDataRequest()
|
||||
connection.send(request) { (result) in
|
||||
print("Sent anisette data request!")
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to send anisette data request. \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
case .success:
|
||||
print("Waiting for anisette data...")
|
||||
Logger.sideload.debug("Waiting for anisette data...")
|
||||
connection.receiveResponse() { (result) in
|
||||
print("Receiving anisette data:", result.error?.localizedDescription ?? "success")
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .success(.error(let response)): self.finish(.failure(response.error))
|
||||
case .success(.anisetteData(let response)): self.finish(.success(response.anisetteData))
|
||||
case .success: self.finish(.failure(ALTServerError(.unknownRequest)))
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to receive anisette data response. \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
case .success(.error(let response)):
|
||||
Logger.sideload.error("Failed to receive anisette data. \(response.error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(response.error))
|
||||
|
||||
case .success(.anisetteData(let response)):
|
||||
Logger.sideload.info("Successfully received anisette data!")
|
||||
self.finish(.success(response.anisetteData))
|
||||
|
||||
case .success: self.finish(.failure(ALTServerError(.unknownResponse)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ class FetchProvisioningProfilesOperation: ResultOperation<[String: ALTProvisioni
|
||||
|
||||
guard let app = self.context.app else { return self.finish(.failure(OperationError.appNotFound(name: nil))) }
|
||||
|
||||
Logger.sideload.notice("Fetching provisioning profiles for app \(self.context.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
self.progress.totalUnitCount = Int64(1 + app.appExtensions.count)
|
||||
|
||||
self.prepareProvisioningProfile(for: app, parentApp: nil, team: team, session: session) { (result) in
|
||||
@@ -245,6 +247,8 @@ extension FetchProvisioningProfilesOperation
|
||||
|
||||
if let appID = appIDs.first(where: { $0.bundleIdentifier.lowercased() == bundleIdentifier.lowercased() })
|
||||
{
|
||||
Logger.sideload.notice("Using existing App ID \(appID.bundleIdentifier, privacy: .public)")
|
||||
|
||||
completionHandler(.success(appID))
|
||||
}
|
||||
else
|
||||
@@ -275,6 +279,9 @@ extension FetchProvisioningProfilesOperation
|
||||
do
|
||||
{
|
||||
let appID = try Result(appID, error).get()
|
||||
|
||||
Logger.sideload.notice("Registered new App ID \(appID.bundleIdentifier, privacy: .public)")
|
||||
|
||||
completionHandler(.success(appID))
|
||||
}
|
||||
catch ALTAppleAPIError.maximumAppIDLimitReached
|
||||
@@ -358,8 +365,15 @@ extension FetchProvisioningProfilesOperation
|
||||
let appID = appID.copy() as! ALTAppID
|
||||
appID.features = features
|
||||
|
||||
ALTAppleAPI.shared.update(appID, team: team, session: session) { (appID, error) in
|
||||
completionHandler(Result(appID, error))
|
||||
ALTAppleAPI.shared.update(appID, team: team, session: session) { (updatedAppID, error) in
|
||||
let result = Result(updatedAppID, error)
|
||||
switch result
|
||||
{
|
||||
case .success(let appID): Logger.sideload.notice("Updated features for App ID \(appID.bundleIdentifier, privacy: .public).")
|
||||
case .failure(let error): Logger.sideload.error("Failed to update features for App ID \(appID.bundleIdentifier, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
|
||||
completionHandler(result)
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -377,6 +391,7 @@ extension FetchProvisioningProfilesOperation
|
||||
}
|
||||
|
||||
guard var applicationGroups = entitlements[.appGroups] as? [String], !applicationGroups.isEmpty else {
|
||||
Logger.sideload.notice("App ID \(appID.bundleIdentifier, privacy: .public) has no app groups, skipping assignment.")
|
||||
// Assigning an App ID to an empty app group array fails,
|
||||
// so just do nothing if there are no app groups.
|
||||
return completionHandler(.success(appID))
|
||||
@@ -414,7 +429,10 @@ extension FetchProvisioningProfilesOperation
|
||||
ALTAppleAPI.shared.fetchAppGroups(for: team, session: session) { (groups, error) in
|
||||
switch Result(groups, error)
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to fetch app groups for team \(team.identifier, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
finish(.failure(error))
|
||||
|
||||
case .success(let fetchedGroups):
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
@@ -439,8 +457,13 @@ extension FetchProvisioningProfilesOperation
|
||||
ALTAppleAPI.shared.addAppGroup(withName: name, groupIdentifier: adjustedGroupIdentifier, team: team, session: session) { (group, error) in
|
||||
switch Result(group, error)
|
||||
{
|
||||
case .success(let group): groups.append(group)
|
||||
case .failure(let error): errors.append(error)
|
||||
case .success(let group):
|
||||
Logger.sideload.notice("Created new App Group \(group.groupIdentifier, privacy: .public).")
|
||||
groups.append(group)
|
||||
|
||||
case .failure(let error):
|
||||
Logger.sideload.notice("Failed to create new App Group \(adjustedGroupIdentifier, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
errors.append(error)
|
||||
}
|
||||
|
||||
dispatchGroup.leave()
|
||||
@@ -456,8 +479,17 @@ extension FetchProvisioningProfilesOperation
|
||||
else
|
||||
{
|
||||
ALTAppleAPI.shared.assign(appID, to: Array(groups), team: team, session: session) { (success, error) in
|
||||
let result = Result(success, error)
|
||||
finish(result.map { _ in appID })
|
||||
let groupIDs = groups.map { $0.groupIdentifier }
|
||||
switch Result(success, error)
|
||||
{
|
||||
case .success:
|
||||
Logger.sideload.notice("Assigned App ID \(appID.bundleIdentifier, privacy: .public) to App Groups \(groupIDs.description, privacy: .public).")
|
||||
finish(.success(appID))
|
||||
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to assign App ID \(appID.bundleIdentifier, privacy: .public) to App Groups \(groupIDs.description, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
finish(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -484,6 +516,8 @@ extension FetchProvisioningProfilesOperation
|
||||
completionHandler(.success(profile))
|
||||
|
||||
case .success:
|
||||
Logger.sideload.notice("Generating new free provisioning profile for App ID \(appID.bundleIdentifier, privacy: .public).")
|
||||
|
||||
// Fetch new provisioning profile
|
||||
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in
|
||||
completionHandler(Result(profile, error))
|
||||
|
||||
@@ -48,6 +48,8 @@ class FindServerOperation: ResultOperation<Server>
|
||||
return
|
||||
}
|
||||
|
||||
Logger.sideload.notice("Discovering AltServers...")
|
||||
|
||||
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
let observer = Unmanaged.passUnretained(self).toOpaque()
|
||||
|
||||
@@ -63,22 +65,30 @@ class FindServerOperation: ResultOperation<Server>
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
|
||||
if let machServiceName = self.localServerMachServiceName
|
||||
{
|
||||
Logger.sideload.notice("Found AltDaemon!")
|
||||
|
||||
// Prefer background daemon, if it exists and is running.
|
||||
let server = Server(connectionType: .local, machServiceName: machServiceName)
|
||||
self.finish(.success(server))
|
||||
}
|
||||
else if self.isWiredServerConnectionAvailable
|
||||
{
|
||||
Logger.sideload.notice("Found AltServer connected via USB!")
|
||||
|
||||
let server = Server(connectionType: .wired)
|
||||
self.finish(.success(server))
|
||||
}
|
||||
else if let server = ServerManager.shared.discoveredServers.first(where: { $0.isPreferred })
|
||||
{
|
||||
Logger.sideload.notice("Found preferred AltServer! \(server.localizedName ?? "nil", privacy: .public)")
|
||||
|
||||
// Preferred server.
|
||||
self.finish(.success(server))
|
||||
}
|
||||
else if let server = ServerManager.shared.discoveredServers.first
|
||||
{
|
||||
Logger.sideload.notice("Found AltServer! \(server.localizedName ?? "nil", privacy: .public)")
|
||||
|
||||
// Any available server.
|
||||
self.finish(.success(server))
|
||||
}
|
||||
@@ -113,7 +123,7 @@ fileprivate extension FindServerOperation
|
||||
connection.connect { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): print("Could not connect to AltDaemon XPC service \(machServiceName).", error)
|
||||
case .failure(let error): Logger.sideload.notice("Could not connect to AltDaemon XPC service \(machServiceName, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
case .success: self.localServerMachServiceName = machServiceName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ class InstallAppOperation: ResultOperation<InstalledApp>
|
||||
let connection = self.context.installationConnection
|
||||
else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||
|
||||
Logger.sideload.notice("Installing resigned app \(resignedApp.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
@Managed var appVersion = self.context.appVersion
|
||||
let storeBuildVersion = $appVersion.buildVersion
|
||||
|
||||
@@ -152,23 +154,32 @@ class InstallAppOperation: ResultOperation<InstalledApp>
|
||||
})
|
||||
}
|
||||
|
||||
let request = BeginInstallationRequest(activeProfiles: activeProfiles, bundleIdentifier: installedApp.resignedBundleIdentifier)
|
||||
let resignedBundleID = installedApp.resignedBundleIdentifier
|
||||
|
||||
let request = BeginInstallationRequest(activeProfiles: activeProfiles, bundleIdentifier: resignedBundleID)
|
||||
connection.send(request) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .failure(let error):
|
||||
Logger.sideload.notice("Failed to send begin installation request for resigned app \(resignedBundleID, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
case .success:
|
||||
Logger.sideload.notice("Sent begin installation request for resigned app \(resignedBundleID, privacy: .public).")
|
||||
|
||||
self.receive(from: connection) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .success:
|
||||
backgroundContext.perform {
|
||||
Logger.sideload.notice("Successfully installed resigned app \(resignedBundleID, privacy: .public)!")
|
||||
|
||||
installedApp.refreshedDate = Date()
|
||||
self.finish(.success(installedApp))
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
Logger.sideload.notice("Failed to install resigned app \(resignedBundleID, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
}
|
||||
}
|
||||
@@ -192,7 +203,7 @@ class InstallAppOperation: ResultOperation<InstalledApp>
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to remove refreshed .ipa:", error)
|
||||
Logger.sideload.error("Failed to remove refreshed .ipa: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,11 +219,12 @@ private extension InstallAppOperation
|
||||
do
|
||||
{
|
||||
let response = try result.get()
|
||||
print(response)
|
||||
|
||||
|
||||
switch response
|
||||
{
|
||||
case .installationProgress(let response):
|
||||
Logger.sideload.debug("Installing \(self.context.resignedApp?.bundleIdentifier ?? self.context.bundleIdentifier, privacy: .public)... \((response.progress * 100).rounded())%")
|
||||
|
||||
if response.progress == 1.0
|
||||
{
|
||||
self.progress.completedUnitCount = self.progress.totalUnitCount
|
||||
@@ -249,7 +261,7 @@ private extension InstallAppOperation
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to remove temporary directory.", error)
|
||||
Logger.sideload.error("Failed to remove temporary directory: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,15 @@ class RefreshAppOperation: ResultOperation<InstalledApp>
|
||||
guard let app = self.context.app else { throw OperationError.appNotFound(name: nil) }
|
||||
guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { throw OperationError.unknownUDID }
|
||||
|
||||
Logger.sideload.notice("Refreshing provisioning profiles for app \(self.context.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
ServerManager.shared.connect(to: server) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .success(let connection):
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
print("Sending refresh app request...")
|
||||
Logger.sideload.debug("Sending refresh app request...")
|
||||
|
||||
var activeProfiles: Set<String>?
|
||||
if UserDefaults.standard.activeAppsLimit != nil
|
||||
@@ -65,20 +67,24 @@ class RefreshAppOperation: ResultOperation<InstalledApp>
|
||||
|
||||
let request = InstallProvisioningProfilesRequest(udid: udid, provisioningProfiles: Set(profiles.values), activeProfiles: activeProfiles)
|
||||
connection.send(request) { (result) in
|
||||
print("Sent refresh app request!")
|
||||
Logger.sideload.debug("Sent refresh app request!")
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .success:
|
||||
print("Waiting for refresh app response...")
|
||||
Logger.sideload.debug("Waiting for refresh app response...")
|
||||
|
||||
connection.receiveResponse() { (result) in
|
||||
print("Receiving refresh app response:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .success(.error(let response)): self.finish(.failure(response.error))
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to receive refresh app response. \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
case .success(.error(let response)):
|
||||
Logger.sideload.debug("Failed to refresh app \(self.context.bundleIdentifier, privacy: .public). \(response.error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(response.error))
|
||||
|
||||
case .success(.installProvisioningProfiles):
|
||||
self.managedObjectContext.perform {
|
||||
@@ -100,10 +106,13 @@ class RefreshAppOperation: ResultOperation<InstalledApp>
|
||||
installedExtension.update(provisioningProfile: provisioningProfile)
|
||||
}
|
||||
|
||||
Logger.sideload.notice("Refreshed provisioning profiles for app \(self.context.bundleIdentifier, privacy: .public)")
|
||||
self.finish(.success(installedApp))
|
||||
}
|
||||
|
||||
case .success: self.finish(.failure(ALTServerError(.unknownRequest)))
|
||||
case .success:
|
||||
Logger.sideload.notice("Received unknown refresh app response for app \(self.context.bundleIdentifier, privacy: .public)")
|
||||
self.finish(.failure(ALTServerError(.unknownResponse)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
import AltStoreCore
|
||||
|
||||
@objc(RemoveAppBackupOperation)
|
||||
class RemoveAppBackupOperation: ResultOperation<Void>
|
||||
{
|
||||
@@ -36,6 +38,9 @@ class RemoveAppBackupOperation: ResultOperation<Void>
|
||||
}
|
||||
|
||||
guard let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||
|
||||
Logger.sideload.notice("Removing backup for app \(self.context.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
installedApp.managedObjectContext?.perform {
|
||||
guard let backupDirectoryURL = FileManager.default.backupDirectoryURL(for: installedApp) else { return self.finish(.failure(OperationError.missingAppGroup)) }
|
||||
|
||||
@@ -61,14 +66,14 @@ class RemoveAppBackupOperation: ResultOperation<Void>
|
||||
|
||||
#else
|
||||
|
||||
print("Failed to remove app backup directory:", error)
|
||||
Logger.sideload.error("Failed to remove app backup directory \(backupDirectoryURL.lastPathComponent, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to remove app backup directory:", error)
|
||||
Logger.sideload.error("Failed to remove app backup directory \(backupDirectoryURL.lastPathComponent, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ class RemoveAppOperation: ResultOperation<InstalledApp>
|
||||
guard let server = self.context.server, let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||
guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { return self.finish(.failure(OperationError.unknownUDID)) }
|
||||
|
||||
Logger.sideload.notice("Removing app \(self.context.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
installedApp.managedObjectContext?.perform {
|
||||
let resignedBundleIdentifier = installedApp.resignedBundleIdentifier
|
||||
|
||||
@@ -43,19 +45,24 @@ class RemoveAppOperation: ResultOperation<InstalledApp>
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .success(let connection):
|
||||
print("Sending remove app request...")
|
||||
Logger.sideload.debug("Sending remove app request...")
|
||||
|
||||
let request = RemoveAppRequest(udid: udid, bundleIdentifier: resignedBundleIdentifier)
|
||||
connection.send(request) { (result) in
|
||||
print("Sent remove app request!")
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): self.finish(.failure(error))
|
||||
case .failure(let error):
|
||||
Logger.sideload.error("Failed to send remove app request. \(error.localizedDescription, privacy: .public)")
|
||||
self.finish(.failure(error))
|
||||
|
||||
case .success:
|
||||
print("Waiting for remove app response...")
|
||||
Logger.sideload.debug("Waiting for remove app response...")
|
||||
connection.receiveResponse() { (result) in
|
||||
print("Receiving remove app response:", result)
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): Logger.sideload.error("Failed to remove app. \(error.localizedDescription, privacy: .public)")
|
||||
case .success: Logger.sideload.info("Successfully removed app \(self.context.bundleIdentifier, privacy: .public)!")
|
||||
}
|
||||
|
||||
switch result
|
||||
{
|
||||
|
||||
@@ -43,6 +43,8 @@ class ResignAppOperation: ResultOperation<ALTApplication>
|
||||
let certificate = self.context.certificate
|
||||
else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||
|
||||
Logger.sideload.notice("Resigning app \(self.context.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
// Prepare app bundle
|
||||
let prepareAppProgress = Progress.discreteProgress(totalUnitCount: 2)
|
||||
self.progress.addChild(prepareAppProgress, withPendingUnitCount: 3)
|
||||
@@ -50,8 +52,6 @@ class ResignAppOperation: ResultOperation<ALTApplication>
|
||||
let prepareAppBundleProgress = self.prepareAppBundle(for: app, profiles: profiles) { (result) in
|
||||
guard let appBundleURL = self.process(result) else { return }
|
||||
|
||||
print("Resigning App:", self.context.bundleIdentifier)
|
||||
|
||||
// Resign app bundle
|
||||
let resignProgress = self.resignAppBundle(at: appBundleURL, team: team, certificate: certificate, profiles: Array(profiles.values)) { (result) in
|
||||
guard let resignedURL = self.process(result) else { return }
|
||||
@@ -64,6 +64,9 @@ class ResignAppOperation: ResultOperation<ALTApplication>
|
||||
|
||||
// Use appBundleURL since we need an app bundle, not .ipa.
|
||||
guard let resignedApplication = ALTApplication(fileURL: appBundleURL) else { throw OperationError.invalidApp }
|
||||
|
||||
Logger.sideload.notice("Resigned app \(self.context.bundleIdentifier, privacy: .public) to \(resignedApplication.bundleIdentifier, privacy: .public).")
|
||||
|
||||
self.finish(.success(resignedApplication))
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -41,6 +41,8 @@ class SendAppOperation: ResultOperation<ServerConnection>
|
||||
|
||||
guard let resignedApp = self.context.resignedApp, let server = self.context.server else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||
|
||||
Logger.sideload.notice("Sending app \(self.context.bundleIdentifier, privacy: .public) to AltServer \(server.localizedName ?? "nil", privacy: .public)...")
|
||||
|
||||
// self.context.resignedApp.fileURL points to the app bundle, but we want the .ipa.
|
||||
let app = AnyApp(name: resignedApp.name, bundleIdentifier: self.context.bundleIdentifier, url: resignedApp.fileURL)
|
||||
let fileURL = InstalledApp.refreshedIPAURL(for: app)
|
||||
@@ -99,17 +101,17 @@ private extension SendAppOperation
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Sending app data (\(appData.count) bytes)...")
|
||||
Logger.sideload.debug("Sending app data (\(appData.count) bytes)...")
|
||||
|
||||
connection.send(appData, prependSize: false) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
print("Failed to send app data (\(appData.count) bytes)")
|
||||
Logger.sideload.error("Failed to send app to AltServer \(connection.server.localizedName ?? "nil", privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
completionHandler(.failure(error))
|
||||
|
||||
case .success:
|
||||
print("Successfully sent app data (\(appData.count) bytes)")
|
||||
Logger.sideload.notice("Finished sending app to AltServer \(connection.server.localizedName ?? "nil", privacy: .public)!")
|
||||
completionHandler(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ class VerifyAppOperation: ResultOperation<Void>
|
||||
|
||||
guard let app = self.context.app else { throw OperationError.invalidParameters }
|
||||
|
||||
Logger.sideload.notice("Verifying app \(self.context.bundleIdentifier, privacy: .public)...")
|
||||
|
||||
guard app.bundleIdentifier == self.context.bundleIdentifier else {
|
||||
throw VerificationError.mismatchedBundleIdentifiers(sourceBundleID: self.context.bundleIdentifier, app: app)
|
||||
}
|
||||
@@ -149,9 +151,9 @@ private extension VerifyAppOperation
|
||||
let data = try Data(contentsOf: ipaURL)
|
||||
let sha256Hash = SHA256.hash(data: data)
|
||||
let hashString = sha256Hash.compactMap { String(format: "%02x", $0) }.joined()
|
||||
|
||||
print("[ALTLog] Comparing app hash (\(hashString)) against expected hash (\(expectedHash))...")
|
||||
|
||||
|
||||
Logger.sideload.debug("Comparing app hash (\(hashString, privacy: .public)) against expected hash (\(expectedHash, privacy: .public))...")
|
||||
|
||||
guard hashString == expectedHash else { throw VerificationError.mismatchedHash(hashString, expectedHash: expectedHash, app: app) }
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,10 @@ struct Server: Equatable
|
||||
|
||||
extension Server
|
||||
{
|
||||
var localizedName: String? {
|
||||
return self.service?.name ?? self.identifier
|
||||
}
|
||||
|
||||
// Defined in extension so we can still use the automatically synthesized initializer.
|
||||
init?(service: NetService, txtData: Data)
|
||||
{
|
||||
|
||||
@@ -80,7 +80,7 @@ extension ServerManager
|
||||
case .wired:
|
||||
guard let incomingConnectionsSemaphore = self.incomingConnectionsSemaphore else { return finish(.failure(ALTServerError(.connectionFailed))) }
|
||||
|
||||
print("Waiting for incoming connection...")
|
||||
Logger.sideload.debug("Waiting for incoming connection...")
|
||||
|
||||
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
|
||||
@@ -104,7 +104,7 @@ extension ServerManager
|
||||
case .wireless:
|
||||
guard let service = server.service else { return finish(.failure(ALTServerError(.connectionFailed))) }
|
||||
|
||||
print("Connecting to service:", service)
|
||||
Logger.sideload.debug("Connecting to AltServer: \(service.name, privacy: .public)")
|
||||
|
||||
let connection = NWConnection(to: .service(name: service.name, type: service.type, domain: service.domain, interface: nil), using: .tcp)
|
||||
self.connectToRemoteServer(server, connection: connection, completion: finish(_:))
|
||||
@@ -166,17 +166,32 @@ private extension ServerManager
|
||||
|
||||
func connectToRemoteServer(_ server: Server, connection: NWConnection, completion: @escaping (Result<Connection, Error>) -> Void)
|
||||
{
|
||||
let serverName: String
|
||||
if let localizedName = server.localizedName
|
||||
{
|
||||
serverName = String(format: NSLocalizedString("remote AltServer %@", comment: ""), localizedName)
|
||||
}
|
||||
else if server.connectionType == .wired
|
||||
{
|
||||
serverName = NSLocalizedString("wired AltServer", comment: "")
|
||||
}
|
||||
else
|
||||
{
|
||||
serverName = NSLocalizedString("AltServer", comment: "")
|
||||
}
|
||||
|
||||
connection.stateUpdateHandler = { [unowned connection] (state) in
|
||||
switch state
|
||||
{
|
||||
case .failed(let error):
|
||||
print("Failed to connect to service \(server.service?.name ?? "").", error)
|
||||
Logger.sideload.error("Failed to connect to \(serverName, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
completion(.failure(OperationError.connectionFailed))
|
||||
|
||||
case .cancelled:
|
||||
completion(.failure(OperationError.cancelled))
|
||||
|
||||
case .ready:
|
||||
Logger.sideload.notice("Connected to \(serverName, privacy: .public)!")
|
||||
let connection = NetworkConnection(connection)
|
||||
completion(.success(connection))
|
||||
|
||||
@@ -201,10 +216,12 @@ private extension ServerManager
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
print("Could not connect to AltDaemon XPC service \(machServiceName).", error)
|
||||
Logger.sideload.error("Could not connect to AltDaemon XPC service \(machServiceName, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
completion(.failure(error))
|
||||
|
||||
case .success: completion(.success(connection))
|
||||
case .success:
|
||||
Logger.sideload.notice("Connected to AltDaemon XPC service \(machServiceName, privacy: .public)!")
|
||||
completion(.success(connection))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
AltStoreCore/Extensions/Logger+AltStore.swift
Normal file
33
AltStoreCore/Extensions/Logger+AltStore.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Logger+AltStore.swift
|
||||
// AltStoreCore
|
||||
//
|
||||
// Created by Riley Testut on 10/2/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
@_exported import OSLog
|
||||
|
||||
public extension Logger
|
||||
{
|
||||
static let altstoreSubsystem = Bundle.main.bundleIdentifier!
|
||||
|
||||
static let sideload = Logger(subsystem: altstoreSubsystem, category: "Sideload")
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
public extension OSLogEntryLog.Level
|
||||
{
|
||||
var localizedName: String {
|
||||
switch self
|
||||
{
|
||||
case .undefined: return NSLocalizedString("Undefined", comment: "")
|
||||
case .debug: return NSLocalizedString("Debug", comment: "")
|
||||
case .info: return NSLocalizedString("Info", comment: "")
|
||||
case .notice: return NSLocalizedString("Notice", comment: "")
|
||||
case .error: return NSLocalizedString("Error", comment: "")
|
||||
case .fault: return NSLocalizedString("Fault", comment: "")
|
||||
@unknown default: return NSLocalizedString("Unknown", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user