WCSession.swift 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2019-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import Foundation
  21. import CocoaLumberjackSwift
  22. @objc public class WCSession: NSObject, NSCoding {
  23. internal var privateKey: Data?
  24. internal var webClientSession: WebClientSession?
  25. internal var messageQueue: WebMessageQueue
  26. private var connection: WCConnection?
  27. private var requestedConversations = [String]()
  28. private var requestedThumbnails = [Data]()
  29. private var lastLoadedMessageIndexes = [Data: Int]()
  30. private var requestCreateMessagesFromWeb = [String: WebAbstractMessage]()
  31. private(set) var webClientProcessQueue:DispatchQueue
  32. public init(webClientSession: WebClientSession) {
  33. self.webClientSession = webClientSession
  34. self.privateKey = webClientSession.privateKey
  35. var hash = webClientSession.initiatorPermanentPublicKeyHash
  36. if hash == nil {
  37. hash = WCSession.ccSha256(data: webClientSession.initiatorPermanentPublicKey!).hexEncodedString()
  38. WebClientSessionStore.shared.updateWebClientSession(session: webClientSession, hash: hash!)
  39. }
  40. webClientProcessQueue = DispatchQueue(label: "ch.threema.webClientProcessQueue", attributes: [])
  41. messageQueue = WebMessageQueue.init()
  42. super.init()
  43. messageQueue.delegate = self
  44. }
  45. // MARK: NSCoding
  46. required public init?(coder aDecoder: NSCoder) {
  47. // super.init(coder:) is optional, see notes below
  48. self.privateKey = aDecoder.decodeObject(forKey: "privateKey") as? Data
  49. self.connection = aDecoder.decodeObject(forKey: "connection") as? WCConnection
  50. let entityManager = EntityManager()
  51. if privateKey != nil {
  52. webClientSession = entityManager.entityFetcher.webClientSession(forPrivateKey: privateKey!)
  53. } else {
  54. webClientSession = entityManager.entityFetcher.activeWebClientSession()
  55. }
  56. self.messageQueue = aDecoder.decodeObject(forKey: "messageQueue") as! WebMessageQueue
  57. self.requestedConversations = aDecoder.decodeObject(forKey: "requestedConversations") as! [String]
  58. self.requestedThumbnails = aDecoder.decodeObject(forKey: "requestedThumbnails") as! [Data]
  59. self.lastLoadedMessageIndexes = aDecoder.decodeObject(forKey: "lastLoadedMessageIndexes") as! [Data: Int]
  60. self.requestedConversations = aDecoder.decodeObject(forKey: "requestedConversations") as! [String]
  61. self.webClientProcessQueue = DispatchQueue(label: "ch.threema.webClientProcessQueue", attributes: [])
  62. }
  63. public func encode(with aCoder: NSCoder) {
  64. // super.encodeWithCoder(aCoder) is optional, see notes below
  65. aCoder.encode(privateKey, forKey: "privateKey")
  66. aCoder.encode(connection, forKey: "connection")
  67. aCoder.encode(messageQueue, forKey: "messageQueue")
  68. aCoder.encode(requestedConversations, forKey: "requestedConversations")
  69. aCoder.encode(requestedThumbnails, forKey: "requestedThumbnails")
  70. aCoder.encode(lastLoadedMessageIndexes, forKey: "lastLoadedMessageIndexes")
  71. }
  72. }
  73. extension WCSession {
  74. // MARK: class functions
  75. class func ccSha256(data: Data) -> Data {
  76. var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
  77. digest.withUnsafeMutableBytes { digestBuffer in
  78. data.withUnsafeBytes { buffer in
  79. let _ = CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), digestBuffer.bindMemory(to: UInt8.self).baseAddress)
  80. }
  81. }
  82. return digest
  83. }
  84. }
  85. extension WCSession {
  86. // MARK: public functions
  87. public func connect(authToken: Data?) {
  88. let newConnection = WCConnection.init(delegate: self)
  89. if connection != nil {
  90. if connection!.context != nil {
  91. newConnection.context = connection!.context!.copy() as? WebConnectionContext
  92. }
  93. let oldConnection = connection
  94. ValidationLogger.shared().logString("Threema Web: Close old session")
  95. oldConnection?.close(close: false, forget: false, sendDisconnect: false, reason: .replace)
  96. }
  97. connection = newConnection
  98. connection?.connect(authToken: authToken)
  99. }
  100. public func sendMessage() {
  101. }
  102. public func receive() {
  103. }
  104. public func stop(close: Bool, forget: Bool, sendDisconnect: Bool, reason: WCConnection.WCConnectionStopReason) {
  105. connection?.close(close: close, forget: forget, sendDisconnect: sendDisconnect, reason: reason)
  106. }
  107. internal func setWCConnectionStateToReady() {
  108. connection?.setWCConnectionStateToReady()
  109. }
  110. internal func setWCConnectionStateToConnectionInfoReceived() {
  111. connection?.setWCConnectionStateToConnectionInfoReceived()
  112. }
  113. internal func connectionContext() -> WebConnectionContext? {
  114. return connection?.context
  115. }
  116. internal func connectionInfoResponse() -> WebUpdateConnectionInfoResponse? {
  117. return connection?.connectionInfoResponse
  118. }
  119. internal func sendChunk(chunk: [UInt8], msgpack: Data?, connectionInfo: Bool) {
  120. connection?.sendChunk(chunk: chunk, msgpack: msgpack, connectionInfo: connectionInfo)
  121. }
  122. internal func connectionWca() -> String? {
  123. return connection?.wca
  124. }
  125. internal func setWcaForConnection(wca: String) {
  126. connection?.wca = wca
  127. }
  128. internal func addRequestCreateMessage(requestId: String, abstractMessage: WebAbstractMessage) {
  129. requestCreateMessagesFromWeb[requestId] = abstractMessage
  130. }
  131. internal func removeRequestCreateMessage(requestId: String) {
  132. requestCreateMessagesFromWeb.removeValue(forKey: requestId)
  133. }
  134. internal func requestMessage(for requestId: String) -> WebAbstractMessage? {
  135. return requestCreateMessagesFromWeb[requestId]
  136. }
  137. }
  138. extension WCSession {
  139. // MARK: Requested lists
  140. public func requestedConversations(contains conversationId: String) -> Bool {
  141. return requestedConversations.contains(conversationId)
  142. }
  143. public func addRequestedConversation(conversationId: String) {
  144. if !requestedConversations(contains: conversationId) {
  145. requestedConversations.append(conversationId)
  146. }
  147. }
  148. public func requestedThumbnails(contains messageId: Data) -> Bool {
  149. return requestedThumbnails.contains(messageId)
  150. }
  151. public func addRequestedThumbnail(messageId: Data) {
  152. if !requestedThumbnails(contains: messageId) {
  153. requestedThumbnails.append(messageId)
  154. }
  155. }
  156. public func lastLoadedMessageIndexes(contains messageId: Data) -> Int? {
  157. return lastLoadedMessageIndexes[messageId]
  158. }
  159. public func addLastLoadedMessageIndex(messageId: Data, index: Int) {
  160. lastLoadedMessageIndexes[messageId] = index
  161. }
  162. public func clearAllRequestedLists() {
  163. requestedConversations.removeAll()
  164. requestedThumbnails.removeAll()
  165. lastLoadedMessageIndexes.removeAll()
  166. }
  167. }
  168. extension WCSession: MessageCompleteDelegate {
  169. func messageComplete(message: Data) {
  170. do {
  171. let object = try message.unpack() as! Dictionary<AnyHashable, Any?>
  172. let webMessage = WebAbstractMessage.init(dictionary: object)
  173. DDLogVerbose("Threema Web: MessagePack -> Received \(webMessage.messageType)/\(webMessage.messageSubType ?? "")")
  174. self.webClientProcessQueue.async {
  175. webMessage.getResponseMsgpack(session: self, completionHandler: { (responseMsgpack, blackListed) in
  176. if responseMsgpack != nil {
  177. self.messageQueue.enqueue(data: responseMsgpack, blackListed: blackListed)
  178. } else {
  179. print("ResponseMsgpack is nil")
  180. }
  181. })
  182. }
  183. } catch {
  184. print("Something went wrong while unpacking data: \(error)")
  185. }
  186. }
  187. }
  188. extension WCSession: WCConnectionDelegate {
  189. internal func currentWebClientSession() -> WebClientSession? {
  190. return webClientSession
  191. }
  192. internal func currentWCSession() -> WCSession {
  193. return self
  194. }
  195. internal func currentMessageQueue() -> WebMessageQueue {
  196. return messageQueue
  197. }
  198. }
  199. extension WCSession: WebMessageQueueDelegate {
  200. internal func sendMessageToWeb(blacklisted: Bool, msgpack: Data, _ connectionInfo: Bool = false) {
  201. self.connection?.sendMessageToWeb(blacklisted: blacklisted, msgpack: msgpack, connectionInfo)
  202. }
  203. internal func connectionStatus() -> WCConnectionState? {
  204. return connection?.connectionStatus
  205. }
  206. }