Create swift package

This commit is contained in:
Joe Mattiello
2023-03-01 00:48:36 -05:00
parent 4669227567
commit 493b3783f0
409 changed files with 13707 additions and 16923 deletions

View File

@@ -0,0 +1,10 @@
//
// AltConstants.swift
//
//
// Created by Joseph Mattiello on 2/28/23.
//
import Foundation
public let ALTDeviceListeningSocket: UInt16 = 28151

View File

@@ -0,0 +1,13 @@
//
// CFNotificationName+SideStore.swift
// SideKit
//
// Created by Joseph Mattiello on 02/28/23.
// Copyright © 2023 Joseph Mattiello. All rights reserved.
//
import Foundation
public let ALTWiredServerConnectionAvailableRequest = CFNotificationName("io.altstore.Request.WiredServerConnectionAvailable" as CFString)
public let ALTWiredServerConnectionAvailableResponse = CFNotificationName("io.altstore.Response.WiredServerConnectionAvailable" as CFString)
public let ALTWiredServerConnectionStartRequest = CFNotificationName("io.altstore.Request.WiredServerConnectionStart" as CFString)

View File

@@ -0,0 +1,101 @@
//
// Connection.swift
// AltKit
//
// Created by Riley Testut on 6/1/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
import Network
import SideKit
public protocol SideConnection: Connection {
func __send(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void)
func __receiveData(expectedSize: Int, completionHandler: @escaping (Data?, Error?) -> Void)
}
public extension SideConnection {
func send(_ data: Data, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void) {
__send(data) { success, error in
let result = Result(success, error).mapError { (failure: Error) -> ALTServerError in
guard let nwError = failure as? NWError else { return ALTServerError(failure) }
return ALTServerError.lostConnection(underlyingError: nwError)
}
completionHandler(result)
}
}
func receiveData(expectedSize: Int, completionHandler: @escaping (Result<Data, ALTServerError>) -> Void) {
__receiveData(expectedSize: expectedSize) { data, error in
let result = Result(data, error).mapError { (failure: Error) -> ALTServerError in
guard let nwError = failure as? NWError else { return ALTServerError(failure) }
return ALTServerError.lostConnection(underlyingError: nwError)
}
completionHandler(result)
}
}
func send<T: Encodable>(_ response: T, shouldDisconnect: Bool = false, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void) {
func finish(_ result: Result<Void, ALTServerError>) {
completionHandler(result)
if shouldDisconnect {
// Add short delay to prevent us from dropping connection too quickly.
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
self.disconnect()
}
}
}
do {
let data = try JSONEncoder().encode(response)
let responseSize = withUnsafeBytes(of: Int32(data.count)) { Data($0) }
send(responseSize) { result in
switch result {
case let .failure(error): finish(.failure(error))
case .success:
self.send(data) { result in
switch result {
case let .failure(error): finish(.failure(error))
case .success: finish(.success(()))
}
}
}
}
} catch {
finish(.failure(.invalidResponse(underlyingError: error)))
}
}
func receiveRequest(completionHandler: @escaping (Result<ServerRequest, ALTServerError>) -> Void) {
let size = MemoryLayout<Int32>.size
print("Receiving request size from connection:", self)
receiveData(expectedSize: size) { result in
do {
let data = try result.get()
let expectedSize = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
print("Receiving request from connection: \(self)... (\(expectedSize) bytes)")
self.receiveData(expectedSize: expectedSize) { result in
do {
let data = try result.get()
let request = try JSONDecoder().decode(ServerRequest.self, from: data)
print("Received request:", request)
completionHandler(.success(request))
} catch {
completionHandler(.failure(ALTServerError(error)))
}
}
} catch {
completionHandler(.failure(ALTServerError(error)))
}
}
}
}

View File

