fix: Adjust the way to obtain the version number of the Setup component to fix the crash issue under macOS 13.

This commit is contained in:
X1a0He
2024-11-14 13:30:13 +08:00
parent c44b3f3c9c
commit 6411b478aa
11 changed files with 496 additions and 459 deletions

View File

@@ -505,10 +505,15 @@ class DownloadUtils {
let (manifestData, _) = try await URLSession.shared.data(for: request)
let manifestXML = try XMLDocument(data: manifestData)
#if DEBUG
if let manifestString = String(data: manifestData, encoding: .utf8) {
print("Manifest内容: \(manifestString)")
}
#endif
let manifestDoc = try XMLDocument(data: manifestData)
guard let downloadPath = try manifestXML.nodes(forXPath: "//asset_list/asset/asset_path").first?.stringValue,
let assetSizeStr = try manifestXML.nodes(forXPath: "//asset_list/asset/asset_size").first?.stringValue,
guard let downloadPath = try manifestDoc.nodes(forXPath: "//asset_list/asset/asset_path").first?.stringValue,
let assetSizeStr = try manifestDoc.nodes(forXPath: "//asset_list/asset/asset_size").first?.stringValue,
let assetSize = Int64(assetSizeStr) else {
throw NetworkError.invalidData("无法从manifest中获取下载信息")
}
@@ -950,140 +955,181 @@ class DownloadUtils {
}
}
func downloadSetupComponents(
func downloadX1a0HeCCPackages(
progressHandler: @escaping (Double, String) -> Void,
cancellationHandler: @escaping () -> Bool
) async throws {
let architecture = AppStatics.isAppleSilicon ? "arm" : "intel"
let baseURLs = [
"https://github.com/X1a0He/Adobe-Downloader/raw/refs/heads/main/X1a0HeCC/\(architecture)/HDBox/",
"https://github.com/X1a0He/Adobe-Downloader/raw/refs/heads/develop/X1a0HeCC/\(architecture)/HDBox/"
]
let components = [
"HDHelper",
"HDIM.dylib",
"HDPIM.dylib",
"HUM.dylib",
"Setup"
]
let targetDirectory = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox"
let baseUrl = "https://cdn-ffc.oobesaas.adobe.com/core/v1/applications?name=CreativeCloud&platform=\(AppStatics.isAppleSilicon ? "macarm64" : "osx10")"
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: tempDirectory, withIntermediateDirectories: true)
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: tempDirectory.path)
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.timeoutIntervalForResource = 300
configuration.httpAdditionalHeaders = NetworkConstants.downloadHeaders
let session = URLSession(configuration: configuration)
do {
var request = URLRequest(url: URL(string: baseUrl)!)
NetworkConstants.downloadHeaders.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw NetworkError.invalidResponse
}
let xmlDoc = try XMLDocument(data: data)
for (index, component) in components.enumerated() {
if cancellationHandler() {
try? FileManager.default.removeItem(at: tempDirectory)
throw NetworkError.cancelled
let packageSets = try xmlDoc.nodes(forXPath: "//packageSet[name='ADC']")
guard let adcPackageSet = packageSets.first else {
throw NetworkError.invalidData("找不到ADC包集")
}
await MainActor.run {
progressHandler(Double(index) / Double(components.count), "正在下载 \(component)...")
let targetPackages = ["HDBox", "IPCBox"]
var packagesToDownload: [(name: String, url: URL, size: Int64)] = []
for packageName in targetPackages {
let packageNodes = try adcPackageSet.nodes(forXPath: ".//package[name='\(packageName)']")
guard let package = packageNodes.first else {
print("未找到包: \(packageName)")
continue
}
guard let manifestUrl = try package.nodes(forXPath: ".//manifestUrl").first?.stringValue,
let cdnBase = try xmlDoc.nodes(forXPath: "//cdn/secure").first?.stringValue else {
print("无法获取manifest URL或CDN基础URL")
continue
}
let manifestFullUrl = cdnBase + manifestUrl
var manifestRequest = URLRequest(url: URL(string: manifestFullUrl)!)
NetworkConstants.downloadHeaders.forEach { manifestRequest.setValue($0.value, forHTTPHeaderField: $0.key) }
let (manifestData, manifestResponse) = try await session.data(for: manifestRequest)
guard let manifestHttpResponse = manifestResponse as? HTTPURLResponse,
(200...299).contains(manifestHttpResponse.statusCode) else {
print("获取manifest失败: HTTP \(String(describing: (manifestResponse as? HTTPURLResponse)?.statusCode))")
continue
}
#if DEBUG
if let manifestString = String(data: manifestData, encoding: .utf8) {
print("Manifest内容: \(manifestString)")
}
#endif
let manifestDoc = try XMLDocument(data: manifestData)
let assetPathNodes = try manifestDoc.nodes(forXPath: "//asset_path")
let sizeNodes = try manifestDoc.nodes(forXPath: "//asset_size")
guard let assetPath = assetPathNodes.first?.stringValue,
let sizeStr = sizeNodes.first?.stringValue,
let size = Int64(sizeStr),
let downloadUrl = URL(string: assetPath) else {
continue
}
packagesToDownload.append((packageName, downloadUrl, size))
}
guard !packagesToDownload.isEmpty else {
throw NetworkError.invalidData("没有找到可下载的包")
}
var lastError: Error? = nil
var downloaded = false
for baseURL in baseURLs {
guard !downloaded else { break }
let totalCount = packagesToDownload.count
for (index, package) in packagesToDownload.enumerated() {
if cancellationHandler() {
try? FileManager.default.removeItem(at: tempDirectory)
throw NetworkError.cancelled
}
await MainActor.run {
progressHandler(Double(index) / Double(totalCount), "正在下载 \(package.name)...")
}
let url = URL(string: baseURL + component)!
let destinationURL = tempDirectory.appendingPathComponent(component)
print(url)
do {
let (downloadURL, response) = try await session.download(from: url)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
if httpResponse.statusCode == 404 {
lastError = NetworkError.fileNotFound(component)
continue
}
if !(200...299).contains(httpResponse.statusCode) {
lastError = NetworkError.httpError(httpResponse.statusCode, "下载 \(component) 失败")
continue
}
try FileManager.default.moveItem(at: downloadURL, to: destinationURL)
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: destinationURL.path)
downloaded = true
} catch URLError.timedOut {
lastError = NetworkError.timeout
continue
} catch {
lastError = error is NetworkError ? error : NetworkError.downloadError("下载 \(component) 失败: \(error.localizedDescription)", error)
let destinationURL = tempDirectory.appendingPathComponent("\(package.name).zip")
var downloadRequest = URLRequest(url: package.url)
NetworkConstants.downloadHeaders.forEach { downloadRequest.setValue($0.value, forHTTPHeaderField: $0.key) }
let (downloadURL, downloadResponse) = try await session.download(for: downloadRequest)
guard let httpResponse = downloadResponse as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("下载失败: HTTP \(String(describing: (downloadResponse as? HTTPURLResponse)?.statusCode))")
continue
}
try FileManager.default.moveItem(at: downloadURL, to: destinationURL)
}
if !downloaded {
try? FileManager.default.removeItem(at: tempDirectory)
throw lastError ?? NetworkError.downloadError("无法下载 \(component)", nil)
await MainActor.run {
progressHandler(0.9, "正在安装组件...")
}
}
let targetDirectory = "/Library/Application Support/Adobe/Adobe Desktop Common"
await MainActor.run {
progressHandler(1.0, "下载完成,正在安装...")
}
if !FileManager.default.fileExists(atPath: targetDirectory) {
let baseCommands = [
"mkdir -p '\(targetDirectory)'",
"chmod 755 '\(targetDirectory)'"
]
let commands = [
"mkdir -p '\(targetDirectory)'",
"chmod 755 '\(targetDirectory)'",
"cp -f '\(tempDirectory.path)/'* '\(targetDirectory)/'",
"chmod 755 '\(targetDirectory)/'*",
"chown -R root:wheel '\(targetDirectory)'"
]
for command in commands {
let result = await withCheckedContinuation { continuation in
PrivilegedHelperManager.shared.executeCommand(command) { result in
continuation.resume(returning: result)
for command in baseCommands {
let result = await withCheckedContinuation { continuation in
PrivilegedHelperManager.shared.executeCommand(command) { result in
continuation.resume(returning: result)
}
}
if result.starts(with: "Error:") {
try? FileManager.default.removeItem(at: tempDirectory)
throw NetworkError.installError("创建目录失败: \(result)")
}
}
}
if result.starts(with: "Error:") {
try? FileManager.default.removeItem(at: tempDirectory)
throw NSError(domain: "SetupComponentsError",
code: -1,
userInfo: [NSLocalizedDescriptionKey: "安装组件失败: \(result)"])
}
}
try await withCheckedThrowingContinuation { continuation in
ModifySetup.backupAndModifySetupFile { success, message in
if success {
continuation.resume()
} else {
continuation.resume(throwing: NSError(domain: "SetupComponentsError",
code: -1,
userInfo: [NSLocalizedDescriptionKey: message]))
for package in packagesToDownload {
let packageDir = "\(targetDirectory)/\(package.name)"
let packageCommands = [
"mkdir -p '\(packageDir)'",
"unzip -o '\(tempDirectory.path)/\(package.name).zip' -d '\(packageDir)/'",
"chmod -R 755 '\(packageDir)'",
"chown -R root:wheel '\(packageDir)'"
]
for command in packageCommands {
let result = await withCheckedContinuation { continuation in
PrivilegedHelperManager.shared.executeCommand(command) { result in
continuation.resume(returning: result)
}
}
if result.starts(with: "Error:") {
try? FileManager.default.removeItem(at: tempDirectory)
throw NetworkError.installError("安装 \(package.name) 失败: \(result)")
}
}
}
}
try? FileManager.default.removeItem(at: tempDirectory)
try await withCheckedThrowingContinuation { continuation in
ModifySetup.backupAndModifySetupFile { success, message in
if success {
continuation.resume()
} else {
continuation.resume(throwing: NetworkError.installError(message))
}
}
}
ModifySetup.clearVersionCache()
ModifySetup.clearVersionCache()
await MainActor.run {
progressHandler(1.0, "安装完成")
try? FileManager.default.removeItem(at: tempDirectory)
await MainActor.run {
progressHandler(1.0, "安装完成")
}
} catch {
print("发生错误: \(error.localizedDescription)")
throw error
}
}
}

View File

@@ -34,8 +34,7 @@ actor InstallManager {
private func executeInstallation(
at appPath: URL,
progressHandler: @escaping (Double, String) -> Void,
logHandler: @escaping (String) -> Void
progressHandler: @escaping (Double, String) -> Void
) async throws {
guard FileManager.default.fileExists(atPath: setupPath) else {
throw InstallError.setupNotFound
@@ -53,13 +52,13 @@ actor InstallManager {
do {
try await PrivilegedHelperManager.shared.executeInstallation(installCommand) { output in
Task { @MainActor in
logHandler(output)
if let range = output.range(of: "Exit Code: (-?[0-9]+)", options: .regularExpression),
let codeStr = output[range].split(separator: ":").last?.trimmingCharacters(in: .whitespaces),
let exitCode = Int(codeStr) {
if exitCode == 0 {
progressHandler(1.0, String(localized: "安装完成"))
PrivilegedHelperManager.shared.executeCommand("pkill -f Setup") { _ in }
continuation.resume()
} else {
let errorMessage: String
@@ -104,13 +103,11 @@ actor InstallManager {
func install(
at appPath: URL,
progressHandler: @escaping (Double, String) -> Void,
logHandler: @escaping (String) -> Void
progressHandler: @escaping (Double, String) -> Void
) async throws {
try await executeInstallation(
at: appPath,
progressHandler: progressHandler,
logHandler: logHandler
progressHandler: progressHandler
)
}
@@ -124,13 +121,11 @@ actor InstallManager {
func retry(
at appPath: URL,
progressHandler: @escaping (Double, String) -> Void,
logHandler: @escaping (String) -> Void
progressHandler: @escaping (Double, String) -> Void
) async throws {
try await executeInstallation(
at: appPath,
progressHandler: progressHandler,
logHandler: logHandler
progressHandler: progressHandler
)
}
}

View File

@@ -16,13 +16,13 @@ class ModifySetup {
}
static func isSetupBackup() -> Bool {
return FileManager.default.fileExists(atPath: "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup.original")
return isSetupExists() && FileManager.default.fileExists(atPath: "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup.original")
}
static func checkComponentVersion() -> String {
let setupPath = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup"
guard isSetupExists() else {
guard FileManager.default.fileExists(atPath: setupPath) else {
cachedVersion = nil
return String(localized: "未找到 Setup 组件")
}
@@ -30,42 +30,42 @@ class ModifySetup {
if let cachedVersion = cachedVersion {
return cachedVersion
}
guard let data = try? Data(contentsOf: URL(fileURLWithPath: setupPath)) else {
return "Unknown"
}
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/strings")
process.arguments = [setupPath]
let versionMarkers = ["Version ", "Adobe Setup Version: "]
for marker in versionMarkers {
if let markerData = marker.data(using: .utf8),
let markerRange = data.range(of: markerData) {
let versionStart = markerRange.upperBound
let searchRange = versionStart..<min(versionStart + 30, data.count)
let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe
do {
try process.run()
process.waitUntilExit()
if let output = try pipe.fileHandleForReading.readToEnd(),
let outputString = String(data: output, encoding: .utf8) {
let lines = outputString.components(separatedBy: .newlines)
for (index, line) in lines.enumerated() {
if line == "Adobe Setup Version: %s" && index + 1 < lines.count {
let version = lines[index + 1].trimmingCharacters(in: .whitespacesAndNewlines)
if version.range(of: "^[0-9.]+$", options: .regularExpression) != nil {
cachedVersion = version
return version
}
}
var versionBytes: [UInt8] = []
for i in searchRange {
let byte = data[i]
if (byte >= 0x30 && byte <= 0x39) || byte == 0x2E || byte == 0x20 {
versionBytes.append(byte)
} else if byte == 0x28 {
break
} else if versionBytes.isEmpty {
continue
} else { break }
}
if let version = String(bytes: versionBytes, encoding: .utf8)?.trimmingCharacters(in: .whitespaces),
!version.isEmpty {
cachedVersion = version
return version
}
}
let message = String(localized: "未知 Setup 组件版本号")
cachedVersion = message
return message
} catch {
print("Error checking Setup version: \(error)")
let message = String(localized: "未知 Setup 组件版本号")
cachedVersion = message
return message
}
let message = String(localized: "未知 Setup 组件版本号")
cachedVersion = message
return message
}
static func clearVersionCache() {
@@ -152,5 +152,25 @@ class ModifySetup {
}
}
}
static func isSetupModified() -> Bool {
let setupPath = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup"
guard FileManager.default.fileExists(atPath: setupPath) else { return false }
let intelPattern = Data([0x55, 0x48, 0x89, 0xE5, 0x53, 0x50, 0x48, 0x89, 0xFB, 0x48, 0x8B, 0x05, 0x70, 0xC7, 0x03, 0x00, 0x48, 0x8B, 0x00, 0x48, 0x89, 0x45, 0xF0, 0xE8, 0x24, 0xD7, 0xFE, 0xFF, 0x48, 0x83, 0xC3, 0x08, 0x48, 0x39, 0xD8, 0x0F])
let armPattern = Data([0xFF, 0xC3, 0x00, 0xD1, 0xF4, 0x4F, 0x01, 0xA9, 0xFD, 0x7B, 0x02, 0xA9, 0xFD, 0x83, 0x00, 0x91, 0xF3, 0x03, 0x00, 0xAA, 0x1F, 0x20, 0x03, 0xD5, 0x68, 0xA1, 0x1D, 0x58, 0x08, 0x01, 0x40, 0xF9, 0xE8, 0x07, 0x00, 0xF9])
do {
let fileData = try Data(contentsOf: URL(fileURLWithPath: setupPath))
if fileData.range(of: intelPattern) != nil || fileData.range(of: armPattern) != nil { return false }
return true
} catch {
print("Error reading Setup file: \(error)")
return false
}
}
}