diff --git a/AltPlugin/Info.plist b/AltPlugin/Info.plist
index 6188d46b..e24386aa 100644
--- a/AltPlugin/Info.plist
+++ b/AltPlugin/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 1.0
+ $(MARKETING_VERSION)
CFBundleVersion
1
NSHumanReadableCopyright
@@ -58,5 +58,9 @@
# For mail version 13.0 (3594.4.2) on OS X Version 10.15 (build 19A558d)
6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053
+ Supported11.0PluginCompatibilityUUIDs
+
+ D985F0E4-3BBC-4B95-BBA1-12056AC4A531
+
diff --git a/AltServer/AltPlugin.zip b/AltServer/AltPlugin.zip
new file mode 100644
index 00000000..d6c5b41c
Binary files /dev/null and b/AltServer/AltPlugin.zip differ
diff --git a/AltServer/AppDelegate.swift b/AltServer/AppDelegate.swift
index a83fefd8..773d6305 100644
--- a/AltServer/AppDelegate.swift
+++ b/AltServer/AppDelegate.swift
@@ -12,32 +12,12 @@ import UserNotifications
import AltSign
import LaunchAtLogin
-import STPrivilegedTask
-
-private let pluginDirectoryURL = URL(fileURLWithPath: "/Library/Mail/Bundles", isDirectory: true)
-private let pluginURL = pluginDirectoryURL.appendingPathComponent("AltPlugin.mailbundle")
-
-enum PluginError: LocalizedError
-{
- case cancelled
- case unknown
- case taskError(String)
- case taskErrorCode(Int)
-
- var errorDescription: String? {
- switch self
- {
- case .cancelled: return NSLocalizedString("Mail plug-in installation was cancelled.", comment: "")
- case .unknown: return NSLocalizedString("Failed to install Mail plug-in.", comment: "")
- case .taskError(let output): return output
- case .taskErrorCode(let errorCode): return String(format: NSLocalizedString("There was an error installing the Mail plug-in. (Error Code: %@)", comment: ""), NSNumber(value: errorCode))
- }
- }
-}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
+ private let pluginManager = PluginManager()
+
private var statusItem: NSStatusItem?
private var connectedDevices = [ALTDevice]()
@@ -52,11 +32,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private weak var authenticationAppleIDTextField: NSTextField?
private weak var authenticationPasswordTextField: NSSecureTextField?
- private var isMailPluginInstalled: Bool {
- let isMailPluginInstalled = FileManager.default.fileExists(atPath: pluginURL.path)
- return isMailPluginInstalled
- }
-
func applicationDidFinishLaunching(_ aNotification: Notification)
{
UserDefaults.standard.registerDefaults()
@@ -89,6 +64,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
UserDefaults.standard.didPresentInitialNotification = true
}
}
+
+ if self.pluginManager.isUpdateAvailable
+ {
+ self.installMailPlugin()
+ }
}
func applicationWillTerminate(_ aNotification: Notification)
@@ -191,27 +171,13 @@ private extension AppDelegate
}
}
- if !self.isMailPluginInstalled
+ if !self.pluginManager.isMailPluginInstalled || self.pluginManager.isUpdateAvailable
{
self.installMailPlugin { (result) in
- DispatchQueue.main.async {
- switch result
- {
- case .failure(PluginError.cancelled): break
- case .failure(let error):
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Failed to Install Mail Plug-in", comment: "")
- alert.informativeText = error.localizedDescription
- alert.runModal()
-
- case .success:
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Mail Plug-in Installed", comment: "")
- alert.informativeText = NSLocalizedString("Please restart Mail and enable AltPlugin in Mail's Preferences. Mail must be running when installing or refreshing apps with AltServer.", comment: "")
- alert.runModal()
-
- install()
- }
+ switch result
+ {
+ case .failure: break
+ case .success: install()
}
}
}
@@ -228,216 +194,67 @@ private extension AppDelegate
@objc func handleInstallMailPluginMenuItem(_ item: NSMenuItem)
{
- if self.isMailPluginInstalled
+ if !self.pluginManager.isMailPluginInstalled || self.pluginManager.isUpdateAvailable
{
- self.uninstallMailPlugin { (result) in
- DispatchQueue.main.async {
- switch result
- {
- case .failure(PluginError.cancelled): break
- case .failure(let error):
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Failed to Uninstall Mail Plug-in", comment: "")
- alert.informativeText = error.localizedDescription
- alert.runModal()
-
- case .success:
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Mail Plug-in Uninstalled", comment: "")
- alert.informativeText = NSLocalizedString("Please restart Mail for changes to take effect. You will not be able to use AltServer until the plug-in is reinstalled.", comment: "")
- alert.runModal()
- }
- }
- }
+ self.installMailPlugin()
}
else
{
- self.installMailPlugin { (result) in
- DispatchQueue.main.async {
- switch result
- {
- case .failure(PluginError.cancelled): break
- case .failure(let error):
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Failed to Install Mail Plug-in", comment: "")
- alert.informativeText = error.localizedDescription
- alert.runModal()
-
- case .success:
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Mail Plug-in Installed", comment: "")
- alert.informativeText = NSLocalizedString("Please restart Mail and enable AltPlugin in Mail's Preferences. Mail must be running when installing or refreshing apps with AltServer.", comment: "")
- alert.runModal()
- }
- }
- }
+ self.uninstallMailPlugin()
}
}
- func installMailPlugin(completionHandler: @escaping (Result) -> Void)
+ private func installMailPlugin(completion: ((Result) -> Void)? = nil)
{
- do
- {
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Install Mail Plug-in", comment: "")
- alert.informativeText = NSLocalizedString("AltServer requires a Mail plug-in in order to retrieve necessary information about your Apple ID. Would you like to install it now?", comment: "")
-
- alert.addButton(withTitle: NSLocalizedString("Install Plug-in", comment: ""))
- alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
-
- NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
-
- let response = alert.runModal()
- guard response == .alertFirstButtonReturn else { throw PluginError.cancelled }
-
- self.downloadPlugin { (result) in
- do
+ self.pluginManager.installMailPlugin { (result) in
+ DispatchQueue.main.async {
+ switch result
{
- let fileURL = try result.get()
- defer { try? FileManager.default.removeItem(at: fileURL) }
+ case .failure(PluginError.cancelled): break
+ case .failure(let error):
+ let alert = NSAlert()
+ alert.messageText = NSLocalizedString("Failed to Install Mail Plug-in", comment: "")
+ alert.informativeText = error.localizedDescription
+ alert.runModal()
- // Ensure plug-in directory exists.
- let authorization = try self.runAndKeepAuthorization("mkdir", arguments: ["-p", pluginDirectoryURL.path])
-
- // Unzip AltPlugin to plug-ins directory.
- try self.runAndKeepAuthorization("unzip", arguments: ["-o", fileURL.path, "-d", pluginDirectoryURL.path], authorization: authorization)
- guard self.isMailPluginInstalled else { throw PluginError.unknown }
-
- // Enable Mail plug-in preferences.
- try self.run("defaults", arguments: ["write", "/Library/Preferences/com.apple.mail", "EnableBundles", "-bool", "YES"], authorization: authorization)
-
- print("Finished installing Mail plug-in!")
-
- completionHandler(.success(()))
- }
- catch
- {
- completionHandler(.failure(error))
- }
- }
- }
- catch
- {
- completionHandler(.failure(PluginError.cancelled))
- }
- }
-
- func downloadPlugin(completionHandler: @escaping (Result) -> Void)
- {
- let pluginURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altserver/altplugin/1_0.zip")!
-
- let downloadTask = URLSession.shared.downloadTask(with: pluginURL) { (fileURL, response, error) in
- if let fileURL = fileURL
- {
- print("Downloaded plugin to URL:", fileURL)
- completionHandler(.success(fileURL))
- }
- else
- {
- completionHandler(.failure(error ?? PluginError.unknown))
- }
- }
-
- downloadTask.resume()
- }
-
- func uninstallMailPlugin(completionHandler: @escaping (Result) -> Void)
- {
- let alert = NSAlert()
- alert.messageText = NSLocalizedString("Uninstall Mail Plug-in", comment: "")
- alert.informativeText = NSLocalizedString("Are you sure you want to uninstall the AltServer Mail plug-in? You will no longer be able to install or refresh apps with AltStore.", comment: "")
-
- alert.addButton(withTitle: NSLocalizedString("Uninstall Plug-in", comment: ""))
- alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
-
- NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
-
- let response = alert.runModal()
- guard response == .alertFirstButtonReturn else { return completionHandler(.failure(PluginError.cancelled)) }
-
- DispatchQueue.global().async {
- do
- {
- if FileManager.default.fileExists(atPath: pluginURL.path)
- {
- // Delete Mail plug-in from privileged directory.
- try self.run("rm", arguments: ["-rf", pluginURL.path])
+ case .success:
+ let alert = NSAlert()
+ alert.messageText = NSLocalizedString("Mail Plug-in Installed", comment: "")
+ alert.informativeText = NSLocalizedString("Please restart Mail and enable AltPlugin in Mail's Preferences. Mail must be running when installing or refreshing apps with AltServer.", comment: "")
+ alert.runModal()
}
- completionHandler(.success(()))
- }
- catch
- {
- completionHandler(.failure(error))
+ completion?(result)
}
}
}
-}
-
-private extension AppDelegate
-{
- func run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws
- {
- _ = try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: true)
- }
- @discardableResult
- func runAndKeepAuthorization(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws -> AuthorizationRef
+ private func uninstallMailPlugin()
{
- return try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: false)
- }
-
- func _run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil, freeAuthorization: Bool) throws -> AuthorizationRef
- {
- var launchPath = "/usr/bin/" + program
- if !FileManager.default.fileExists(atPath: launchPath)
- {
- launchPath = "/bin/" + program
- }
-
- print("Running program:", launchPath)
-
- let task = STPrivilegedTask()
- task.launchPath = launchPath
- task.arguments = arguments
- task.freeAuthorizationWhenDone = freeAuthorization
-
- let errorCode: OSStatus
-
- if let authorization = authorization
- {
- errorCode = task.launch(withAuthorization: authorization)
- }
- else
- {
- errorCode = task.launch()
- }
-
- guard errorCode == 0 else { throw PluginError.taskErrorCode(Int(errorCode)) }
-
- task.waitUntilExit()
-
- print("Exit code:", task.terminationStatus)
-
- guard task.terminationStatus == 0 else {
- let outputData = task.outputFileHandle.readDataToEndOfFile()
-
- if let outputString = String(data: outputData, encoding: .utf8), !outputString.isEmpty
- {
- throw PluginError.taskError(outputString)
+ self.pluginManager.uninstallMailPlugin { (result) in
+ DispatchQueue.main.async {
+ switch result
+ {
+ case .failure(PluginError.cancelled): break
+ case .failure(let error):
+ let alert = NSAlert()
+ alert.messageText = NSLocalizedString("Failed to Uninstall Mail Plug-in", comment: "")
+ alert.informativeText = error.localizedDescription
+ alert.runModal()
+
+ case .success:
+ let alert = NSAlert()
+ alert.messageText = NSLocalizedString("Mail Plug-in Uninstalled", comment: "")
+ alert.informativeText = NSLocalizedString("Please restart Mail for changes to take effect. You will not be able to use AltServer until the plug-in is reinstalled.", comment: "")
+ alert.runModal()
+ }
}
-
- throw PluginError.taskErrorCode(Int(task.terminationStatus))
}
-
- guard let authorization = task.authorization else { throw PluginError.unknown }
- return authorization
}
}
extension AppDelegate: NSMenuDelegate
{
-
func menuWillOpen(_ menu: NSMenu)
{
guard menu == self.appMenu else { return }
@@ -448,7 +265,11 @@ extension AppDelegate: NSMenuDelegate
self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:))
self.launchAtLoginMenuItem.state = LaunchAtLogin.isEnabled ? .on : .off
- if self.isMailPluginInstalled
+ if self.pluginManager.isUpdateAvailable
+ {
+ self.installMailPluginMenuItem.title = NSLocalizedString("Update Mail Plug-in", comment: "")
+ }
+ else if self.pluginManager.isMailPluginInstalled
{
self.installMailPluginMenuItem.title = NSLocalizedString("Uninstall Mail Plug-in", comment: "")
}
diff --git a/AltServer/PluginManager.swift b/AltServer/PluginManager.swift
new file mode 100644
index 00000000..d5b2e3d2
--- /dev/null
+++ b/AltServer/PluginManager.swift
@@ -0,0 +1,310 @@
+//
+// PluginManager.swift
+// AltServer
+//
+// Created by Riley Testut on 9/16/20.
+// Copyright © 2020 Riley Testut. All rights reserved.
+//
+
+import Foundation
+import AppKit
+import CryptoKit
+
+import STPrivilegedTask
+
+private let pluginDirectoryURL = URL(fileURLWithPath: "/Library/Mail/Bundles", isDirectory: true)
+private let pluginURL = pluginDirectoryURL.appendingPathComponent("AltPlugin.mailbundle")
+
+enum PluginError: LocalizedError
+{
+ case cancelled
+ case unknown
+ case notFound
+ case mismatchedHash(hash: String, expectedHash: String)
+ case taskError(String)
+ case taskErrorCode(Int)
+
+ var errorDescription: String? {
+ switch self
+ {
+ case .cancelled: return NSLocalizedString("Mail plug-in installation was cancelled.", comment: "")
+ case .unknown: return NSLocalizedString("Failed to install Mail plug-in.", comment: "")
+ case .notFound: return NSLocalizedString("The Mail plug-in does not exist at the requested URL.", comment: "")
+ case .mismatchedHash(let hash, let expectedHash): return String(format: NSLocalizedString("The hash of the downloaded Mail plug-in does not match the expected hash.\n\nHash:\n%@\n\nExpected Hash:\n%@", comment: ""), hash, expectedHash)
+ case .taskError(let output): return output
+ case .taskErrorCode(let errorCode): return String(format: NSLocalizedString("There was an error installing the Mail plug-in. (Error Code: %@)", comment: ""), NSNumber(value: errorCode))
+ }
+ }
+}
+
+struct PluginVersion
+{
+ var url: URL
+ var sha256Hash: String
+ var version: String
+
+ static let v1_0 = PluginVersion(url: URL(string: "https://f000.backblazeb2.com/file/altstore/altserver/altplugin/1_0.zip")!,
+ sha256Hash: "070e9b7e1f74e7a6474d36253ab5a3623ff93892acc9e1043c3581f2ded12200",
+ version: "1.0")
+
+ static let v1_1 = PluginVersion(url: Bundle.main.url(forResource: "AltPlugin", withExtension: "zip")!,
+ sha256Hash: "cd1e8c85cbb1935d2874376566671f3c5823101d4933fc6ee63bab8b2a37f800",
+ version: "1.1")
+}
+
+class PluginManager
+{
+ var isMailPluginInstalled: Bool {
+ let isMailPluginInstalled = FileManager.default.fileExists(atPath: pluginURL.path)
+ return isMailPluginInstalled
+ }
+
+ var isUpdateAvailable: Bool {
+ guard let bundle = Bundle(url: pluginURL) else { return false }
+
+ // Load Info.plist from disk because Bundle.infoDictionary is cached by system.
+ let infoDictionaryURL = bundle.bundleURL.appendingPathComponent("Contents/Info.plist")
+ guard let infoDictionary = NSDictionary(contentsOf: infoDictionaryURL) as? [String: Any],
+ let version = infoDictionary["CFBundleShortVersionString"] as? String
+ else { return false }
+
+ let isUpdateAvailable = (version != self.preferredVersion.version)
+ return isUpdateAvailable
+ }
+
+ private var preferredVersion: PluginVersion {
+ if #available(macOS 11, *)
+ {
+ return .v1_1
+ }
+ else
+ {
+ return .v1_0
+ }
+ }
+}
+
+extension PluginManager
+{
+ func installMailPlugin(completionHandler: @escaping (Result) -> Void)
+ {
+ do
+ {
+ let alert = NSAlert()
+
+ if self.isUpdateAvailable
+ {
+ alert.messageText = NSLocalizedString("Update Mail Plug-in", comment: "")
+ alert.informativeText = NSLocalizedString("An update is available for AltServer's Mail plug-in. Please update the plug-in now in order to keep using AltStore.", comment: "")
+
+ alert.addButton(withTitle: NSLocalizedString("Update Plug-in", comment: ""))
+ alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
+ }
+ else
+ {
+ alert.messageText = NSLocalizedString("Install Mail Plug-in", comment: "")
+ alert.informativeText = NSLocalizedString("AltServer requires a Mail plug-in in order to retrieve necessary information about your Apple ID. Would you like to install it now?", comment: "")
+
+ alert.addButton(withTitle: NSLocalizedString("Install Plug-in", comment: ""))
+ alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
+ }
+
+ NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
+
+ let response = alert.runModal()
+ guard response == .alertFirstButtonReturn else { throw PluginError.cancelled }
+
+ self.downloadPlugin { (result) in
+ do
+ {
+ let fileURL = try result.get()
+
+ // Ensure plug-in directory exists.
+ let authorization = try self.runAndKeepAuthorization("mkdir", arguments: ["-p", pluginDirectoryURL.path])
+
+ // Create temporary directory.
+ let temporaryDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
+ try FileManager.default.createDirectory(at: temporaryDirectoryURL, withIntermediateDirectories: true, attributes: nil)
+ defer { try? FileManager.default.removeItem(at: temporaryDirectoryURL) }
+
+ // Unzip AltPlugin to temporary directory.
+ try self.runAndKeepAuthorization("unzip", arguments: ["-o", fileURL.path, "-d", temporaryDirectoryURL.path], authorization: authorization)
+
+ if FileManager.default.fileExists(atPath: pluginURL.path)
+ {
+ // Delete existing Mail plug-in.
+ try self.runAndKeepAuthorization("rm", arguments: ["-rf", pluginURL.path], authorization: authorization)
+ }
+
+ // Copy AltPlugin to Mail plug-ins directory.
+ // Must be separate step than unzip to prevent macOS from considering plug-in corrupted.
+ let unzippedPluginURL = temporaryDirectoryURL.appendingPathComponent(pluginURL.lastPathComponent)
+ try self.runAndKeepAuthorization("cp", arguments: ["-R", unzippedPluginURL.path, pluginDirectoryURL.path], authorization: authorization)
+
+ guard self.isMailPluginInstalled else { throw PluginError.unknown }
+
+ // Enable Mail plug-in preferences.
+ try self.run("defaults", arguments: ["write", "/Library/Preferences/com.apple.mail", "EnableBundles", "-bool", "YES"], authorization: authorization)
+
+ print("Finished installing Mail plug-in!")
+
+ completionHandler(.success(()))
+ }
+ catch
+ {
+ completionHandler(.failure(error))
+ }
+ }
+ }
+ catch
+ {
+ completionHandler(.failure(PluginError.cancelled))
+ }
+ }
+
+ func uninstallMailPlugin(completionHandler: @escaping (Result) -> Void)
+ {
+ let alert = NSAlert()
+ alert.messageText = NSLocalizedString("Uninstall Mail Plug-in", comment: "")
+ alert.informativeText = NSLocalizedString("Are you sure you want to uninstall the AltServer Mail plug-in? You will no longer be able to install or refresh apps with AltStore.", comment: "")
+
+ alert.addButton(withTitle: NSLocalizedString("Uninstall Plug-in", comment: ""))
+ alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
+
+ NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
+
+ let response = alert.runModal()
+ guard response == .alertFirstButtonReturn else { return completionHandler(.failure(PluginError.cancelled)) }
+
+ DispatchQueue.global().async {
+ do
+ {
+ if FileManager.default.fileExists(atPath: pluginURL.path)
+ {
+ // Delete Mail plug-in from privileged directory.
+ try self.run("rm", arguments: ["-rf", pluginURL.path])
+ }
+
+ completionHandler(.success(()))
+ }
+ catch
+ {
+ completionHandler(.failure(error))
+ }
+ }
+ }
+}
+
+private extension PluginManager
+{
+ func downloadPlugin(completion: @escaping (Result) -> Void)
+ {
+ let pluginVersion = self.preferredVersion
+
+ func finish(_ result: Result)
+ {
+ do
+ {
+ let fileURL = try result.get()
+
+ if #available(OSX 10.15, *)
+ {
+ let data = try Data(contentsOf: fileURL)
+ let sha256Hash = SHA256.hash(data: data)
+ let hashString = sha256Hash.compactMap { String(format: "%02x", $0) }.joined()
+
+ print("Comparing Mail plug-in hash (\(hashString)) against expected hash (\(pluginVersion.sha256Hash))...")
+ guard hashString == pluginVersion.sha256Hash else { throw PluginError.mismatchedHash(hash: hashString, expectedHash: pluginVersion.sha256Hash) }
+ }
+
+ completion(.success(fileURL))
+ }
+ catch
+ {
+ completion(.failure(error))
+ }
+ }
+
+ if pluginVersion.url.isFileURL
+ {
+ finish(.success(pluginVersion.url))
+ }
+ else
+ {
+ let downloadTask = URLSession.shared.downloadTask(with: pluginVersion.url) { (fileURL, response, error) in
+ if let response = response as? HTTPURLResponse
+ {
+ guard response.statusCode != 404 else { return finish(.failure(PluginError.notFound)) }
+ }
+
+ let result = Result(fileURL, error)
+ finish(result)
+
+ if let fileURL = fileURL
+ {
+ try? FileManager.default.removeItem(at: fileURL)
+ }
+ }
+
+ downloadTask.resume()
+ }
+ }
+
+ func run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws
+ {
+ _ = try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: true)
+ }
+
+ @discardableResult
+ func runAndKeepAuthorization(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws -> AuthorizationRef
+ {
+ return try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: false)
+ }
+
+ func _run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil, freeAuthorization: Bool) throws -> AuthorizationRef
+ {
+ var launchPath = "/usr/bin/" + program
+ if !FileManager.default.fileExists(atPath: launchPath)
+ {
+ launchPath = "/bin/" + program
+ }
+
+ print("Running program:", launchPath)
+
+ let task = STPrivilegedTask()
+ task.launchPath = launchPath
+ task.arguments = arguments
+ task.freeAuthorizationWhenDone = freeAuthorization
+
+ let errorCode: OSStatus
+
+ if let authorization = authorization
+ {
+ errorCode = task.launch(withAuthorization: authorization)
+ }
+ else
+ {
+ errorCode = task.launch()
+ }
+
+ guard errorCode == 0 else { throw PluginError.taskErrorCode(Int(errorCode)) }
+
+ task.waitUntilExit()
+
+ print("Exit code:", task.terminationStatus)
+
+ guard task.terminationStatus == 0 else {
+ let outputData = task.outputFileHandle.readDataToEndOfFile()
+
+ if let outputString = String(data: outputData, encoding: .utf8), !outputString.isEmpty
+ {
+ throw PluginError.taskError(outputString)
+ }
+
+ throw PluginError.taskErrorCode(Int(task.terminationStatus))
+ }
+
+ guard let authorization = task.authorization else { throw PluginError.unknown }
+ return authorization
+ }
+}
diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj
index fc517814..a9e8303c 100644
--- a/AltStore.xcodeproj/project.pbxproj
+++ b/AltStore.xcodeproj/project.pbxproj
@@ -239,6 +239,7 @@
BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */; };
BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */; };
BFC57A702416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */; };
+ BFC712BB2512B9CF00AB5EBE /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */; };
BFC712C32512D5F100AB5EBE /* XPCConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C12512D5F100AB5EBE /* XPCConnection.swift */; };
BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C12512D5F100AB5EBE /* XPCConnection.swift */; };
BFC712C52512D5F100AB5EBE /* XPCConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */; };
@@ -294,6 +295,7 @@
BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */; };
BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE338E722F10E56002E24B9 /* LaunchViewController.swift */; };
BFE48975238007CE003239E0 /* AnisetteDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE48974238007CE003239E0 /* AnisetteDataManager.swift */; };
+ BFE4FFB6251BF7BA0018CF9B /* AltPlugin.zip in Resources */ = {isa = PBXBuildFile; fileRef = BFE4FFB5251BF7BA0018CF9B /* AltPlugin.zip */; };
BFE60738231ADF49002B0E8E /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFE60737231ADF49002B0E8E /* Settings.storyboard */; };
BFE6073A231ADF82002B0E8E /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE60739231ADF82002B0E8E /* SettingsViewController.swift */; };
BFE6073C231AE1E7002B0E8E /* SettingsHeaderFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFE6073B231AE1E7002B0E8E /* SettingsHeaderFooterView.xib */; };
@@ -670,6 +672,7 @@
BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeactivateAppOperation.swift; sourceTree = ""; };
BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledAppsCollectionHeaderView.swift; sourceTree = ""; };
BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InstalledAppsCollectionHeaderView.xib; sourceTree = ""; };
+ BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = ""; };
BFC712C12512D5F100AB5EBE /* XPCConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XPCConnection.swift; sourceTree = ""; };
BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XPCConnectionHandler.swift; sourceTree = ""; };
BFC84A4C2421A19100853474 /* SourcesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesViewController.swift; sourceTree = ""; };
@@ -731,6 +734,7 @@
BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchSourceOperation.swift; sourceTree = ""; };
BFE338E722F10E56002E24B9 /* LaunchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchViewController.swift; sourceTree = ""; };
BFE48974238007CE003239E0 /* AnisetteDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnisetteDataManager.swift; sourceTree = ""; };
+ BFE4FFB5251BF7BA0018CF9B /* AltPlugin.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = AltPlugin.zip; sourceTree = ""; };
BFE60737231ADF49002B0E8E /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; };
BFE60739231ADF82002B0E8E /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; };
BFE6073B231AE1E7002B0E8E /* SettingsHeaderFooterView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsHeaderFooterView.xib; sourceTree = ""; };
@@ -925,6 +929,7 @@
BF45868F229872EA00BD7491 /* AppDelegate.swift */,
BF458695229872EA00BD7491 /* Main.storyboard */,
BFE48974238007CE003239E0 /* AnisetteDataManager.swift */,
+ BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */,
BF703195229F36FF006E110F /* Devices */,
BFD52BDC22A0A659000B7ED1 /* Connections */,
BF055B4A233B528B0086DEA9 /* Extensions */,
@@ -1256,6 +1261,7 @@
BF703194229F36F6006E110F /* Resources */ = {
isa = PBXGroup;
children = (
+ BFE4FFB5251BF7BA0018CF9B /* AltPlugin.zip */,
BF458693229872EA00BD7491 /* Assets.xcassets */,
);
name = Resources;
@@ -1934,6 +1940,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ BFE4FFB6251BF7BA0018CF9B /* AltPlugin.zip in Resources */,
BF458694229872EA00BD7491 /* Assets.xcassets in Resources */,
BF458697229872EA00BD7491 /* Main.storyboard in Resources */,
);
@@ -2169,6 +2176,7 @@
BFECAC8724FD950B0077C41F /* Bundle+AltStore.swift in Sources */,
BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */,
BF18BFFD2485A1E400DD5981 /* WiredConnectionHandler.swift in Sources */,
+ BFC712BB2512B9CF00AB5EBE /* PluginManager.swift in Sources */,
BFECAC8224FD950B0077C41F /* ServerProtocol.swift in Sources */,
BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */,
BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */,
@@ -2818,6 +2826,7 @@
"/Users/Riley/Library/Developer/Xcode/DerivedData/AltStore-bhqnkrahfutztzeudtxhcxccgtlo/Build/Products/Debug",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltPlugin;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
@@ -2847,6 +2856,7 @@
"/Users/Riley/Library/Developer/Xcode/DerivedData/AltStore-bhqnkrahfutztzeudtxhcxccgtlo/Build/Products/Debug",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltPlugin;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;