@@ -0,0 +1,158 @@
//
// ConnectionManager.swift
// AltServer
//
// Created by Riley Testut on 5/23/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
import Network
import SideKit
public protocol RequestHandler {
func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: Connection, completionHandler: @escaping (Result<AnisetteDataResponse, Error>) -> Void)
func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: Connection, completionHandler: @escaping (Result<InstallationProgressResponse, Error>) -> Void)
func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: Connection,
completionHandler: @escaping (Result<InstallProvisioningProfilesResponse, Error>) -> Void)
func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: Connection,
completionHandler: @escaping (Result<RemoveProvisioningProfilesResponse, Error>) -> Void)
func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: Connection, completionHandler: @escaping (Result<RemoveAppResponse, Error>) -> Void)
func handleEnableUnsignedCodeExecutionRequest(_ request: EnableUnsignedCodeExecutionRequest, for connection: Connection, completionHandler: @escaping (Result<EnableUnsignedCodeExecutionResponse, Error>) -> Void)
}
public protocol ConnectionHandler: AnyObject {
associatedtype ConnectionType = Connection
var connectionHandler: ((ConnectionType) -> Void)? { get set }
var disconnectionHandler: ((ConnectionType) -> Void)? { get set }
func startListening()
func stopListening()
}
public class ConnectionManager<RequestHandlerType: RequestHandler, ConnectionType: NetworkConnection & AnyObject, ConnectionHandlerType: ConnectionHandler> where ConnectionHandlerType.ConnectionType == ConnectionType {
public let requestHandler: RequestHandlerType
public let connectionHandlers: [ConnectionHandlerType]
public var isStarted = false
private var connections = [ConnectionType]()
private let connectionsLock = NSLock()
public init(requestHandler: RequestHandlerType, connectionHandlers: [ConnectionHandlerType]) {
self.requestHandler = requestHandler
self.connectionHandlers = connectionHandlers
for handler in connectionHandlers {
handler.connectionHandler = { [weak self] connection in
self?.prepare(connection)
}
handler.disconnectionHandler = { [weak self] connection in
self?.disconnect(connection)
}
}
}
public func start() {
guard !isStarted else { return }
for connectionHandler in connectionHandlers {
connectionHandler.startListening()
}
isStarted = true
}
public func stop() {
guard isStarted else { return }
for connectionHandler in connectionHandlers {
connectionHandler.stopListening()
}
isStarted = false
}
}
private extension ConnectionManager {
func prepare(_ connection: ConnectionType) {
connectionsLock.lock()
defer { self.connectionsLock.unlock() }
guard !connections.contains(where: { $0 === connection }) else { return }
connections.append(connection)
handleRequest(for: connection)
}
func disconnect(_ connection: ConnectionType) {
connectionsLock.lock()
defer { self.connectionsLock.unlock() }
guard let index = connections.firstIndex(where: { $0 === connection }) else { return }
connections.remove(at: index)
}
func handleRequest(for connection: ConnectionType) {
func finish<T: ServerMessageProtocol>(_ result: Result<T, Error>) {
do {
let response = try result.get()
connection.send(response, shouldDisconnect: true) { result in
print("Sent response \(response) with result:", result)
}
} catch {
let response = ErrorResponse(error: ALTServerError(error))
connection.send(response, shouldDisconnect: true) { result in
print("Sent error response \(response) with result:", result)
}
}
}
connection.receiveRequest { result in
print("Received request with result:", result)
switch result {
case let .failure(error): finish(Result<ErrorResponse, Error>.failure(error))
case let .success(.anisetteData(request)):
self.requestHandler.handleAnisetteDataRequest(request, for: connection) { result in
finish(result)
}
case let .success(.prepareApp(request)):
self.requestHandler.handlePrepareAppRequest(request, for: connection) { result in
finish(result)
}
case .success(.beginInstallation): break
case let .success(.installProvisioningProfiles(request)):
self.requestHandler.handleInstallProvisioningProfilesRequest(request, for: connection) { result in
finish(result)
}
case let .success(.removeProvisioningProfiles(request)):
self.requestHandler.handleRemoveProvisioningProfilesRequest(request, for: connection) { result in
finish(result)
}
case let .success(.removeApp(request)):
self.requestHandler.handleRemoveAppRequest(request, for: connection) { result in
finish(result)
}
case let .success(.enableUnsignedCodeExecution(request)):
self.requestHandler.handleEnableUnsignedCodeExecutionRequest(request, for: connection) { result in
finish(result)
}
case .success(.unknown):
finish(Result<ErrorResponse, Error>.failure(ALTServerError.unknownRequest))
}
}
}
}

View File

@@ -0,0 +1,48 @@
//
// NetworkConnection.swift
// AltKit
//
// Created by Riley Testut on 6/1/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
import Network
import SideKit
public class NetworkConnection: NSObject, SideConnection {
public let nwConnection: NWConnection
public init(_ nwConnection: NWConnection) {
self.nwConnection = nwConnection
}
public func __send(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void) {
nwConnection.send(content: data, completion: .contentProcessed { error in
completionHandler(error == nil, error)
})
}
public func __receiveData(expectedSize: Int, completionHandler: @escaping (Data?, Error?) -> Void) {
nwConnection.receive(minimumIncompleteLength: expectedSize, maximumLength: expectedSize) { data, _, _, error in
guard data != nil || error != nil else {
return completionHandler(nil, ALTServerError.lostConnection(underlyingError: error))
}
completionHandler(data, error)
}
}
public func disconnect() {
switch nwConnection.state {
case .cancelled, .failed: break
default: nwConnection.cancel()
}
}
}
public extension NetworkConnection {
override var description: String {
"\(nwConnection.endpoint) (Network)"
}
}

