mirror of
https://github.com/X1a0He/Adobe-Downloader.git
synced 2025-11-25 03:14:57 +08:00
feat: Added "Cleanup Tool" and "Common Issues" functions in the program settings page
1. Fixed the issue of Helper not being able to reconnect in some cases 2. Fixed the issue of not being able to reconnect after reinstalling the program and reinstalling Helper 3. Adjusted the content translation of X1a0He CC, version 1.5.0 can choose "Download and Process" and "Only Download" 4. Adjusted the translation of some Setup component content 5. Added "Cleanup Tool" and "Common Issues" functions in the program settings page 6. Added the current version display in the program settings page - PS: The "Cleanup Tool" function in the current version is an experimental feature. If some files are not cleaned up, please feedback in time - PS: 1.5.0 version will be the last open source version, please be aware
This commit is contained in:
@@ -416,7 +416,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 141;
|
||||
CURRENT_PROJECT_VERSION = 150;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Adobe Downloader/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = TG862GVKHK;
|
||||
@@ -432,7 +432,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.4.1;
|
||||
MARKETING_VERSION = 1.5.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.x1a0he.macOS.Adobe-Downloader";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -450,7 +450,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 141;
|
||||
CURRENT_PROJECT_VERSION = 150;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Adobe Downloader/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = TG862GVKHK;
|
||||
@@ -466,7 +466,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.4.1;
|
||||
MARKETING_VERSION = 1.5.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.x1a0he.macOS.Adobe-Downloader";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
|
||||
@@ -396,20 +396,21 @@ class PrivilegedHelperManager: NSObject {
|
||||
}
|
||||
|
||||
private func parseCommand(_ command: String) -> (CommandType, String, String, Int) {
|
||||
let components = command.components(separatedBy: " ")
|
||||
let components = command.split(separator: " ", omittingEmptySubsequences: true).map(String.init)
|
||||
|
||||
if command.hasPrefix("installer -pkg") {
|
||||
return (.install, components[2].replacingOccurrences(of: "\"", with: ""), "", 0)
|
||||
return (.install, components[2], "", 0)
|
||||
} else if command.hasPrefix("rm -rf") {
|
||||
return (.uninstall, components[2].replacingOccurrences(of: "\"", with: ""), "", 0)
|
||||
let path = components.dropFirst(2).joined(separator: " ")
|
||||
return (.uninstall, path, "", 0)
|
||||
} else if command.hasPrefix("mv") || command.hasPrefix("cp") {
|
||||
return (.moveFile,
|
||||
components[1].replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "'", with: ""),
|
||||
components[2].replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "'", with: ""),
|
||||
0)
|
||||
let paths = components.dropFirst(1)
|
||||
let sourcePath = String(paths.first ?? "")
|
||||
let destPath = paths.dropFirst().joined(separator: " ")
|
||||
return (.moveFile, sourcePath, destPath, 0)
|
||||
} else if command.hasPrefix("chmod") {
|
||||
return (.setPermissions,
|
||||
components[2].replacingOccurrences(of: "\"", with: ""),
|
||||
components.dropFirst(2).joined(separator: " "),
|
||||
"",
|
||||
Int(components[1]) ?? 0)
|
||||
}
|
||||
@@ -637,13 +638,13 @@ enum HelperError: LocalizedError {
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .connectionFailed:
|
||||
return "无法连接到 Helper"
|
||||
return String(localized: "无法连接到 Helper")
|
||||
case .proxyError:
|
||||
return "无法获取 Helper 代理"
|
||||
return String(localized: "无法获取 Helper 代理")
|
||||
case .authorizationFailed:
|
||||
return "获取授权失败"
|
||||
return String(localized: "获取授权失败")
|
||||
case .installationFailed(let reason):
|
||||
return "安装失败: \(reason)"
|
||||
return String(localized: "安装失败: \(reason)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,7 +1072,8 @@ class DownloadUtils {
|
||||
|
||||
func downloadX1a0HeCCPackages(
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
cancellationHandler: @escaping () -> Bool
|
||||
cancellationHandler: @escaping () -> Bool,
|
||||
shouldProcess: Bool = true
|
||||
) async throws {
|
||||
let baseUrl = "https://cdn-ffc.oobesaas.adobe.com/core/v1/applications?name=CreativeCloud&platform=\(AppStatics.isAppleSilicon ? "macarm64" : "osx10")"
|
||||
|
||||
@@ -1159,6 +1160,7 @@ class DownloadUtils {
|
||||
|
||||
let destinationURL = tempDirectory.appendingPathComponent("\(package.name).zip")
|
||||
var downloadRequest = URLRequest(url: package.url)
|
||||
print(downloadRequest)
|
||||
NetworkConstants.downloadHeaders.forEach { downloadRequest.setValue($0.value, forHTTPHeaderField: $0.key) }
|
||||
let (downloadURL, downloadResponse) = try await session.download(for: downloadRequest)
|
||||
|
||||
@@ -1172,7 +1174,7 @@ class DownloadUtils {
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(0.9, "正在安装组件...")
|
||||
progressHandler(0.9, shouldProcess ? "正在安装组件..." : "正在完成下载...")
|
||||
}
|
||||
|
||||
let targetDirectory = "/Library/Application\\ Support/Adobe/Adobe\\ Desktop\\ Common"
|
||||
@@ -1227,21 +1229,23 @@ class DownloadUtils {
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000)
|
||||
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
ModifySetup.backupAndModifySetupFile { success, message in
|
||||
if success {
|
||||
continuation.resume()
|
||||
} else {
|
||||
continuation.resume(throwing: NetworkError.installError(message))
|
||||
if shouldProcess {
|
||||
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()
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(1.0, "安装完成")
|
||||
progressHandler(1.0, shouldProcess ? "安装完成" : "下载完成")
|
||||
}
|
||||
} catch {
|
||||
print("发生错误: \(error.localizedDescription)")
|
||||
|
||||
@@ -115,7 +115,7 @@ actor InstallManager {
|
||||
let errorMessage: String
|
||||
switch exitCode {
|
||||
case 107:
|
||||
errorMessage = String(localized: "安装失败: 架构或本不一致 (退出代码: \(exitCode))")
|
||||
errorMessage = String(localized: "安装失败: 架构或版本不一致 (退出代码: \(exitCode))")
|
||||
case 103:
|
||||
errorMessage = String(localized: "安装失败: 权限问题 (退出代码: \(exitCode))")
|
||||
case 182:
|
||||
|
||||
@@ -158,7 +158,7 @@ class ModifySetup {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
if !isSetupExists() {
|
||||
DispatchQueue.main.async {
|
||||
completion(false, "未找到 Setup 组件")
|
||||
completion(false, String(localized: "未找到 Setup 组件"))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -166,7 +166,7 @@ class ModifySetup {
|
||||
backupSetupFile { backupSuccess in
|
||||
if !backupSuccess {
|
||||
DispatchQueue.main.async {
|
||||
completion(false, "备份 Setup 组件失败")
|
||||
completion(false, String(localized: "备份 Setup 组件失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -174,9 +174,9 @@ class ModifySetup {
|
||||
modifySetupFile { modifySuccess in
|
||||
DispatchQueue.main.async {
|
||||
if modifySuccess {
|
||||
completion(true, "所有操作已成功完成")
|
||||
completion(true, String(localized: "所有操作已成功完成"))
|
||||
} else {
|
||||
completion(false, "修改 Setup 组件失败")
|
||||
completion(false, String(localized: "修改 Setup 组件失败"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,8 @@ struct DownloadProgressView: View {
|
||||
@State private var expandedProducts: Set<String> = []
|
||||
@State private var iconImage: NSImage? = nil
|
||||
@State private var showSetupProcessAlert = false
|
||||
@State private var showCommandLineInstall = false
|
||||
@State private var showCopiedAlert = false
|
||||
|
||||
private var statusLabel: some View {
|
||||
Text(task.status.description)
|
||||
@@ -78,12 +80,14 @@ struct DownloadProgressView: View {
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.orange)
|
||||
.controlSize(.regular)
|
||||
|
||||
Button(action: onCancel) {
|
||||
Label("取消", systemImage: "xmark")
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.red)
|
||||
.controlSize(.regular)
|
||||
|
||||
case .paused:
|
||||
Button(action: onResume) {
|
||||
@@ -91,12 +95,14 @@ struct DownloadProgressView: View {
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.blue)
|
||||
.controlSize(.regular)
|
||||
|
||||
Button(action: onCancel) {
|
||||
Label("取消", systemImage: "xmark")
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.red)
|
||||
.controlSize(.regular)
|
||||
|
||||
case .failed(let info):
|
||||
if info.recoverable {
|
||||
@@ -105,6 +111,7 @@ struct DownloadProgressView: View {
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.blue)
|
||||
.controlSize(.regular)
|
||||
}
|
||||
|
||||
Button(action: onRemove) {
|
||||
@@ -112,11 +119,24 @@ struct DownloadProgressView: View {
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.red)
|
||||
.controlSize(.regular)
|
||||
|
||||
case .completed:
|
||||
HStack(spacing: 8) {
|
||||
if task.displayInstallButton {
|
||||
Button(action: {
|
||||
#if DEBUG
|
||||
do {
|
||||
_ = try PrivilegedHelperManager.shared.getHelperProxy()
|
||||
showInstallPrompt = false
|
||||
isInstalling = true
|
||||
Task {
|
||||
await networkManager.installProduct(at: task.directory)
|
||||
}
|
||||
} catch {
|
||||
showSetupProcessAlert = true
|
||||
}
|
||||
#else
|
||||
if !ModifySetup.isSetupModified() {
|
||||
showSetupProcessAlert = true
|
||||
} else {
|
||||
@@ -131,11 +151,13 @@ struct DownloadProgressView: View {
|
||||
showSetupProcessAlert = true
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}) {
|
||||
Label("安装", systemImage: "square.and.arrow.down.on.square")
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.green)
|
||||
.controlSize(.regular)
|
||||
.alert("Setup 组件未处理", isPresented: $showSetupProcessAlert) {
|
||||
Button("确定") { }
|
||||
} message: {
|
||||
@@ -154,6 +176,7 @@ struct DownloadProgressView: View {
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.red)
|
||||
.controlSize(.regular)
|
||||
}
|
||||
|
||||
case .retrying:
|
||||
@@ -162,6 +185,7 @@ struct DownloadProgressView: View {
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.red)
|
||||
.controlSize(.regular)
|
||||
}
|
||||
}
|
||||
.controlSize(.small)
|
||||
@@ -410,21 +434,74 @@ struct DownloadProgressView: View {
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
isPackageListExpanded.toggle()
|
||||
HStack {
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
isPackageListExpanded.toggle()
|
||||
}
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: isPackageListExpanded ? "chevron.down" : "chevron.right")
|
||||
.foregroundColor(.secondary)
|
||||
Text("产品和包列表")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: isPackageListExpanded ? "chevron.down" : "chevron.right")
|
||||
.foregroundColor(.secondary)
|
||||
Text("产品和包列表")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Spacer()
|
||||
|
||||
if case .completed = task.status {
|
||||
Button(action: {
|
||||
showCommandLineInstall.toggle()
|
||||
}) {
|
||||
Label("命令行安装", systemImage: "terminal")
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.purple)
|
||||
.controlSize(.regular)
|
||||
.popover(isPresented: $showCommandLineInstall, arrowEdge: .bottom) {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Button("复制命令") {
|
||||
let setupPath = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup"
|
||||
let driverPath = "\(task.directory.path)/driver.xml"
|
||||
let command = "sudo \"\(setupPath)\" --install=1 --driverXML=\"\(driverPath)\""
|
||||
let pasteboard = NSPasteboard.general
|
||||
pasteboard.clearContents()
|
||||
pasteboard.setString(command, forType: .string)
|
||||
showCopiedAlert = true
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||
showCopiedAlert = false
|
||||
}
|
||||
}
|
||||
|
||||
if showCopiedAlert {
|
||||
Text("已复制")
|
||||
.font(.caption)
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
|
||||
let setupPath = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup"
|
||||
let driverPath = "\(task.directory.path)/driver.xml"
|
||||
let command = "sudo \"\(setupPath)\" --install=1 --driverXML=\"\(driverPath)\""
|
||||
Text(command)
|
||||
.font(.system(.caption, design: .monospaced))
|
||||
.foregroundColor(.secondary)
|
||||
.textSelection(.enabled)
|
||||
.padding(8)
|
||||
.background(Color.secondary.opacity(0.1))
|
||||
.cornerRadius(6)
|
||||
}
|
||||
.padding()
|
||||
.frame(width: 400)
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
|
||||
actionButtons
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
if isPackageListExpanded {
|
||||
ScrollView(showsIndicators: false) {
|
||||
@@ -442,11 +519,6 @@ struct DownloadProgressView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
actionButtons
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(NSColor.windowBackgroundColor))
|
||||
@@ -603,20 +675,29 @@ struct PackageRow: View {
|
||||
}
|
||||
|
||||
#Preview("下载中") {
|
||||
DownloadProgressView(
|
||||
let product = ProductsToDownload(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
buildGuid: "123"
|
||||
)
|
||||
product.packages = [
|
||||
Package(
|
||||
type: "Application",
|
||||
fullPackageName: "AdobeAudition25All",
|
||||
downloadSize: 878454797,
|
||||
downloadURL: "https://example.com/download",
|
||||
packageVersion: "25.0.0.1"
|
||||
)
|
||||
]
|
||||
|
||||
return DownloadProgressView(
|
||||
task: NewDownloadTask(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
language: "zh_CN",
|
||||
displayName: "Adobe Audition",
|
||||
directory: URL(fileURLWithPath: "Adobe Downloader Audition_25.0-zh_CN-macuniversal"),
|
||||
productsToDownload: [
|
||||
ProductsToDownload(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
buildGuid: "123"
|
||||
)
|
||||
],
|
||||
directory: URL(fileURLWithPath: "/Users/test/Downloads/Adobe Audition_25.0-zh_CN-macuniversal"),
|
||||
productsToDownload: [product],
|
||||
createAt: Date(),
|
||||
totalStatus: .downloading(DownloadStatus.DownloadInfo(
|
||||
fileName: "AdobeAudition25All_stripped.zip",
|
||||
@@ -629,7 +710,7 @@ struct PackageRow: View {
|
||||
totalDownloadedSize: 457424883,
|
||||
totalSize: 878454797,
|
||||
totalSpeed: 1024 * 1024 * 2,
|
||||
platform: ""
|
||||
platform: "macuniversal"
|
||||
),
|
||||
onCancel: {},
|
||||
onPause: {},
|
||||
@@ -641,20 +722,32 @@ struct PackageRow: View {
|
||||
}
|
||||
|
||||
#Preview("已完成") {
|
||||
DownloadProgressView(
|
||||
let product = ProductsToDownload(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
buildGuid: "123"
|
||||
)
|
||||
let package = Package(
|
||||
type: "Application",
|
||||
fullPackageName: "AdobeAudition25All",
|
||||
downloadSize: 878454797,
|
||||
downloadURL: "https://example.com/download",
|
||||
packageVersion: "25.0.0.1"
|
||||
)
|
||||
package.status = .completed
|
||||
package.progress = 1.0
|
||||
package.downloadedSize = 878454797
|
||||
package.downloaded = true
|
||||
product.packages = [package]
|
||||
|
||||
return DownloadProgressView(
|
||||
task: NewDownloadTask(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
language: "zh_CN",
|
||||
displayName: "Adobe Audition",
|
||||
directory: URL(fileURLWithPath: "Adobe Downloader Audition_25.0-zh_CN-macuniversal"),
|
||||
productsToDownload: [
|
||||
ProductsToDownload(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
buildGuid: "123"
|
||||
)
|
||||
],
|
||||
directory: URL(fileURLWithPath: "/Users/test/Downloads/Adobe Audition_25.0-zh_CN-macuniversal"),
|
||||
productsToDownload: [product],
|
||||
createAt: Date(),
|
||||
totalStatus: .completed(DownloadStatus.CompletionInfo(
|
||||
timestamp: Date(),
|
||||
@@ -665,7 +758,7 @@ struct PackageRow: View {
|
||||
totalDownloadedSize: 878454797,
|
||||
totalSize: 878454797,
|
||||
totalSpeed: 0,
|
||||
platform: ""
|
||||
platform: "macuniversal"
|
||||
),
|
||||
onCancel: {},
|
||||
onPause: {},
|
||||
@@ -677,20 +770,31 @@ struct PackageRow: View {
|
||||
}
|
||||
|
||||
#Preview("暂停") {
|
||||
DownloadProgressView(
|
||||
let product = ProductsToDownload(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
buildGuid: "123"
|
||||
)
|
||||
let package = Package(
|
||||
type: "Application",
|
||||
fullPackageName: "AdobeAudition25All",
|
||||
downloadSize: 878454797,
|
||||
downloadURL: "https://example.com/download",
|
||||
packageVersion: "25.0.0.1"
|
||||
)
|
||||
package.status = .paused
|
||||
package.progress = 0.52
|
||||
package.downloadedSize = 457424883
|
||||
product.packages = [package]
|
||||
|
||||
return DownloadProgressView(
|
||||
task: NewDownloadTask(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
language: "zh_CN",
|
||||
displayName: "Adobe Audition",
|
||||
directory: URL(fileURLWithPath: "Adobe Downloader Audition_25.0-zh_CN-macuniversal"),
|
||||
productsToDownload: [
|
||||
ProductsToDownload(
|
||||
sapCode: "AUDT",
|
||||
version: "25.0",
|
||||
buildGuid: "123"
|
||||
)
|
||||
],
|
||||
directory: URL(fileURLWithPath: "/Users/test/Downloads/Adobe Audition_25.0-zh_CN-macuniversal"),
|
||||
productsToDownload: [product],
|
||||
createAt: Date(),
|
||||
totalStatus: .paused(DownloadStatus.PauseInfo(
|
||||
reason: .userRequested,
|
||||
@@ -701,7 +805,7 @@ struct PackageRow: View {
|
||||
totalDownloadedSize: 457424883,
|
||||
totalSize: 878454797,
|
||||
totalSpeed: 0,
|
||||
platform: ""
|
||||
platform: "macuniversal"
|
||||
),
|
||||
onCancel: {},
|
||||
onPause: {},
|
||||
|
||||
@@ -25,9 +25,17 @@ class SecureCommandHandler {
|
||||
case .install:
|
||||
return "installer -pkg \"\(path1)\" -target /"
|
||||
case .uninstall:
|
||||
if path1.contains("*") {
|
||||
return "rm -rf \(path1)"
|
||||
}
|
||||
if path1.hasPrefix("\"") && path1.hasSuffix("\"") {
|
||||
return "rm -rf \(path1)"
|
||||
}
|
||||
return "rm -rf \"\(path1)\""
|
||||
case .moveFile:
|
||||
return "cp \"\(path1)\" \"\(path2)\""
|
||||
let source = path1.hasPrefix("\"") ? path1 : "\"\(path1)\""
|
||||
let dest = path2.hasPrefix("\"") ? path2 : "\"\(path2)\""
|
||||
return "cp \(source) \(dest)"
|
||||
case .setPermissions:
|
||||
return "chmod \(permissions) \"\(path1)\""
|
||||
case .shellCommand:
|
||||
@@ -36,13 +44,14 @@ class SecureCommandHandler {
|
||||
}
|
||||
|
||||
static func validatePath(_ path: String) -> Bool {
|
||||
let cleanPath = path.trimmingCharacters(in: .init(charactersIn: "\"'"))
|
||||
let allowedPaths = ["/Library/Application Support/Adobe"]
|
||||
if allowedPaths.contains(where: { path.hasPrefix($0) }) {
|
||||
if allowedPaths.contains(where: { cleanPath.hasPrefix($0) }) {
|
||||
return true
|
||||
}
|
||||
|
||||
let forbiddenPaths = ["/System", "/usr", "/bin", "/sbin", "/var"]
|
||||
return !forbiddenPaths.contains { path.hasPrefix($0) }
|
||||
return !forbiddenPaths.contains { cleanPath.hasPrefix($0) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,14 +90,18 @@ class HelperTool: NSObject, HelperToolProtocol {
|
||||
|
||||
func executeCommand(type: CommandType, path1: String, path2: String, permissions: Int, withReply reply: @escaping (String) -> Void) {
|
||||
operationQueue.async {
|
||||
self.logger.notice("收到安全命令执行请求")
|
||||
|
||||
guard let shellCommand = SecureCommandHandler.createCommand(type: type, path1: path1, path2: path2, permissions: permissions) else {
|
||||
self.logger.error("不安全的路径访问被拒绝")
|
||||
reply("Error: Invalid path access")
|
||||
return
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
self.logger.notice("收到安全命令执行请求: \(shellCommand, privacy: .public)")
|
||||
#else
|
||||
self.logger.notice("收到安全命令执行请求")
|
||||
#endif
|
||||
|
||||
let isSetupCommand = shellCommand.contains("Setup") && shellCommand.contains("--install")
|
||||
|
||||
let task = Process()
|
||||
@@ -210,18 +223,33 @@ extension HelperTool: NSXPCListenerDelegate {
|
||||
return false
|
||||
}
|
||||
|
||||
var requirement: SecRequirement?
|
||||
let requirementString = "anchor apple generic and identifier \"com.x1a0he.macOS.Adobe-Downloader\""
|
||||
guard SecRequirementCreateWithString(requirementString as CFString,
|
||||
[], &requirement) == errSecSuccess,
|
||||
let req = requirement else {
|
||||
logger.error("签名要求创建失败")
|
||||
var staticCode: SecStaticCode?
|
||||
let staticCodeResult = SecCodeCopyStaticCode(code, [], &staticCode)
|
||||
guard staticCodeResult == errSecSuccess,
|
||||
let staticCodeRef = staticCode else {
|
||||
logger.error("获取静态代码失败: \(staticCodeResult)")
|
||||
return false
|
||||
}
|
||||
|
||||
let validityResult = SecCodeCheckValidity(code, [], req)
|
||||
var requirement: SecRequirement?
|
||||
let requirementString = "identifier \"com.x1a0he.macOS.Adobe-Downloader\" and anchor apple generic and certificate leaf[subject.CN] = \"Apple Development: x1a0he@outlook.com (LFN2762T4F)\" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */"
|
||||
guard SecRequirementCreateWithString(requirementString as CFString,
|
||||
[], &requirement) == errSecSuccess,
|
||||
let req = requirement else {
|
||||
self.logger.error("签名要求创建失败")
|
||||
return false
|
||||
}
|
||||
|
||||
let validityResult = SecStaticCodeCheckValidity(staticCodeRef, [], req)
|
||||
if validityResult != errSecSuccess {
|
||||
logger.error("代码签名验证不匹配: \(validityResult)")
|
||||
self.logger.error("代码签名验证不匹配: \(validityResult), 要求字符串: \(requirementString)")
|
||||
|
||||
var signingInfo: CFDictionary?
|
||||
if SecCodeCopySigningInformation(staticCodeRef, [], &signingInfo) == errSecSuccess,
|
||||
let info = signingInfo as? [String: Any] {
|
||||
self.logger.notice("实际签名信息: \(String(describing: info))")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user