diff options
| author | Dustin Mierau <dustin@mierau.me> | 2023-12-04 09:12:58 -0800 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2023-12-04 09:12:58 -0800 |
| commit | 471546236b8991f61d26c0e3aa8ee48b83b983af (patch) | |
| tree | b74331a8c8ea61ee7e1eaedfc7bbd1686bbc4702 /Hotline/Network/HotlineProtocol.swift | |
| parent | 885119acdd27bd53ed3096285c3eca3e2d99ad70 (diff) | |
Starting to hash out a server view and tabs. Added research pdfs. etc.
Diffstat (limited to 'Hotline/Network/HotlineProtocol.swift')
| -rw-r--r-- | Hotline/Network/HotlineProtocol.swift | 388 |
1 files changed, 0 insertions, 388 deletions
diff --git a/Hotline/Network/HotlineProtocol.swift b/Hotline/Network/HotlineProtocol.swift deleted file mode 100644 index 5372e78..0000000 --- a/Hotline/Network/HotlineProtocol.swift +++ /dev/null @@ -1,388 +0,0 @@ -import Foundation - -struct HotlineServer: Identifiable, Hashable { - let id = UUID() - let address: String - let port: UInt16 - let users: UInt16 - let name: String? - let description: String? - - static func == (lhs: HotlineServer, rhs: HotlineServer) -> Bool { - return lhs.id == rhs.id - } - - func hash(into hasher: inout Hasher) { - hasher.combine(self.id) - } -} - -struct HotlineUser: Identifiable, Hashable { - let id: UInt16 - let iconID: UInt16 - let status: UInt16 - let name: String - - var isAdmin: Bool { - return ((self.status & 0x0002) != 0) - } - - var isIdle: Bool { - return ((self.status & 0x0001) != 0) - } - - static func == (lhs: HotlineUser, rhs: HotlineUser) -> Bool { - return lhs.id == rhs.id - } - - init(id: UInt16, iconID: UInt16, status: UInt16, name: String) { - self.id = id - self.iconID = iconID - self.status = status - self.name = name - } - - init(from data: Data) { - self.id = data.readUInt16(at: 0)! - self.iconID = data.readUInt16(at: 2)! - self.status = data.readUInt16(at: 4)! - - let userNameLength = Int(data.readUInt16(at: 6)!) - self.name = data.readString(at: 8, length: userNameLength, encoding: .ascii)! - } - - func hash(into hasher: inout Hasher) { - hasher.combine(self.id) - } - - func encoded() -> Data { - var data = Data() - self.encode(to: &data) - return data - } - - func encode(to data: inout Data) { - data.appendUInt16(self.id) - data.appendUInt16(self.iconID) - data.appendUInt16(self.status) - - let userNameData = name.data(using: .ascii, allowLossyConversion: true)! - - data.appendUInt16(UInt16(userNameData.count)) - data.append(userNameData) - } -} - -struct HotlineUserInfo { - let id: UInt16 - let iconID: UInt16 - let flags: UInt16 - let userName: String - - init(data: Data) { - self.id = data.readUInt16(at: 0)! - self.iconID = data.readUInt16(at: 2)! - self.flags = data.readUInt16(at: 4)! - - let userNameLength = data.readUInt16(at: 6)! - self.userName = data.readString(at: 8, length: Int(userNameLength), encoding: .ascii)! - } -} - -struct HotlineTransactionField { - let type: HotlineTransactionFieldType - let dataSize: UInt16 - let data: Data - - init(type: HotlineTransactionFieldType, dataSize: UInt16, data: Data) { - self.type = type - self.dataSize = dataSize - self.data = data - } - - init(type: HotlineTransactionFieldType, val: UInt8) { - self.init(type: type, dataSize: UInt16(MemoryLayout<UInt8>.size), data: Data(val)) - } - - init(type: HotlineTransactionFieldType, val: UInt16) { - self.init(type: type, dataSize: UInt16(MemoryLayout<UInt16>.size), data: Data(val)) - } - - init(type: HotlineTransactionFieldType, val: UInt32) { - self.init(type: type, dataSize: UInt16(MemoryLayout<UInt32>.size), data: Data(val)) - } - - init(type: HotlineTransactionFieldType, string: String, encoding: String.Encoding = .ascii, encrypt: Bool = false) { - var stringInput = string - - if encrypt { - stringInput = String(string.utf8.map { char in - Character(UnicodeScalar(0xFF - char)) - }) - } - - var stringData: Data? - stringData = stringInput.data(using: encoding, allowLossyConversion: true) - if stringData == nil { - stringData = Data() - } - - self.init(type: type, dataSize: UInt16(stringData!.count), data: stringData!) - } - - init(type: HotlineTransactionFieldType, string: String, encrypt: Bool) { - self.init(type: type, string: string, encoding: .ascii, encrypt: encrypt) - } - - func getUInt8() -> UInt8? { - return self.data.readUInt8(at: 0) - } - - func getUInt16() -> UInt16? { - return self.data.readUInt16(at: 0) - } - - func getUInt32() -> UInt32? { - return self.data.readUInt32(at: 0) - } - - func getInteger() -> Int? { - switch(self.data.count) { - case 1: - if let val = self.getUInt8() { - return Int(val) - } - case 2: - if let val = self.getUInt16() { - return Int(val) - } - case 4: - if let val = self.getUInt32() { - return Int(val) - } - default: - break - } - - return nil - } - - func getString(encoding: String.Encoding = .ascii) -> String? { - return String(data: self.data, encoding: encoding) - } - - func getUserInfo() -> HotlineUserInfo { - return HotlineUserInfo(data: self.data) - } -} - -struct HotlineTransaction { - static let headerSize = 20 - static var sequenceID: UInt32 = 1 - - static func nextID() -> UInt32 { - HotlineTransaction.sequenceID += 1 - return HotlineTransaction.sequenceID - } - - var flags: UInt8 = 0 - var isReply: UInt8 = 0 - var type: HotlineTransactionType - var id: UInt32 = HotlineTransaction.nextID() - var errorCode: UInt32 = 0 - var totalSize: UInt32 = UInt32(HotlineTransaction.headerSize) - var dataSize: UInt32 = 0 - - var fields: [HotlineTransactionField] = [] - - init(type: HotlineTransactionType) { - self.type = type - } - - init(type: HotlineTransactionType, flags: UInt8, isReply: UInt8, id: UInt32, errorCode: UInt32, totalSize: UInt32, dataSize: UInt32) { - self.type = type - self.flags = flags - self.isReply = isReply - self.id = id - self.errorCode = errorCode - self.totalSize = totalSize - self.dataSize = dataSize - } - - mutating func setFieldUInt8(type: HotlineTransactionFieldType, val: UInt8) { - self.fields.append(HotlineTransactionField(type: type, val: val)) - } - - mutating func setFieldUInt16(type: HotlineTransactionFieldType, val: UInt16) { - self.fields.append(HotlineTransactionField(type: type, val: val)) - } - - mutating func setFieldUInt32(type: HotlineTransactionFieldType, val: UInt32) { - self.fields.append(HotlineTransactionField(type: type, val: val)) - } - - mutating func setFieldEncodedString(type: HotlineTransactionFieldType, val: String) { - self.fields.append(HotlineTransactionField(type: type, string: val, encrypt: true)) - } - - mutating func setFieldString(type: HotlineTransactionFieldType, val: String) { - self.fields.append(HotlineTransactionField(type: type, string: val)) - } - - func getField(type: HotlineTransactionFieldType) -> HotlineTransactionField? { - return self.fields.first { p in - p.type == type - } - } - - func getFieldList(type: HotlineTransactionFieldType) -> [HotlineTransactionField] { - return self.fields.filter { p in - p.type == type - } - } - - func encoded() -> Data { - var data = Data() - self.encode(to: &data) - return data - } - - func encode(to data: inout Data) { - data.appendUInt8(self.flags) - data.appendUInt8(self.isReply) - data.appendUInt16(self.type.rawValue) - data.appendUInt32(self.id) - data.appendUInt32(self.errorCode) - - if self.fields.count > 0 { - var fieldData = Data() - fieldData.appendUInt16(UInt16(self.fields.count)) - for f in self.fields { - fieldData.appendUInt16(f.type.rawValue) - fieldData.appendUInt16(f.dataSize) - fieldData.append(f.data) - } - - data.appendUInt32(UInt32(fieldData.count)) - data.appendUInt32(UInt32(fieldData.count)) - data.append(fieldData) - } - else { - data.appendUInt32(2) - data.appendUInt32(2) - data.appendUInt16(0) - } - } -} - -enum HotlineTransactionFieldType: UInt16 { - case errorText = 100 // String - case data = 101 // String - case userName = 102 // String - case userID = 103 // Integer - case userIconID = 104 // Integer - case userLogin = 105 // Encoded string - case userPassword = 106 // Encoded string - case referenceNumber = 107 // Integer - case transferSize = 108 // Integer - case chatOptions = 109 // Integer - case userAccess = 110 // 64-bit integer? - case userAlias = 111 // ??? - case userFlags = 112 // Integer - case options = 113 // 32-bit integer? - case chatID = 114 // Integer - case chatSubject = 115 // String - case waitingCount = 116 // Integer - case serverAgreement = 150 // ??? - case serverBanner = 151 // Data? - case serverBannerType = 152 // Integer - case serverBannerURL = 153 // String - case noServerAgreement = 154 // Integer - case versionNumber = 160 // Integer - case communityBannerID = 161 // Integer - case serverName = 162 // String - // TODO: Add file field types - case quotingMessage = 214 // String? - case automaticResponse = 215 // String - case folderItemCount = 220 // Integer - case userNameWithInfo = 300 // Data { user id: 2, icon id: 2, user flags: 2, user name size: 2, user name: size } - case newsCategoryGUID = 319 // Data? - case newsCategoryListData = 320 // Data { type: 1 (1 = folder, 10 = category, 255 = other), category name: rest } - case newsArticleListData = 321 // Data - case newsCategoryName = 322 // String - case newsCategoryListData15 = 323 // Data - case newsPath = 325 // Data - case newsArticleID = 326 // Integer - case newsArticleDataFlavor = 327 // String - case newsArticleTitle = 328 // String - case newsArticlePoster = 329 // String - case newsArticleDate = 330 // Data { year: 2, ms: 2, secs: 4 } - case newsArticlePrevious = 331 // Integer - case newsArticleNext = 332 // Integer - case newsArticleData = 333 // Data - case newsArticleFlags = 334 // Integer - case newsArticleParentArticle = 335 // Integer - case newsArticleFirstChildArticle = 336 // Integer - case newsArticleRecursiveDelete = 337 // Integer -} - -enum HotlineTransactionType: UInt16 { - case reply = 0 - case error = 100 - case getMessages = 101 - case newMessage = 102 // Server - case oldPostNews = 103 - case serverMessage = 104 // Server - case sendChat = 105 - case chatMessage = 106 // Server - case login = 107 - case sendInstantMessage = 108 - case showAgreement = 109 // Server - case disconnectUser = 110 - case disconnectMessage = 111 // Server - case inviteToNewChat = 112 - case inviteToChat = 113 // Server - case rejectChatInvite = 114 - case joinChat = 115 - case leaveChat = 116 - case notifyChatOfUserChange = 117 // Server - case notifyChatOfUserDelete = 118 // Server - case notifyChatSubject = 119 // Server - case setChatSubject = 120 - case agreed = 121 - case serverBanner = 122 // Server - case getFileNameList = 200 - case downloadFile = 202 - case uploadFile = 203 - case deleteFile = 204 - case newFolder = 205 - case getFileInfo = 206 - case setFileInfo = 207 - case moveFile = 208 - case makeFileAlias = 209 - case downloadFolder = 210 - case downloadInfo = 211 // Server - case downloadBanner = 212 - case uploadFolder = 213 - case getUserNameList = 300 - case notifyOfUserChange = 301 // Server - case notifyOfUserDelete = 302 // Server - case getClientInfoText = 303 - case setClientUserInfo = 304 - case newUser = 350 - case deleteUser = 351 - case getUser = 352 - case setUser = 353 - case userAccess = 354 // Server - case userBroadcast = 355 // Client & Server - case getNewsCategoryNameList = 370 - case getNewsArticleNameList = 371 - case deleteNewsItem = 380 - case newNewsFolder = 381 - case newNewsCategory = 382 - case getNewsArticleData = 400 - case postNewsArticle = 410 - case deleteNewsArticle = 411 - case connectionKeepAlive = 500 -} - |