From 9e8a3c99c26991efcc61b6cbe40ee4101c0439d7 Mon Sep 17 00:00:00 2001 From: Cole Feuer <13751213+tagavari@users.noreply.github.com> Date: Sat, 13 Aug 2022 11:16:40 -0400 Subject: [PATCH] Use ditto to extract archives --- AirMessage.xcodeproj/project.pbxproj | 25 +++----- .../SoftwareUpdateViewController.swift | 1 - AirMessage/Helper/ArchiveHelper.swift | 20 ++++++ .../Helper/FileNormalizationHelper.swift | 62 +++---------------- AirMessage/Helper/ProcessHelper.swift | 60 ++++++++++++++++++ AirMessage/Helper/UpdateHelper.swift | 3 +- 6 files changed, 97 insertions(+), 74 deletions(-) create mode 100644 AirMessage/Helper/ArchiveHelper.swift create mode 100644 AirMessage/Helper/ProcessHelper.swift diff --git a/AirMessage.xcodeproj/project.pbxproj b/AirMessage.xcodeproj/project.pbxproj index ea16b53..b087bb3 100644 --- a/AirMessage.xcodeproj/project.pbxproj +++ b/AirMessage.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ ED28095725A20332009E7636 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED28095625A20332009E7636 /* OnboardingViewController.swift */; }; ED28095925A20333009E7636 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ED28095825A20333009E7636 /* Assets.xcassets */; }; ED28095C25A20333009E7636 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ED28095A25A20333009E7636 /* Main.storyboard */; }; + ED31446E28A7ECC4009FC28C /* ArchiveHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED31446D28A7ECC4009FC28C /* ArchiveHelper.swift */; }; + ED31447028A7F284009FC28C /* ProcessHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED31446F28A7F284009FC28C /* ProcessHelper.swift */; }; ED440E0D269FAC6E00BFBEE5 /* ServerStateRecovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED440E0C269FAC6E00BFBEE5 /* ServerStateRecovery.swift */; }; ED440E0E269FB51F00BFBEE5 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = ED440E10269FB51F00BFBEE5 /* Localizable.stringsdict */; }; ED44738726A387DD006F5F0E /* KeychainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED44738626A387DD006F5F0E /* KeychainManager.swift */; }; @@ -64,7 +66,6 @@ ED8F3A7E2749A3D50069F25D /* AppleScriptCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8F3A7D2749A3D50069F25D /* AppleScriptCodes.swift */; }; ED8F3A802749AC300069F25D /* FileDownloadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8F3A7F2749AC300069F25D /* FileDownloadRequest.swift */; }; ED98B3EF2711D8300029DD09 /* Ink in Frameworks */ = {isa = PBXBuildFile; productRef = ED98B3EE2711D8300029DD09 /* Ink */; }; - ED98B3F22711D84E0029DD09 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = ED98B3F12711D84E0029DD09 /* Zip */; }; ED9C293A2756684200EA07C8 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = ED9C29392756684200EA07C8 /* Sentry */; }; ED9FBBC627E13F430037DF16 /* Zlib in Frameworks */ = {isa = PBXBuildFile; productRef = ED9FBBC527E13F430037DF16 /* Zlib */; }; EDADEB1926A0E3D9001DA84A /* StorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDADEB1826A0E3D9001DA84A /* StorageManager.swift */; }; @@ -304,6 +305,8 @@ ED28095B25A20333009E7636 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; ED28095D25A20333009E7636 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; ED28095E25A20333009E7636 /* AirMessage.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AirMessage.entitlements; sourceTree = ""; }; + ED31446D28A7ECC4009FC28C /* ArchiveHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveHelper.swift; sourceTree = ""; }; + ED31446F28A7F284009FC28C /* ProcessHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessHelper.swift; sourceTree = ""; }; ED440E0C269FAC6E00BFBEE5 /* ServerStateRecovery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerStateRecovery.swift; sourceTree = ""; }; ED440E0F269FB51F00BFBEE5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; ED44738626A387DD006F5F0E /* KeychainManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainManager.swift; sourceTree = ""; }; @@ -546,7 +549,6 @@ files = ( EDB04DBE273DDFDA00673AE4 /* SQLite in Frameworks */, ED9C293A2756684200EA07C8 /* Sentry in Frameworks */, - ED98B3F22711D84E0029DD09 /* Zip in Frameworks */, EDC898662752BF290081BDB7 /* OpenSSL.framework in Frameworks */, ED456D99289EFE1E00729CF6 /* WebSocketKit in Frameworks */, ED9FBBC627E13F430037DF16 /* Zlib in Frameworks */, @@ -742,6 +744,7 @@ ED891A4625A30BAE002230EB /* Helper */ = { isa = PBXGroup; children = ( + ED31446D28A7ECC4009FC28C /* ArchiveHelper.swift */, ED8D56472846E00000B6DB6B /* AssertionHelper.swift */, EDC1DAF7274DA24C00D32C6B /* AtomicValue.swift */, 2EAEE35ACA7C4D8977E66420 /* CompressionHelper.swift */, @@ -755,6 +758,7 @@ ED15912226AC5484008F5304 /* LogManager.swift */, ED891A4725A30BC6002230EB /* PasswordGrade.swift */, 2EAEEE327A491386A20EC729 /* PreferencesManager.swift */, + ED31446F28A7F284009FC28C /* ProcessHelper.swift */, ED853E062744218900DCF446 /* ReadWriteLock.swift */, EDD70D6B2699F56E00E8DC06 /* ServerLaunch.swift */, EDADEB1826A0E3D9001DA84A /* StorageManager.swift */, @@ -1199,7 +1203,6 @@ packageProductDependencies = ( EDDE47302688C2890029628B /* Swifter */, ED98B3EE2711D8300029DD09 /* Ink */, - ED98B3F12711D84E0029DD09 /* Zip */, EDB04DBD273DDFDA00673AE4 /* SQLite */, ED9C29392756684200EA07C8 /* Sentry */, ED9FBBC527E13F430037DF16 /* Zlib */, @@ -1281,7 +1284,6 @@ packageReferences = ( EDDE472F2688C2890029628B /* XCRemoteSwiftPackageReference "swifter" */, ED98B3ED2711D8300029DD09 /* XCRemoteSwiftPackageReference "ink" */, - ED98B3F02711D84E0029DD09 /* XCRemoteSwiftPackageReference "Zip" */, EDB04DBC273DDFDA00673AE4 /* XCRemoteSwiftPackageReference "SQLite.swift" */, ED9C29382756684200EA07C8 /* XCRemoteSwiftPackageReference "sentry-cocoa" */, ED456D97289EFE1E00729CF6 /* XCRemoteSwiftPackageReference "websocket-kit-osx10" */, @@ -1417,6 +1419,7 @@ 2EAEE232C464EF7328584EA2 /* AccountType.swift in Sources */, ED1E5684269A50360019CCD5 /* DraggableAppView.swift in Sources */, ED8F3A792749A05D0069F25D /* MessageError.swift in Sources */, + ED31446E28A7ECC4009FC28C /* ArchiveHelper.swift in Sources */, ED853E1027442BD900DCF446 /* FileHandleCompat.swift in Sources */, ED15912326AC5484008F5304 /* LogManager.swift in Sources */, 2EAEE84D6A21D1570484C26A /* ServerState.swift in Sources */, @@ -1452,6 +1455,7 @@ 2EAEE263C6403206657014EB /* EncryptionManager.swift in Sources */, EDDA7B87274333540027A82C /* FileNormalizationHelper.swift in Sources */, ED8D56482846E00000B6DB6B /* AssertionHelper.swift in Sources */, + ED31447028A7F284009FC28C /* ProcessHelper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1869,14 +1873,6 @@ minimumVersion = 0.5.1; }; }; - ED98B3F02711D84E0029DD09 /* XCRemoteSwiftPackageReference "Zip" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/marmelroy/Zip.git"; - requirement = { - kind = upToNextMinorVersion; - minimumVersion = 2.1.2; - }; - }; ED9C29382756684200EA07C8 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/getsentry/sentry-cocoa.git"; @@ -1914,11 +1910,6 @@ package = ED98B3ED2711D8300029DD09 /* XCRemoteSwiftPackageReference "ink" */; productName = Ink; }; - ED98B3F12711D84E0029DD09 /* Zip */ = { - isa = XCSwiftPackageProductDependency; - package = ED98B3F02711D84E0029DD09 /* XCRemoteSwiftPackageReference "Zip" */; - productName = Zip; - }; ED9C29392756684200EA07C8 /* Sentry */ = { isa = XCSwiftPackageProductDependency; package = ED9C29382756684200EA07C8 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; diff --git a/AirMessage/Controllers/SoftwareUpdateViewController.swift b/AirMessage/Controllers/SoftwareUpdateViewController.swift index a121ea5..46b612a 100644 --- a/AirMessage/Controllers/SoftwareUpdateViewController.swift +++ b/AirMessage/Controllers/SoftwareUpdateViewController.swift @@ -6,7 +6,6 @@ import Foundation import AppKit import WebKit import Ink -import Zip class SoftwareUpdateViewController: NSViewController { //Outlets diff --git a/AirMessage/Helper/ArchiveHelper.swift b/AirMessage/Helper/ArchiveHelper.swift new file mode 100644 index 0000000..27c5223 --- /dev/null +++ b/AirMessage/Helper/ArchiveHelper.swift @@ -0,0 +1,20 @@ +// +// ArchiveHelper.swift +// AirMessage +// +// Created by Cole Feuer on 2022-08-13. +// + +import Foundation + +func decompressArchive(fromURL: URL, to toURL: URL) throws { + //Run ditto to extract the files + let process = Process() + if #available(macOS 10.13, *) { + process.executableURL = URL(fileURLWithPath: "/usr/bin/ditto") + } else { + process.launchPath = "/usr/bin/ditto" + } + process.arguments = ["-x", "-k", fromURL.path, toURL.path] + try runProcessCatchError(process) +} diff --git a/AirMessage/Helper/FileNormalizationHelper.swift b/AirMessage/Helper/FileNormalizationHelper.swift index ab1c5c0..bd5902e 100644 --- a/AirMessage/Helper/FileNormalizationHelper.swift +++ b/AirMessage/Helper/FileNormalizationHelper.swift @@ -41,11 +41,10 @@ func normalizeFile(url inputFile: URL, ext: String) -> NormalizedFile? { let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/sips") process.arguments = ["--setProperty", "format", "jpeg", inputFile.path, "--out", tempFile.path] - let processResult = runProcessLogError(process) - - //Check the process result - guard processResult else { - LogManager.log("Failed to convert file \(inputFile.path) from HEIC to JPEG", level: .info) + do { + try runProcessCatchError(process) + } catch { + LogManager.log("Failed to convert file \(inputFile.path) from HEIC to JPEG: \(error)", level: .info) try? FileManager.default.removeItem(at: tempFile) //Clean up immediately return nil } @@ -64,11 +63,10 @@ func normalizeFile(url inputFile: URL, ext: String) -> NormalizedFile? { let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/afconvert") process.arguments = ["-f", "mp4f", "-d", "aac", inputFile.path, "-o", tempFile.path] - let processResult = runProcessLogError(process) - - //Check the process result - guard processResult else { - LogManager.log("Failed to convert file \(inputFile.path) from CAF to MP4", level: .info) + do { + try runProcessCatchError(process) + } catch { + LogManager.log("Failed to convert file \(inputFile.path) from CAF to MP4: \(error)", level: .info) try? FileManager.default.removeItem(at: tempFile) //Clean up immediately return nil } @@ -81,47 +79,3 @@ func normalizeFile(url inputFile: URL, ext: String) -> NormalizedFile? { return nil } } - -/** - Starts a process, waits for it to finish, and logs any errors the process encountered while running - - Parameters: - - process: The process to start and monitor - - Returns: Whether the process ran successfully - */ -@available(macOS 10.13, *) -private func runProcessLogError(_ process: Process) -> Bool { - //Capture the process' error pipe - let errorPipe = Pipe() - process.standardOutput = nil - process.standardError = errorPipe - - //Start the process - do { - try process.run() - } catch { - LogManager.log("An error occurred while running process: \(error)", level: .info) - SentrySDK.capture(error: error) - return false - } - - //Wait for the process to exit - process.waitUntilExit() - - //Check the output code - guard process.terminationStatus == 0 else { - let errorFileHandle = errorPipe.fileHandleForReading - - let errorMessage: String? - if let data = try? errorFileHandle.readToEndCompat(), !data.isEmpty { - errorMessage = String(data: data, encoding: .utf8) - } else { - errorMessage = nil - } - - LogManager.log("An error occurred while running process: \(errorMessage ?? "Unknown error")", level: .info) - SentrySDK.capture(message: "An error occurred while running process: \(errorMessage ?? "Unknown error")") - return false - } - - return true -} diff --git a/AirMessage/Helper/ProcessHelper.swift b/AirMessage/Helper/ProcessHelper.swift new file mode 100644 index 0000000..f9de1af --- /dev/null +++ b/AirMessage/Helper/ProcessHelper.swift @@ -0,0 +1,60 @@ +// +// ProcessHelper.swift +// AirMessage +// +// Created by Cole Feuer on 2022-08-13. +// + +import Foundation +import Sentry + +///Starts a process, waits for it to finish, and logs any errors the process encountered while running +func runProcessCatchError(_ process: Process) throws { + //Capture the process' error pipe + let errorPipe = Pipe() + process.standardOutput = nil + process.standardError = errorPipe + + //Start the process + if #available(macOS 10.13, *) { + try process.run() + } else { + process.launch() + } + + //Wait for the process to exit + process.waitUntilExit() + + //Check the output code + guard process.terminationStatus == 0 else { + let errorFileHandle = errorPipe.fileHandleForReading + + let errorMessage: String? + if let data = try? errorFileHandle.readToEndCompat(), !data.isEmpty { + errorMessage = String(data: data, encoding: .utf8) + } else { + errorMessage = nil + } + + //Throw a process error + throw ProcessError(exitCode: process.terminationStatus, message: errorMessage) + } +} + +struct ProcessError: Error { + let exitCode: Int32 + let message: String? + + init(exitCode: Int32, message: String?) { + self.exitCode = exitCode + self.message = message + } + + public var localizedDescription: String { + if let message = message { + return "Exit code \(exitCode): \(message)" + } else { + return "Exit code \(exitCode)" + } + } +} \ No newline at end of file diff --git a/AirMessage/Helper/UpdateHelper.swift b/AirMessage/Helper/UpdateHelper.swift index 7532740..f13b084 100644 --- a/AirMessage/Helper/UpdateHelper.swift +++ b/AirMessage/Helper/UpdateHelper.swift @@ -4,7 +4,6 @@ import Foundation import AppKit -import Zip import Sentry class UpdateHelper { @@ -423,7 +422,7 @@ private class UpdateDownloadURLDelegate: ForwardCompatURLSessionDelegate, URLSes LogManager.log("Downloaded and moved update file to \(zippedFile.path)", level: .info) //Unzip file - try Zip.unzipFile(zippedFile, destination: unzippedFolder, overwrite: true, password: nil) + try decompressArchive(fromURL: zippedFile, to: unzippedFolder) LogManager.log("Decompressed update file to \(unzippedFolder.path)", level: .info) //Find app file