aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Network/HotlineProtocol.swift
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2023-11-29 20:13:23 -0800
committerDustin Mierau <dustin@mierau.me>2023-11-29 20:13:23 -0800
commit30255476781789df318a571622ab732681696139 (patch)
tree7e05705c09c1a455ca6573af686f9eb0629df894 /Hotline/Network/HotlineProtocol.swift
parent2bdcc0c3d751d7c7626d8b0e9f79331b803c668b (diff)
Further work on protocol.
Diffstat (limited to 'Hotline/Network/HotlineProtocol.swift')
-rw-r--r--Hotline/Network/HotlineProtocol.swift212
1 files changed, 178 insertions, 34 deletions
diff --git a/Hotline/Network/HotlineProtocol.swift b/Hotline/Network/HotlineProtocol.swift
index 787675e..d5fe734 100644
--- a/Hotline/Network/HotlineProtocol.swift
+++ b/Hotline/Network/HotlineProtocol.swift
@@ -17,25 +17,145 @@ struct HotlineServer: Identifiable, Hashable {
}
}
-struct HotlineTransactionParameter {
+struct HotlineUser: Identifiable, Hashable {
let id: UInt16
+ let userName: String
+
+ static func == (lhs: HotlineUser, rhs: HotlineUser) -> Bool {
+ return lhs.id == rhs.id
+ }
+
+ func hash(into hasher: inout Hasher) {
+ hasher.combine(self.id)
+ }
+}
+
+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 HotlineTransactionParameter {
+ let type: HotlineTransactionFieldType
let dataSize: UInt16
let data: Data
+
+ func getUInt8() -> UInt8? {
+ return data.readUInt8(at: 0)
+ }
+
+ func getUInt16() -> UInt16? {
+ return data.readUInt16(at: 0)
+ }
+
+ func getUInt32() -> UInt32? {
+ return data.readUInt32(at: 0)
+ }
+
+ 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
+ }
- let flags: UInt8
- let isReply: UInt8
- let type: HotlineTransactionType
- let id: UInt32
- let errorCode: UInt32
- let totalSize: UInt32
- let dataSize: UInt32
+ 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 parameterCount: UInt16 = 0
- var parameters: [HotlineTransactionParameter]? = nil
+ var parameters: [HotlineTransactionParameter] = []
+
+ 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 setParameterUInt8(type: HotlineTransactionFieldType, val: UInt8) {
+ self.parameters.append(HotlineTransactionParameter(type: type, dataSize: UInt16(MemoryLayout<UInt8>.size), data: Data(val)))
+// self.parameters[type] = HotlineTransactionParameter(dataSize: UInt16(MemoryLayout<UInt8>.size), data: Data(val))
+ }
+
+ mutating func setParameterUInt16(type: HotlineTransactionFieldType, val: UInt16) {
+ self.parameters.append(HotlineTransactionParameter(type: type, dataSize: UInt16(MemoryLayout<UInt16>.size), data: Data(val)))
+// self.parameters[type] = HotlineTransactionParameter(dataSize: UInt16(MemoryLayout<UInt16>.size), data: Data(val))
+ }
+
+ mutating func setParameterUInt32(type: HotlineTransactionFieldType, val: UInt32) {
+ self.parameters.append(HotlineTransactionParameter(type: type, dataSize: UInt16(MemoryLayout<UInt32>.size), data: Data(val)))
+// self.parameters[type] = HotlineTransactionParameter(type: type, dataSize: UInt16(MemoryLayout<UInt32>.size), data: Data(val))
+ }
+
+ mutating func setParameterEncodedString(type: HotlineTransactionFieldType, val: String) {
+ let encodedVal = String(val.utf8.map { char in
+ Character(UnicodeScalar(0xFF - char))
+ })
+
+ self.setParameterString(type: type, val: encodedVal)
+ }
+
+ mutating func setParameterString(type: HotlineTransactionFieldType, val: String) {
+ var stringData = Data()
+// stringData.appendUInt16(UInt16(val.count))
+ stringData.append(contentsOf: val.utf8)
+
+ self.parameters.append(HotlineTransactionParameter(type: type, dataSize: UInt16(stringData.count), data: stringData))
+// self.parameters[type] = HotlineTransactionParameter(dataSize: UInt16(stringData.count), data: stringData)
+ }
+
+ func getParameter(type: HotlineTransactionFieldType) -> HotlineTransactionParameter? {
+ return self.parameters.first { p in
+ p.type == type
+ }
+
+// return self.parameters[type]
+ }
+
+ func getParameterList(type: HotlineTransactionFieldType) -> [HotlineTransactionParameter] {
+ return self.parameters.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)
@@ -43,45 +163,68 @@ struct HotlineTransaction {
data.appendUInt16(self.type.rawValue)
data.appendUInt32(self.id)
data.appendUInt32(self.errorCode)
- data.appendUInt32(self.totalSize)
- data.appendUInt32(self.dataSize)
- if let p = self.parameters, p.count > 0 {
- data.appendUInt16(UInt16(p.count))
- for param in p {
- data.appendUInt16(param.id)
- data.appendUInt16(param.dataSize)
- data.append(param.data)
+ if self.parameters.count > 0 {
+ var parameterData = Data()
+ parameterData.appendUInt16(UInt16(self.parameters.count))
+ for param in self.parameters {
+ parameterData.appendUInt16(param.type.rawValue)
+ parameterData.appendUInt16(param.dataSize)
+ parameterData.append(param.data)
}
+
+ data.appendUInt32(UInt32(parameterData.count))
+ data.appendUInt32(UInt32(parameterData.count))
+ data.append(parameterData)
+ }
+ else {
+ data.appendUInt32(0)
+ data.appendUInt32(0)
}
}
}
+enum HotlineTransactionFieldType: UInt16 {
+ case userName = 102 // String
+ case userLogin = 105 // Encoded string
+ case userPassword = 106 // Encoded string
+ case userIconID = 104 // Integer
+ case userID = 103 // Integer
+ case data = 101 // String
+ case userAccess = 110 // 64-bit integer??
+ case userFlags = 112
+ case options = 113 // 32-bit integer?
+ case versionNumber = 160 // Integer
+ case bannerID = 161
+ case serverName = 162
+ case userInfo = 300
+}
+
enum HotlineTransactionType: UInt16 {
- case unknown = 0
+ case reply = 0
case error = 100
case getMessages = 101
- case newMessage = 102
+ case newMessage = 102 // Server
case oldPostNews = 103
- case serverMessage = 104
+ case serverMessage = 104 // Server
case sendChat = 105
- case chatMessage = 106
+ case chatMessage = 106 // Server
case login = 107
case sendInstantMessage = 108
- case showAgreement = 109
+ case showAgreement = 109 // Server
case disconnectUser = 110
- case disconnectMessage = 111
+ case disconnectMessage = 111 // Server
case inviteToNewChat = 112
- case inviteToChat = 113
+ case inviteToChat = 113 // Server
case rejectChatInvite = 114
case joinChat = 115
case leaveChat = 116
- case notifyChatOfUserChange = 117
- case notifyChatOfUserDelete = 118
- case notifyChatSubject = 119
+ case notifyChatOfUserChange = 117 // Server
+ case notifyChatOfUserDelete = 118 // Server
+ case notifyChatSubject = 119 // Server
case setChatSubject = 120
case agreed = 121
- case serverBanner = 122
+ case serverBanner = 122 // Server
case getFileNameList = 200
case downloadFile = 202
case uploadFile = 203
@@ -92,20 +235,20 @@ enum HotlineTransactionType: UInt16 {
case moveFile = 208
case makeFileAlias = 209
case downloadFolder = 210
- case downloadInfo = 211
+ case downloadInfo = 211 // Server
case downloadBanner = 212
case uploadFolder = 213
case getUserNameList = 300
- case notifyOfUserChange = 301
- case notifyOfUserDelete = 302
+ 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
- case userBroadcast = 355
+ case userAccess = 354 // Server
+ case userBroadcast = 355 // Client & Server
case getNewsCategoryNameList = 370
case getNewsArticleNameList = 371
case deleteNewsItem = 380
@@ -114,5 +257,6 @@ enum HotlineTransactionType: UInt16 {
case getNewsArticleData = 400
case postNewsArticle = 410
case deleteNewsArticle = 411
+ case connectionKeepAlive = 500
}