View File

@@ -0,0 +1,138 @@
//
// XPCConnection.swift
// AltKit
//
// Created by Riley Testut on 6/15/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
import SideKit
@objc private protocol XPCConnectionProxy {
func ping(completionHandler: @escaping () -> Void)
func receive(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void)
}
public extension XPCConnection {
static let unc0verMachServiceName = "cy:io.altstore.altdaemon"
static let odysseyMachServiceName = "lh:io.altstore.altdaemon"
static let machServiceNames = [unc0verMachServiceName, odysseyMachServiceName]
}
public class XPCConnection: NSObject, SideConnection {
public let xpcConnection: NSXPCConnection
private let queue = DispatchQueue(label: "io.altstore.XPCConnection")
private let dispatchGroup = DispatchGroup()
private var semaphore: DispatchSemaphore?
private var buffer = Data(capacity: 1024)
private var error: Error?
public init(_ xpcConnection: NSXPCConnection) {
let proxyInterface = NSXPCInterface(with: XPCConnectionProxy.self)
xpcConnection.remoteObjectInterface = proxyInterface
xpcConnection.exportedInterface = proxyInterface
self.xpcConnection = xpcConnection
super.init()
xpcConnection.interruptionHandler = {
self.error = ALTServerError.lostConnection(underlyingError: nil)
}
xpcConnection.exportedObject = self
xpcConnection.resume()
}
deinit {
self.disconnect()
}
}
private extension XPCConnection {
func makeProxy(errorHandler: @escaping (Error) -> Void) -> XPCConnectionProxy {
let proxy = xpcConnection.remoteObjectProxyWithErrorHandler { error in
print("Error messaging remote object proxy:", error)
self.error = error
errorHandler(error)
} as! XPCConnectionProxy
return proxy
}
}
public extension XPCConnection {
func connect(completionHandler: @escaping (Result<Void, Error>) -> Void) {
let proxy = makeProxy { error in
completionHandler(.failure(error))
}
proxy.ping {
completionHandler(.success(()))
}
}
func disconnect() {
xpcConnection.invalidate()
}
func __send(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void) {
guard error == nil else { return completionHandler(false, error) }
let proxy = makeProxy { error in
completionHandler(false, error)
}
proxy.receive(data) { success, error in
completionHandler(success, error)
}
}
func __receiveData(expectedSize: Int, completionHandler: @escaping (Data?, Error?) -> Void) {
guard error == nil else { return completionHandler(nil, error) }
queue.async {
let copiedBuffer = self.buffer // Copy buffer to prevent runtime crashes.
guard copiedBuffer.count >= expectedSize else {
self.semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
_ = self.semaphore?.wait(timeout: .now() + 1.0)
self.__receiveData(expectedSize: expectedSize, completionHandler: completionHandler)
}
return
}
let data = copiedBuffer.prefix(expectedSize)
self.buffer = copiedBuffer.dropFirst(expectedSize)
completionHandler(data, nil)
}
}
}
public extension XPCConnection {
override var description: String {
"\(xpcConnection.endpoint) (XPC)"
}
}
extension XPCConnection: XPCConnectionProxy {
fileprivate func ping(completionHandler: @escaping () -> Void) {
completionHandler()
}
fileprivate func receive(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void) {
queue.async {
self.buffer.append(data)
self.semaphore?.signal()
self.semaphore = nil
completionHandler(true, nil)
}
}
}

View File

