2022-02-12 06:52:45 +00:00
|
|
|
import Foundation
|
|
|
|
import Brief
|
|
|
|
import AppleArchive
|
|
|
|
import System
|
|
|
|
import Cocoa
|
|
|
|
import Security.Authorization
|
|
|
|
import Security.AuthorizationTags
|
|
|
|
|
|
|
|
class Updater: UpdaterProtocol {
|
|
|
|
|
2022-02-17 06:02:57 +00:00
|
|
|
func installUpdate(url: URL, to destinationURL: URL) async throws -> String {
|
2022-02-12 06:52:45 +00:00
|
|
|
// let (downloadedURL, _) = try await URLSession.shared.download(from: url)
|
|
|
|
// let unzipped = try await decompress(url: downloadedURL)
|
2022-02-17 06:02:57 +00:00
|
|
|
// try await move(url: unzipped, to: destinationURL)
|
2022-02-12 06:52:45 +00:00
|
|
|
// let config = NSWorkspace.OpenConfiguration()
|
|
|
|
// config.activates = true
|
2022-02-17 06:02:57 +00:00
|
|
|
// TODO: clean
|
|
|
|
_ = try await authorize()
|
|
|
|
// if let host = NSRunningApplication.runningApplications(withBundleIdentifier: "com.maxgoedjen.Secretive.Host").first(where: { $0.bundleURL?.path.hasPrefix("/Applications") ?? false }) {
|
|
|
|
// host.terminate()
|
2022-02-12 06:52:45 +00:00
|
|
|
//
|
2022-02-17 06:02:57 +00:00
|
|
|
// }
|
2022-02-12 06:52:45 +00:00
|
|
|
return "OK"
|
|
|
|
}
|
|
|
|
|
|
|
|
func decompress(url: URL) async throws -> URL {
|
|
|
|
let zipURL = url.deletingPathExtension().appendingPathExtension("zip")
|
|
|
|
try FileManager.default.copyItem(at: url, to: zipURL)
|
|
|
|
let id = UUID()
|
|
|
|
let destinationURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(id.uuidString)/")
|
|
|
|
_ = try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: [:])
|
|
|
|
let process = Process()
|
|
|
|
let pipe = Pipe()
|
|
|
|
process.launchPath = "/usr/bin/unzip"
|
|
|
|
process.arguments = ["-o", zipURL.path, "-d", destinationURL.path]
|
|
|
|
process.standardOutput = pipe
|
|
|
|
try process.run()
|
|
|
|
_ = try pipe.fileHandleForReading.readToEnd()
|
|
|
|
guard let appURL = try FileManager.default.contentsOfDirectory(at: destinationURL, includingPropertiesForKeys: nil).first(where: { $0.pathExtension == "app" }) else {
|
|
|
|
throw DecompressionError(reason: "Unzip failed")
|
|
|
|
}
|
|
|
|
return appURL
|
|
|
|
}
|
|
|
|
|
2022-02-17 06:02:57 +00:00
|
|
|
func move(url: URL, to destinationURL: URL) async throws {
|
|
|
|
let auth = try await authorize()
|
|
|
|
try await move(url: url, to: destinationURL)
|
|
|
|
try await revokeAuthorization(auth)
|
2022-02-12 06:52:45 +00:00
|
|
|
}
|
|
|
|
|
2022-02-17 06:02:57 +00:00
|
|
|
func authorize() async throws -> AuthorizationRef {
|
2022-02-12 06:52:45 +00:00
|
|
|
let flags = AuthorizationFlags()
|
|
|
|
var authorization: AuthorizationRef? = nil
|
2022-02-17 06:02:57 +00:00
|
|
|
AuthorizationCreate(nil, nil, flags, &authorization)
|
2022-02-12 06:52:45 +00:00
|
|
|
let authFlags: AuthorizationFlags = [.interactionAllowed, .extendRights, .preAuthorize]
|
2022-02-17 06:02:57 +00:00
|
|
|
var result: OSStatus?
|
2022-02-12 06:52:45 +00:00
|
|
|
kAuthorizationRightExecute.withCString { cString in
|
|
|
|
var item = AuthorizationItem(name: cString, valueLength: 0, value: nil, flags: 0)
|
|
|
|
withUnsafeMutablePointer(to: &item) { pointer in
|
|
|
|
var rights = AuthorizationRights(count: 1, items: pointer)
|
2022-02-17 06:02:57 +00:00
|
|
|
result = AuthorizationCopyRights(authorization!, &rights, nil, authFlags, nil)
|
2022-02-12 06:52:45 +00:00
|
|
|
}
|
|
|
|
}
|
2022-02-17 06:02:57 +00:00
|
|
|
guard result == errAuthorizationSuccess, let authorization = authorization else {
|
|
|
|
throw RightsNotAcquiredError()
|
|
|
|
}
|
|
|
|
return authorization
|
2022-02-12 06:52:45 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-02-17 06:02:57 +00:00
|
|
|
func revokeAuthorization(_ authorization: AuthorizationRef) async throws {
|
|
|
|
AuthorizationFree(authorization, .destroyRights)
|
|
|
|
}
|
2022-02-12 06:52:45 +00:00
|
|
|
|
2022-02-17 06:02:57 +00:00
|
|
|
func priveledgedMove(url: URL, to destination: URL) async throws {
|
|
|
|
try FileManager.default.replaceItemAt(destination, withItemAt: url)
|
2022-02-12 06:52:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Updater {
|
2022-02-17 06:02:57 +00:00
|
|
|
|
2022-02-12 06:52:45 +00:00
|
|
|
struct DecompressionError: Error, LocalizedError {
|
|
|
|
let reason: String
|
|
|
|
}
|
2022-02-17 06:02:57 +00:00
|
|
|
|
|
|
|
struct RightsNotAcquiredError: Error, LocalizedError {
|
|
|
|
}
|
|
|
|
|
2022-02-12 06:52:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension URLSession {
|
|
|
|
|
|
|
|
@available(macOS, deprecated: 12.0)
|
|
|
|
public func download(from url: URL) async throws -> (URL, URLResponse) {
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
let task = downloadTask(with: url) { url, response, error in
|
|
|
|
guard let url = url, let response = response else {
|
|
|
|
continuation.resume(throwing: error ?? UnknownError())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
continuation.resume(returning: (url, response))
|
|
|
|
}
|
|
|
|
task.resume()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct UnknownError: Error {}
|
|
|
|
|
|
|
|
}
|