diff options
| -rw-r--r-- | Hotline/Application-macOS.swift | 3 | ||||
| -rw-r--r-- | Hotline/Info.plist | 35 | ||||
| -rw-r--r-- | Hotline/Models/Bookmark.swift | 39 | ||||
| -rw-r--r-- | Hotline/Utility/FoundationExtensions.swift | 30 | ||||
| -rw-r--r-- | Hotline/macOS/TrackerView.swift | 29 |
5 files changed, 136 insertions, 0 deletions
diff --git a/Hotline/Application-macOS.swift b/Hotline/Application-macOS.swift index 9aec187..016cb60 100644 --- a/Hotline/Application-macOS.swift +++ b/Hotline/Application-macOS.swift @@ -22,6 +22,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { AppLaunchState.shared.launchState = .launched + let blah = NSFileTypeForHFSTypeCode("HTbm".fourCharCode()) + print("FILETYPE: \(blah)", blah!, "'HTbm'") + CKContainer.default().accountStatus { status, error in switch status { case .noAccount: diff --git a/Hotline/Info.plist b/Hotline/Info.plist index 8b58335..ac7a2c9 100644 --- a/Hotline/Info.plist +++ b/Hotline/Info.plist @@ -2,6 +2,19 @@ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeName</key> + <string>Hotline Bookmark</string> + <key>LSHandlerRank</key> + <string>Alternate</string> + <key>LSItemContentTypes</key> + <array> + <string>'HTbm'</string> + </array> + </dict> + </array> <key>CFBundleURLTypes</key> <array> <dict> @@ -25,5 +38,27 @@ </array> </dict> </array> + <key>UTImportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array/> + <key>UTTypeDescription</key> + <string>Hotline Bookmark</string> + <key>UTTypeIconFiles</key> + <array/> + <key>UTTypeIdentifier</key> + <string>'HTbm'</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>.htbm</string> + </array> + <key>public.mime-type</key> + <array/> + </dict> + </dict> + </array> </dict> </plist> diff --git a/Hotline/Models/Bookmark.swift b/Hotline/Models/Bookmark.swift index c713bb0..234a196 100644 --- a/Hotline/Models/Bookmark.swift +++ b/Hotline/Models/Bookmark.swift @@ -96,6 +96,45 @@ 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 { + return nil + } + + print("Bookmark: Parsing Hotline bookmark file...") + + 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 = addressHost + self.address = addressHost + self.port = addressPort + self.login = loginString.isEmpty ? nil : loginString + self.password = passwordString.isEmpty ? nil : passwordString + } + static func populateDefaults(force: Bool = false, context: ModelContext) { if force || Bookmark.fetchCount(context: context) == 0 { Bookmark.add(Bookmark.DefaultBookmarks, context: context) diff --git a/Hotline/Utility/FoundationExtensions.swift b/Hotline/Utility/FoundationExtensions.swift index 14e76d5..0c8966a 100644 --- a/Hotline/Utility/FoundationExtensions.swift +++ b/Hotline/Utility/FoundationExtensions.swift @@ -202,6 +202,19 @@ extension Array where Element == UInt8 { return str } + mutating func consumeLongPString() -> String? { + let (str, len) = self.readLongPString(at: 0) + guard let str = str else { + return nil + } + if len == 0 { + return "" + } + + self.removeFirst(len) + return str + } + mutating func consumeString(_ length: Int) -> String? { guard let val = self.readString(at: 0, length: length) else { return nil @@ -388,6 +401,23 @@ extension Array where Element == UInt8 { mutating func appendData(_ data: [UInt8]) { self.append(contentsOf: data) } + + mutating func hotlineEncrypt() { + for i in (0..<self.count).reversed() { + self[i] = 0xFF - self[i] + } + } + + func hotlineEncrypted() -> [UInt8] { + var cpy = [UInt8](self) + + + + for i in (0..<cpy.count).reversed() { + cpy[i] = 0xFF - cpy[i] + } + return cpy + } } extension Data { diff --git a/Hotline/macOS/TrackerView.swift b/Hotline/macOS/TrackerView.swift index 104e15d..5a9db9b 100644 --- a/Hotline/macOS/TrackerView.swift +++ b/Hotline/macOS/TrackerView.swift @@ -1,5 +1,6 @@ import SwiftUI import SwiftData +import UniformTypeIdentifiers struct TrackerView: View { // @Environment(BookmarksOld.self) private var bookmarks: BookmarksOld @@ -12,6 +13,7 @@ struct TrackerView: View { @State private var trackerSheetPresented: Bool = false @State private var trackerSheetBookmark: Bookmark? = nil @State private var attemptedPrepopulate: Bool = false + @State private var fileDropActive = false @Query(sort: \Bookmark.order) private var bookmarks: [Bookmark] @State private var selection: Bookmark? = nil @@ -137,6 +139,33 @@ struct TrackerView: View { } return .ignored } + .onDrop(of: [UTType.fileURL], isTargeted: $fileDropActive) { providers, dropPoint in + for provider in providers { + let _ = provider.loadDataRepresentation(for: UTType.fileURL) { dataRepresentation, err in + // HOTLINE CREATOR CODE: 1213484099 + // HOTLINE BOOKMARK TYPE CODE: 1213489773 + + if let filePathData = dataRepresentation, + let filePath = String(data: filePathData, encoding: .utf8), + let fileURL = URL(string: filePath) { + + print("Hotline Bookmark: Dropped from ", fileURL.path(percentEncoded: false)) + + DispatchQueue.main.async { + if let newBookmark = Bookmark(fileURL: fileURL) { + print("Hotline Bookmark: Added bookmark.") + Bookmark.add(newBookmark, context: modelContext) + } + else { + print("Hotline Bookmark: Failed to parse.") + } + } + } + } + } + + return true + } .sheet(item: $trackerSheetBookmark) { item in TrackerBookmarkSheet(item) } |