Files
SideStore/Sources/SideStoreAppKit/Operations/InstallAppOperation.swift

179 lines
6.9 KiB
Swift
Raw Normal View History

//
// InstallAppOperation.swift
// AltStore
//
// Created by Riley Testut on 6/19/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
import Network
import AltSign
2023-03-01 00:48:36 -05:00
import SideStoreCore
2023-03-01 14:36:52 -05:00
import RoxasUIKit
import MiniMuxerSwift
import minimuxer
@objc(InstallAppOperation)
2023-03-01 00:48:36 -05:00
final class InstallAppOperation: ResultOperation<InstalledApp> {
let context: InstallAppOperationContext
2023-03-01 00:48:36 -05:00
private var didCleanUp = false
2023-03-01 00:48:36 -05:00
init(context: InstallAppOperationContext) {
self.context = context
2023-03-01 00:48:36 -05:00
super.init()
2023-03-01 00:48:36 -05:00
progress.totalUnitCount = 100
}
2023-03-01 00:48:36 -05:00
override func main() {
super.main()
2023-03-01 00:48:36 -05:00
if let error = context.error {
finish(.failure(error))
return
}
2023-03-01 00:48:36 -05:00
guard
2023-03-01 00:48:36 -05:00
let certificate = context.certificate,
let resignedApp = context.resignedApp
else { return finish(.failure(OperationError.invalidParameters)) }
let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
backgroundContext.perform {
/* App */
let installedApp: InstalledApp
2023-03-01 00:48:36 -05:00
// Fetch + update rather than insert + resolve merge conflicts to prevent potential context-level conflicts.
2023-03-01 00:48:36 -05:00
if let app = InstalledApp.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(InstalledApp.bundleIdentifier), self.context.bundleIdentifier), in: backgroundContext) {
installedApp = app
2023-03-01 00:48:36 -05:00
} else {
installedApp = InstalledApp(resignedApp: resignedApp, originalBundleIdentifier: self.context.bundleIdentifier, certificateSerialNumber: certificate.serialNumber, context: backgroundContext)
}
2023-03-01 00:48:36 -05:00
installedApp.update(resignedApp: resignedApp, certificateSerialNumber: certificate.serialNumber)
installedApp.needsResign = false
2023-03-01 00:48:36 -05:00
if let team = DatabaseManager.shared.activeTeam(in: backgroundContext) {
installedApp.team = team
}
2023-03-01 00:48:36 -05:00
/* App Extensions */
2020-01-21 16:53:34 -08:00
var installedExtensions = Set<InstalledExtension>()
2023-03-01 00:48:36 -05:00
2020-01-21 16:53:34 -08:00
if
let bundle = Bundle(url: resignedApp.fileURL),
let directory = bundle.builtInPlugInsURL,
let enumerator = FileManager.default.enumerator(at: directory, includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants])
{
2023-03-01 00:48:36 -05:00
for case let fileURL as URL in enumerator {
2020-01-21 16:53:34 -08:00
guard let appExtensionBundle = Bundle(url: fileURL) else { continue }
guard let appExtension = ALTApplication(fileURL: appExtensionBundle.bundleURL) else { continue }
2023-03-01 00:48:36 -05:00
let parentBundleID = self.context.bundleIdentifier
let resignedParentBundleID = resignedApp.bundleIdentifier
2023-03-01 00:48:36 -05:00
let resignedBundleID = appExtension.bundleIdentifier
let originalBundleID = resignedBundleID.replacingOccurrences(of: resignedParentBundleID, with: parentBundleID)
2023-03-01 00:48:36 -05:00
print("`parentBundleID`: \(parentBundleID)")
print("`resignedParentBundleID`: \(resignedParentBundleID)")
print("`resignedBundleID`: \(resignedBundleID)")
print("`originalBundleID`: \(originalBundleID)")
2023-03-01 00:48:36 -05:00
2020-01-21 16:53:34 -08:00
let installedExtension: InstalledExtension
2023-03-01 00:48:36 -05:00
if let appExtension = installedApp.appExtensions.first(where: { $0.bundleIdentifier == originalBundleID }) {
2020-01-21 16:53:34 -08:00
installedExtension = appExtension
2023-03-01 00:48:36 -05:00
} else {
installedExtension = InstalledExtension(resignedAppExtension: appExtension, originalBundleIdentifier: originalBundleID, context: backgroundContext)
2020-01-21 16:53:34 -08:00
}
2023-03-01 00:48:36 -05:00
installedExtension.update(resignedAppExtension: appExtension)
2023-03-01 00:48:36 -05:00
2020-01-21 16:53:34 -08:00
installedExtensions.insert(installedExtension)
}
}
2023-03-01 00:48:36 -05:00
2020-01-21 16:53:34 -08:00
installedApp.appExtensions = installedExtensions
2023-03-01 00:48:36 -05:00
self.context.beginInstallationHandler?(installedApp)
2023-03-01 00:48:36 -05:00
// Temporary directory and resigned .ipa no longer needed, so delete them now to ensure AltStore doesn't quit before we get the chance to.
self.cleanUp()
2023-03-01 00:48:36 -05:00
var activeProfiles: Set<String>?
2023-03-01 00:48:36 -05:00
if let sideloadedAppsLimit = UserDefaults.standard.activeAppsLimit {
// When installing these new profiles, AltServer will remove all non-active profiles to ensure we remain under limit.
2023-03-01 00:48:36 -05:00
let fetchRequest = InstalledApp.activeAppsFetchRequest()
fetchRequest.includesPendingChanges = false
2023-03-01 00:48:36 -05:00
var activeApps = InstalledApp.fetch(fetchRequest, in: backgroundContext)
2023-03-01 00:48:36 -05:00
if !activeApps.contains(installedApp) {
let activeAppsCount = activeApps.map { $0.requiredActiveSlots }.reduce(0, +)
2023-03-01 00:48:36 -05:00
let availableActiveApps = max(sideloadedAppsLimit - activeAppsCount, 0)
2023-03-01 00:48:36 -05:00
if installedApp.requiredActiveSlots <= availableActiveApps {
// This app has not been explicitly activated, but there are enough slots available,
// so implicitly activate it.
installedApp.isActive = true
activeApps.append(installedApp)
2023-03-01 00:48:36 -05:00
} else {
installedApp.isActive = false
}
}
2023-03-01 00:48:36 -05:00
activeProfiles = Set(activeApps.flatMap { installedApp -> [String] in
let appExtensionProfiles = installedApp.appExtensions.map { $0.resignedBundleIdentifier }
return [installedApp.resignedBundleIdentifier] + appExtensionProfiles
})
}
2023-03-01 00:48:36 -05:00
Implement emotional damage (#95) * Implement em_proxy * Update libimobiledevice * Add minimuxer library to Xcode * Build missing C files for libimobiledevice * Remove objective C library * Add pairing file to Info.plist * Heartbeat self on startup * Enable JIT on-device * Implement on-device installation * Fix OpenSSL header errors * Random submodule bullcrap go * Search release folder for emotional damage * Clean dependencies * Build Rust dependencies attempt 1/999 * Update em_proxy * Implement refreshing apps * Clean up old operations * Remove all AltServer code * Remove files from Xcode project * Implement auto mounting the developer DMG * Recover from app being backgrounded * Fixed keeping pairing file in app after updating SideStore (#3) * Use compliant error handling for minimuxer * Fix app failing to install * Don't kill proxy on backgrounding * Makes sure the ALTPairingFile gets transferred even if team IDs change (#4) * Step 1 to allow SideStore to resign itself * Update ResignAppOperation.swift * Adding cache for action runner (#5) * Start caching commit for actions Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Update build.yml Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Update build.yml Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Use rust lib directories to cache Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Cache cargo also Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Fix spacing Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Replace cargo id for caching Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Remove cache if statements Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Add disconnected WireGuard detection * Add minimuxer logging Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> Co-authored-by: jawshoeadan <62785552+jawshoeadan@users.noreply.github.com> Co-authored-by: Joelle Stickney <joellestickney@gmail.com> Co-authored-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-02 17:58:59 -07:00
let ns_bundle = NSString(string: installedApp.bundleIdentifier)
let ns_bundle_ptr = UnsafeMutablePointer<CChar>(mutating: ns_bundle.utf8String)
2023-03-01 00:48:36 -05:00
Implement emotional damage (#95) * Implement em_proxy * Update libimobiledevice * Add minimuxer library to Xcode * Build missing C files for libimobiledevice * Remove objective C library * Add pairing file to Info.plist * Heartbeat self on startup * Enable JIT on-device * Implement on-device installation * Fix OpenSSL header errors * Random submodule bullcrap go * Search release folder for emotional damage * Clean dependencies * Build Rust dependencies attempt 1/999 * Update em_proxy * Implement refreshing apps * Clean up old operations * Remove all AltServer code * Remove files from Xcode project * Implement auto mounting the developer DMG * Recover from app being backgrounded * Fixed keeping pairing file in app after updating SideStore (#3) * Use compliant error handling for minimuxer * Fix app failing to install * Don't kill proxy on backgrounding * Makes sure the ALTPairingFile gets transferred even if team IDs change (#4) * Step 1 to allow SideStore to resign itself * Update ResignAppOperation.swift * Adding cache for action runner (#5) * Start caching commit for actions Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Update build.yml Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Update build.yml Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Use rust lib directories to cache Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Cache cargo also Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Fix spacing Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Replace cargo id for caching Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Remove cache if statements Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> * Add disconnected WireGuard detection * Add minimuxer logging Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com> Co-authored-by: jawshoeadan <62785552+jawshoeadan@users.noreply.github.com> Co-authored-by: Joelle Stickney <joellestickney@gmail.com> Co-authored-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-02 17:58:59 -07:00
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)))
}
}
}
2023-03-01 00:48:36 -05:00
override func finish(_ result: Result<InstalledApp, Error>) {
cleanUp()
// Only remove refreshed IPA when finished.
2023-03-01 00:48:36 -05:00
if let app = context.app {
let fileURL = InstalledApp.refreshedIPAURL(for: app)
2023-03-01 00:48:36 -05:00
do {
try FileManager.default.removeItem(at: fileURL)
2023-03-01 00:48:36 -05:00
} catch {
print("Failed to remove refreshed .ipa:", error)
}
}
2023-03-01 00:48:36 -05:00
super.finish(result)
}
}
2023-03-01 00:48:36 -05:00
private extension InstallAppOperation {
func cleanUp() {
guard !didCleanUp else { return }
didCleanUp = true
do {
try FileManager.default.removeItem(at: context.temporaryDirectory)
} catch {
print("Failed to remove temporary directory.", error)
}
}
}