mirror of
https://github.com/xtool-org/xtool.git
synced 2026-02-04 11:53:30 +01:00
108 lines
3.7 KiB
Swift
108 lines
3.7 KiB
Swift
//
|
|
// Signer.swift
|
|
// XKit
|
|
//
|
|
// Created by Kabir Oberai on 13/10/19.
|
|
// Copyright © 2019 Kabir Oberai. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public struct Signer {
|
|
|
|
public enum Error: LocalizedError {
|
|
case noSigners
|
|
case errorReading(String)
|
|
case errorWriting(String)
|
|
|
|
public var errorDescription: String? {
|
|
switch self {
|
|
case .noSigners:
|
|
return NSLocalizedString("signer.error.no_signers", value: "No signers found", comment: "")
|
|
case .errorReading(let file):
|
|
return "\(file)".withCString {
|
|
String.localizedStringWithFormat(
|
|
NSLocalizedString(
|
|
"signer.error.error_reading", value: "Error while reading %s", comment: ""
|
|
), $0
|
|
)
|
|
}
|
|
case .errorWriting(let file):
|
|
return "\(file)".withCString {
|
|
String.localizedStringWithFormat(
|
|
NSLocalizedString(
|
|
"signer.error.error_writing", value: "Error while writing %s", comment: ""
|
|
), $0
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public let context: SigningContext
|
|
public let confirmRevocation: @Sendable ([DeveloperServicesCertificate]) async -> Bool
|
|
public init(
|
|
context: SigningContext,
|
|
confirmRevocation: @escaping @Sendable ([DeveloperServicesCertificate]) async -> Bool
|
|
) {
|
|
self.context = context
|
|
self.confirmRevocation = confirmRevocation
|
|
}
|
|
|
|
public func sign(
|
|
app: URL,
|
|
status: @escaping (String) -> Void,
|
|
progress: @escaping @Sendable (Double?) -> Void,
|
|
didProvision: @escaping () throws -> Void = {}
|
|
) async throws -> String {
|
|
status(NSLocalizedString("signer.provisioning", value: "Provisioning", comment: ""))
|
|
let response = try await DeveloperServicesProvisioningOperation(
|
|
context: context,
|
|
app: app,
|
|
confirmRevocation: confirmRevocation,
|
|
progress: progress
|
|
).perform()
|
|
|
|
let provisioningDict = response.provisioningDict
|
|
let signingInfo = response.signingInfo
|
|
guard let mainInfo = provisioningDict[app] else {
|
|
throw Error.errorReading("app bundle ID")
|
|
}
|
|
|
|
try didProvision()
|
|
|
|
for (url, info) in provisioningDict {
|
|
let infoPlist = url.appendingPathComponent("Info.plist")
|
|
guard let data = try? Data(contentsOf: infoPlist),
|
|
let dict = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any]
|
|
else { throw Error.errorReading(infoPlist.lastPathComponent) }
|
|
let nsDict = NSMutableDictionary(dictionary: dict)
|
|
nsDict["CFBundleIdentifier"] = info.newBundleID
|
|
guard nsDict.write(to: infoPlist, atomically: true) else {
|
|
throw Error.errorWriting(infoPlist.lastPathComponent)
|
|
}
|
|
|
|
let profURL = url.appendingPathComponent("embedded.mobileprovision")
|
|
if profURL.exists {
|
|
try FileManager.default.removeItem(at: profURL)
|
|
}
|
|
try info.mobileprovision.data().write(to: profURL)
|
|
}
|
|
|
|
let entitlements = provisioningDict.mapValues(\.entitlements)
|
|
|
|
status(NSLocalizedString("signer.signing", value: "Signing", comment: ""))
|
|
try await context.signerImpl.sign(
|
|
app: app,
|
|
certificate: signingInfo.certificate,
|
|
privateKey: signingInfo.privateKey,
|
|
entitlementMapping: entitlements,
|
|
progress: progress
|
|
)
|
|
progress(1)
|
|
|
|
return mainInfo.newBundleID
|
|
}
|
|
|
|
}
|