diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-05-23 12:50:16 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-05-23 12:50:16 -0700 |
| commit | 23b3f338f0818a53b2499692e999a0528e5d9753 (patch) | |
| tree | 19f2d018824cdc41c0aa37dde6bde5d9054be9f2 /Hotline/Models | |
| parent | 842611754debec7d1814e44e6a2cf302e84c0d66 (diff) | |
Add Bookmark exporting to Server window context menu.
Diffstat (limited to 'Hotline/Models')
| -rw-r--r-- | Hotline/Models/Bookmark.swift | 100 |
1 files changed, 91 insertions, 9 deletions
diff --git a/Hotline/Models/Bookmark.swift b/Hotline/Models/Bookmark.swift index 234a196..115e5a9 100644 --- a/Hotline/Models/Bookmark.swift +++ b/Hotline/Models/Bookmark.swift @@ -96,16 +96,11 @@ final class Bookmark { self.serverDescription = server.description } - init?(fileURL bookmarkFileURL: URL) { - guard let fileAttributes = try? FileManager.default.attributesOfItem(atPath: bookmarkFileURL.path(percentEncoded: false)), - let fileSize = fileAttributes[FileAttributeKey.size] as? UInt64, - fileSize <= 2000, - let fileData = try? Data(contentsOf: bookmarkFileURL) else { + init?(fileData: Data, name: String? = nil) { + guard fileData.count <= 2000 else { return nil } - - print("Bookmark: Parsing Hotline bookmark file...") - + var fileDataArray: [UInt8] = [UInt8](fileData) guard let headerValue = fileDataArray.consumeUInt32(), @@ -128,13 +123,100 @@ final class Bookmark { let (addressHost, addressPort) = Server.parseServerAddressAndPort(addressString) self.type = .server - self.name = addressHost + if let name = name, !name.isEmpty { + self.name = name + } + else { + self.name = addressHost + } self.address = addressHost self.port = addressPort self.login = loginString.isEmpty ? nil : loginString self.password = passwordString.isEmpty ? nil : passwordString } + convenience init?(fileURL bookmarkFileURL: URL) { + guard bookmarkFileURL.isFileURL, + let fileAttributes = try? FileManager.default.attributesOfItem(atPath: bookmarkFileURL.path(percentEncoded: false)), + let fileSize = fileAttributes[FileAttributeKey.size] as? UInt64, + fileSize <= 2000, + let fileData = try? Data(contentsOf: bookmarkFileURL) else { + return nil + } + + print("Bookmark: Parsing Hotline bookmark file...") + + let fileName = bookmarkFileURL.deletingPathExtension().lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines) + + self.init(fileData: fileData, name: fileName) + +// var fileDataArray: [UInt8] = [UInt8](fileData) +// +// guard let headerValue = fileDataArray.consumeUInt32(), +// headerValue.fourCharCode() == "HTsc", +// let versionNumber = fileDataArray.consumeUInt16(), +// versionNumber == 1, +// fileDataArray.consume(128), // Skip 128 reserved bytes. +// let loginLength = fileDataArray.consumeUInt16(), +// let loginData: Data = fileDataArray.consumeBytes(32), +// let passwordLength = fileDataArray.consumeUInt16(), +// let passwordData: Data = fileDataArray.consumeBytes(32), +// let addressLength = fileDataArray.consumeUInt16(), +// let addressData: Data = fileDataArray.consumeBytes(256), +// let addressString = String(data: addressData[0..<Int(addressLength)], encoding: .ascii), +// let loginString = String(data: loginData[0..<Int(loginLength)], encoding: .ascii), +// let passwordString = String(data: passwordData[0..<Int(passwordLength)], encoding: .ascii) else { +// return nil +// } +// +// let (addressHost, addressPort) = Server.parseServerAddressAndPort(addressString) +// +// self.type = .server +// self.name = fileName.isEmpty ? addressHost : fileName +// self.address = addressHost +// self.port = addressPort +// self.login = loginString.isEmpty ? nil : loginString +// self.password = passwordString.isEmpty ? nil : passwordString + } + + func bookmarkFileData() -> Data? { + guard let addressData = self.displayAddress.data(using: .ascii) else { + return nil + } + + let loginData: Data = self.login?.data(using: .ascii) ?? Data() + let passwordData: Data = self.password?.data(using: .ascii) ?? Data() + + var fileData: Data = Data() + + fileData.appendUInt32("HTsc".fourCharCode()) // magic + fileData.appendUInt16(0x0001) // version + fileData.append(Data(repeating: 0x00, count: 128)) // reserved + + fileData.appendUInt16(UInt16(loginData.count)) + fileData.append(loginData) + if loginData.count < 32 { + // Pad login data to 32 bytes + fileData.append(Data(repeating: 0x00, count: 32 - loginData.count)) + } + + fileData.appendUInt16(UInt16(passwordData.count)) + fileData.append(passwordData) + if passwordData.count < 32 { + // Pad password data to 32 bytes + fileData.append(Data(repeating: 0x00, count: 32 - passwordData.count)) + } + + fileData.appendUInt16(UInt16(addressData.count)) + fileData.append(addressData) + if passwordData.count < 256 { + // Pad address data to 256 bytes + fileData.append(Data(repeating: 0x00, count: 256 - addressData.count)) + } + + return fileData + } + static func populateDefaults(force: Bool = false, context: ModelContext) { if force || Bookmark.fetchCount(context: context) == 0 { Bookmark.add(Bookmark.DefaultBookmarks, context: context) |