@@ -0,0 +1,70 @@
//
// Bundle+AltStore.swift
// AltStore
//
// Created by Riley Testut on 5/30/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
import Shared
public extension Bundle {
enum Info {
public static let deviceID = "ALTDeviceID"
public static let serverID = "ALTServerID"
public static let certificateID = "ALTCertificateID"
public static let appGroups = "ALTAppGroups"
public static let altBundleID = "ALTBundleIdentifier"
public static let orgbundleIdentifier = "com.SideStore"
public static let appbundleIdentifier = orgbundleIdentifier + ".SideStore"
public static let devicePairingString = "ALTPairingFile"
public static let urlTypes = "CFBundleURLTypes"
public static let exportedUTIs = "UTExportedTypeDeclarations"
public static let untetherURL = "ALTFugu14UntetherURL"
public static let untetherRequired = "ALTFugu14UntetherRequired"
public static let untetherMinimumiOSVersion = "ALTFugu14UntetherMinimumVersion"
public static let untetherMaximumiOSVersion = "ALTFugu14UntetherMaximumVersion"
}
}
public extension Bundle {
var infoPlistURL: URL {
let infoPlistURL = bundleURL.appendingPathComponent("Info.plist")
return infoPlistURL
}
var provisioningProfileURL: URL {
let provisioningProfileURL = bundleURL.appendingPathComponent("embedded.mobileprovision")
return provisioningProfileURL
}
var certificateURL: URL {
let certificateURL = bundleURL.appendingPathComponent("ALTCertificate.p12")
return certificateURL
}
var altstorePlistURL: URL {
let altstorePlistURL = bundleURL.appendingPathComponent("AltStore.plist")
return altstorePlistURL
}
}
public extension Bundle {
static var baseAltStoreAppGroupID = "group.com.SideStore.SideStore"
var appGroups: [String] {
infoDictionary?[Bundle.Info.appGroups] as? [String] ?? []
}
var altstoreAppGroup: String? {
let appGroup = appGroups.first { $0.contains(Bundle.baseAltStoreAppGroupID) }
return appGroup
}
var completeInfoDictionary: [String: Any]? {
let infoPlistURL = self.infoPlistURL
return NSDictionary(contentsOf: infoPlistURL) as? [String: Any]
}
}

View File

@@ -0,0 +1,128 @@
//
// NSError+AltStore.swift
// AltStore
//
// Created by Riley Testut on 3/11/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
extension NSError {
@objc(alt_localizedFailure)
var localizedFailure: String? {
let localizedFailure = (userInfo[NSLocalizedFailureErrorKey] as? String) ?? (NSError.userInfoValueProvider(forDomain: domain)?(self, NSLocalizedFailureErrorKey) as? String)
return localizedFailure
}
@objc(alt_localizedDebugDescription)
var localizedDebugDescription: String? {
let debugDescription = (userInfo[NSDebugDescriptionErrorKey] as? String) ?? (NSError.userInfoValueProvider(forDomain: domain)?(self, NSDebugDescriptionErrorKey) as? String)
return debugDescription
}
@objc(alt_errorWithLocalizedFailure:)
func withLocalizedFailure(_ failure: String) -> NSError {
var userInfo = self.userInfo
userInfo[NSLocalizedFailureErrorKey] = failure
if let failureReason = localizedFailureReason {
userInfo[NSLocalizedFailureReasonErrorKey] = failureReason
} else if localizedFailure == nil && localizedFailureReason == nil && localizedDescription.contains(localizedErrorCode) {
// Default localizedDescription, so replace with just the localized error code portion.
userInfo[NSLocalizedFailureReasonErrorKey] = "(\(localizedErrorCode).)"
} else {
userInfo[NSLocalizedFailureReasonErrorKey] = localizedDescription
}
if let localizedDescription = NSError.userInfoValueProvider(forDomain: domain)?(self, NSLocalizedDescriptionKey) as? String {
userInfo[NSLocalizedDescriptionKey] = localizedDescription
}
// Don't accidentally remove localizedDescription from dictionary
// userInfo[NSLocalizedDescriptionKey] = NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSLocalizedDescriptionKey) as? String
if let recoverySuggestion = localizedRecoverySuggestion {
userInfo[NSLocalizedRecoverySuggestionErrorKey] = recoverySuggestion
}
let error = NSError(domain: domain, code: code, userInfo: userInfo)
return error
}
func sanitizedForCoreData() -> NSError {
var userInfo = self.userInfo
userInfo[NSLocalizedFailureErrorKey] = localizedFailure
userInfo[NSLocalizedDescriptionKey] = localizedDescription
userInfo[NSLocalizedFailureReasonErrorKey] = localizedFailureReason
userInfo[NSLocalizedRecoverySuggestionErrorKey] = localizedRecoverySuggestion
// Remove userInfo values that don't conform to NSSecureEncoding.
userInfo = userInfo.filter { _, value in
(value as AnyObject) is NSSecureCoding
}
// Sanitize underlying errors.
if let underlyingError = userInfo[NSUnderlyingErrorKey] as? Error {
let sanitizedError = (underlyingError as NSError).sanitizedForCoreData()
userInfo[NSUnderlyingErrorKey] = sanitizedError
}
if #available(iOS 14.5, macOS 11.3, *), let underlyingErrors = userInfo[NSMultipleUnderlyingErrorsKey] as? [Error] {
let sanitizedErrors = underlyingErrors.map { ($0 as NSError).sanitizedForCoreData() }
userInfo[NSMultipleUnderlyingErrorsKey] = sanitizedErrors
}
let error = NSError(domain: domain, code: code, userInfo: userInfo)
return error
}
}
extension Error {
var underlyingError: Error? {
let underlyingError = (self as NSError).userInfo[NSUnderlyingErrorKey] as? Error
return underlyingError
}
var localizedErrorCode: String {
let localizedErrorCode = String(format: NSLocalizedString("%@ error %@", comment: ""), (self as NSError).domain, (self as NSError).code as NSNumber)
return localizedErrorCode
}
}
protocol ALTLocalizedError: LocalizedError, CustomNSError {
var failure: String? { get }
var underlyingError: Error? { get }
}
extension ALTLocalizedError {
var errorUserInfo: [String: Any] {
let userInfo = ([
NSLocalizedDescriptionKey: errorDescription,
NSLocalizedFailureReasonErrorKey: failureReason,
NSLocalizedFailureErrorKey: failure,
NSUnderlyingErrorKey: underlyingError
] as [String: Any?]).compactMapValues { $0 }
return userInfo
}
var underlyingError: Error? {
// Error's default implementation calls errorUserInfo,
// but ALTLocalizedError.errorUserInfo calls underlyingError.
// Return nil to prevent infinite recursion.
nil
}
var errorDescription: String? {
guard let errorFailure = failure else { return (underlyingError as NSError?)?.localizedDescription }
guard let failureReason = failureReason else { return errorFailure }
let errorDescription = errorFailure + " " + failureReason
return errorDescription
}
var failureReason: String? { (underlyingError as NSError?)?.localizedDescription }
var recoverySuggestion: String? { (underlyingError as NSError?)?.localizedRecoverySuggestion }
var helpAnchor: String? { (underlyingError as NSError?)?.helpAnchor }
}

View File

@@ -0,0 +1,28 @@
//
// NSXPCConnection+MachServices.swift
// AltStore
//
// Created by Riley Testut on 9/22/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
@objc private protocol XPCPrivateAPI {
init(machServiceName: String)
init(machServiceName: String, options: NSXPCConnection.Options)
}
public extension NSXPCConnection {
class func makeConnection(machServiceName: String) -> NSXPCConnection {
let connection = unsafeBitCast(self, to: XPCPrivateAPI.Type.self).init(machServiceName: machServiceName, options: .privileged)
return unsafeBitCast(connection, to: NSXPCConnection.self)
}
}
public extension NSXPCListener {
class func makeListener(machServiceName: String) -> NSXPCListener {
let listener = unsafeBitCast(self, to: XPCPrivateAPI.Type.self).init(machServiceName: machServiceName)
return unsafeBitCast(listener, to: NSXPCListener.self)
}
}

View File

@@ -0,0 +1,57 @@
//
// Result+Conveniences.swift
// AltStore
//
// Created by Riley Testut on 5/22/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
public extension Result {
var value: Success? {
switch self {
case let .success(value): return value
case .failure: return nil
}
}
var error: Failure? {
switch self {
case .success: return nil
case let .failure(error): return error
}
}
init(_ value: Success?, _ error: Failure?) {
switch (value, error) {
case let (value?, _): self = .success(value)
case let (_, error?): self = .failure(error)
case (nil, nil): preconditionFailure("Either value or error must be non-nil")
}
}
}
public extension Result where Success == Void {
init(_ success: Bool, _ error: Failure?) {
if success {
self = .success(())
} else if let error = error {
self = .failure(error)
} else {
preconditionFailure("Error must be non-nil if success is false")
}
}
}
public extension Result {
init<T, U>(_ values: (T?, U?), _ error: Failure?) where Success == (T, U) {
if let value1 = values.0, let value2 = values.1 {
self = .success((value1, value2))
} else if let error = error {
self = .failure(error)
} else {
preconditionFailure("Error must be non-nil if either provided values are nil")
}
}
}

View File

@@ -0,0 +1,21 @@
//
// AltXPCProtocol.swift
// SideStore
//
// Created by Joseph Mattiello on 02/28/23.
// Copyright © 2023 Joseph Mattiello. All rights reserved.
//
import Foundation
public typealias AltXPCProtocol = SideXPCProtocol
@objc
public protocol SideXPCProtocol {
func ping(completionHandler: @escaping () -> Void)
func requestAnisetteData(completionHandler: @escaping (ALTAnisetteData?, Error?) -> Void)
}
@objc public class ALTAnisetteData: NSObject {
// implementation
}