WebAbstractMessage.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2018-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. //
  21. // WebAbstractMessage.swift
  22. // Threema
  23. //
  24. // Copyright © 2018 Threema GmbH. All rights reserved.
  25. //
  26. import Foundation
  27. import SwiftMsgPack
  28. import CocoaLumberjackSwift
  29. public class WebAbstractMessage: NSObject {
  30. var messageType: String
  31. var messageSubType: String?
  32. var requestId: String?
  33. var ack: WebAbstractMessageAcknowledgement?
  34. var args: [AnyHashable:Any?]?
  35. var data: Any?
  36. public init(messageType: String, messageSubType: String?, requestId: String?, ack: WebAbstractMessageAcknowledgement?, args: [AnyHashable:Any?]?, data: Any?) {
  37. self.messageType = messageType
  38. self.messageSubType = messageSubType
  39. self.requestId = requestId
  40. self.ack = ack
  41. self.args = args
  42. self.data = data
  43. }
  44. public init(dictionary: [AnyHashable: Any?]) {
  45. messageType = dictionary["type"] as! String
  46. messageSubType = dictionary["subType"] as? String
  47. requestId = dictionary["id"] as? String
  48. if let tmpId = dictionary["ack"] as? [AnyHashable: Any?] {
  49. ack = WebAbstractMessageAcknowledgement.init(object: tmpId)
  50. }
  51. args = dictionary["args"] as? [AnyHashable:Any?]
  52. if dictionary["id"] != nil {
  53. requestId = dictionary["id"] as? String
  54. }
  55. if dictionary["data"] != nil {
  56. data = dictionary["data"]! as Any?
  57. }
  58. }
  59. public init(message: WebAbstractMessage) {
  60. messageType = message.messageType
  61. messageSubType = message.messageSubType
  62. requestId = message.requestId
  63. ack = message.ack
  64. args = message.args
  65. data = message.data
  66. }
  67. public func addIds(message: WebAbstractMessage) {
  68. requestId = message.requestId
  69. ack = message.ack
  70. }
  71. public func messagePack() -> Data
  72. {
  73. var msgData = Data()
  74. do {
  75. var dict: [AnyHashable: Any?] = ["type": messageType, "subType": messageSubType ?? ""]
  76. if requestId != nil {
  77. dict.updateValue(requestId, forKey: "id")
  78. }
  79. if ack != nil {
  80. dict.updateValue(ack?.objectDict(), forKey: "ack")
  81. }
  82. if args != nil {
  83. dict.updateValue(args, forKey: "args")
  84. }
  85. if data != nil {
  86. dict.updateValue(data, forKey: "data")
  87. }
  88. try msgData.pack(dict)
  89. return msgData
  90. } catch {
  91. print("Something went wrong while packing data: \(error)")
  92. return msgData
  93. }
  94. }
  95. func getResponseMsgpack(session: WCSession, completionHandler: @escaping (_ msgPack: Data?, _ blacklisted: Bool) -> Void) {
  96. if session.connectionStatus() != .ready && messageType != "update" && messageSubType != "connectionInfo" {
  97. if messageSubType != nil {
  98. ValidationLogger.shared()?.logString("Threema Web: Message received in invalid state: \(session.connectionStatus()?.rawValue ?? 0) \(messageType) \(messageSubType!)")
  99. completionHandler(nil, true)
  100. } else {
  101. ValidationLogger.shared()?.logString("Threema Web: Message received in invalid state: \(session.connectionStatus()?.rawValue ?? 0) \(messageType)")
  102. completionHandler(nil, true)
  103. }
  104. return;
  105. }
  106. if messageType == "request" {
  107. switch messageSubType {
  108. case "clientInfo"?:
  109. let requestClientInfo = WebClientInfoRequest.init(message: self)
  110. let browserName = requestClientInfo.browserName ?? "Unknown"
  111. let broserVersion = requestClientInfo.browserVersion != nil ? NSNumber.init(value: requestClientInfo.browserVersion!) : NSNumber.init(value: 0)
  112. if session.webClientSession != nil {
  113. WebClientSessionStore.shared.updateWebClientSession(session: session.webClientSession!, browserName: browserName, browserVersion: broserVersion)
  114. }
  115. let responseClientInfo = WebClientInfoResponse.init(requestId: self.requestId)
  116. DDLogVerbose("Threema Web: MessagePack -> Send response/clientInfo")
  117. completionHandler(responseClientInfo.messagePack(), false)
  118. return
  119. case "profile"?:
  120. let responseProfile = WebProfileResponse.init(requestId: self.requestId)
  121. DDLogVerbose("Threema Web: MessagePack -> Send response/profile")
  122. completionHandler(responseProfile.messagePack(), false)
  123. return
  124. case "receivers"?:
  125. self.buildResponseReceivers { (responseReceivers) in
  126. DDLogVerbose("Threema Web: MessagePack -> Send response/receivers")
  127. completionHandler(responseReceivers!.messagePack(), false)
  128. return
  129. }
  130. break
  131. case "conversations"?:
  132. let requestConversations = WebConversationsRequest.init(message: self)
  133. let responseConversations = WebConversationsResponse.init(requestId: self.requestId, conversationRequest: requestConversations, session: session)
  134. DDLogVerbose("Threema Web: MessagePack -> Send response/conversations")
  135. completionHandler(responseConversations.messagePack(), false)
  136. return
  137. case "batteryStatus"?:
  138. let responseBatteryStatus = WebBatteryStatusUpdate.init(self.requestId)
  139. DDLogVerbose("Threema Web: MessagePack -> Send update/batteryStatus")
  140. completionHandler(responseBatteryStatus.messagePack(), true)
  141. return
  142. case "messages"?:
  143. let requestMessages = WebMessagesRequest.init(message: self)
  144. let responseMessages = WebMessagesResponse.init(requestMessage: requestMessages, session: session)
  145. session.addRequestedConversation(conversationId: responseMessages.identity)
  146. DDLogVerbose("Threema Web: MessagePack -> Send response/messages")
  147. completionHandler(responseMessages.messagePack(), false)
  148. return
  149. case "avatar"?:
  150. let requestAvatar = WebAvatarRequest.init(message: self)
  151. let responseAvatar = WebAvatarResponse.init(request: requestAvatar)
  152. DDLogVerbose("Threema Web: MessagePack -> Send response/avatar")
  153. completionHandler(responseAvatar.messagePack(), false)
  154. return
  155. case "thumbnail"?:
  156. let requestThumbnail = WebThumbnailRequest.init(message: self)
  157. var baseMessage: BaseMessage? = nil
  158. let entityManager = EntityManager()
  159. baseMessage = entityManager.entityFetcher.message(withId: requestThumbnail.messageId)
  160. if baseMessage != nil {
  161. if baseMessage!.isKind(of: ImageMessage.self) {
  162. let imageMessage = baseMessage as! ImageMessage
  163. if imageMessage.image == nil && imageMessage.thumbnail == nil {
  164. let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "internalError")
  165. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  166. completionHandler(confirmResponse.messagePack(), false)
  167. return
  168. } else {
  169. if imageMessage.image != nil && imageMessage.image.data == nil {
  170. let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "internalError")
  171. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  172. completionHandler(confirmResponse.messagePack(), false)
  173. return
  174. }
  175. else if imageMessage.thumbnail != nil && imageMessage.thumbnail.data == nil {
  176. let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "internalError")
  177. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  178. completionHandler(confirmResponse.messagePack(), false)
  179. return
  180. }
  181. }
  182. let responseThumbnail = WebThumbnailResponse.init(request: requestThumbnail, imageMessage: baseMessage!)
  183. DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
  184. completionHandler(responseThumbnail.messagePack(), false)
  185. return
  186. }
  187. else if baseMessage!.isKind(of: VideoMessage.self) {
  188. let responseThumbnail = WebThumbnailResponse.init(request: requestThumbnail, videoMessage: baseMessage!)
  189. DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
  190. completionHandler(responseThumbnail.messagePack(), false)
  191. return
  192. }
  193. else if baseMessage!.isKind(of: FileMessage.self) {
  194. let responseThumbnail = WebThumbnailResponse.init(request: requestThumbnail, fileMessage: baseMessage!)
  195. DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
  196. completionHandler(responseThumbnail.messagePack(), false)
  197. return
  198. }
  199. } else {
  200. let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "invalidMessage")
  201. DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
  202. completionHandler(confirmResponse.messagePack(), false)
  203. return
  204. }
  205. break
  206. case "contactDetail"?:
  207. let requestContactDetail = WebContactDetailRequest.init(message: self)
  208. var contact: Contact? = nil
  209. let entityManager = EntityManager()
  210. contact = entityManager.entityFetcher.contact(forId: requestContactDetail.identity)
  211. let responseContactDetail = WebContactDetailResponse.init(contact: contact, contactDetailRequest: requestContactDetail)
  212. DDLogVerbose("Threema Web: MessagePack -> Send response/contactDetail")
  213. completionHandler(responseContactDetail.messagePack(), false)
  214. return
  215. case "read"?:
  216. let requestRead = WebReadRequest.init(message: self)
  217. updateReadStateForMessage(requestMessage: requestRead)
  218. let confirmResponse = WebConfirmResponse.init(webReadRequest: requestRead)
  219. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  220. completionHandler(confirmResponse.messagePack(), false)
  221. return
  222. case "ack"?:
  223. let requestAck = WebAckRequest.init(message: self)
  224. var baseMessage: BaseMessage? = nil
  225. var entityManager: EntityManager? = nil
  226. entityManager = EntityManager()
  227. baseMessage = entityManager?.entityFetcher.message(withId: requestAck.messageId)
  228. if baseMessage != nil {
  229. updateAckForMessage(entityManager: entityManager!, requestMessage: requestAck, baseMessage: baseMessage)
  230. let responseMessage = WebMessagesUpdate.init(self.requestId, baseMessage: baseMessage!, conversation: baseMessage!.conversation, objectMode: .modified, session: session)
  231. DDLogVerbose("Threema Web: MessagePack -> Send update/messages")
  232. completionHandler(responseMessage.messagePack(), false)
  233. return
  234. } else {
  235. let confirmResponse = WebConfirmResponse.init(message: requestAck, success: false, error: "invalidMessage")
  236. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  237. completionHandler(confirmResponse.messagePack(), false)
  238. return
  239. }
  240. case "blob"?:
  241. let requestBlob = WebBlobRequest.init(message: self)
  242. var baseMessage: BaseMessage? = nil
  243. let entityManager = EntityManager()
  244. baseMessage = entityManager.entityFetcher.message(withId: requestBlob.messageId)
  245. if baseMessage != nil {
  246. if baseMessage!.isKind(of: ImageMessage.self) {
  247. let responseMessage = WebBlobResponse.init(request: requestBlob, imageMessage: baseMessage! as! ImageMessage)
  248. responseMessage.addImage {
  249. DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
  250. completionHandler(responseMessage.messagePack(), false)
  251. return
  252. }
  253. }
  254. else if baseMessage!.isKind(of: VideoMessage.self) {
  255. let responseMessage = WebBlobResponse.init(request: requestBlob, videoMessage: baseMessage! as! VideoMessage)
  256. responseMessage.addVideo {
  257. DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
  258. completionHandler(responseMessage.messagePack(), false)
  259. return
  260. }
  261. }
  262. else if baseMessage!.isKind(of: AudioMessage.self) {
  263. let responseMessage = WebBlobResponse.init(request: requestBlob, audioMessage: baseMessage! as! AudioMessage)
  264. responseMessage.addAudio {
  265. DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
  266. completionHandler(responseMessage.messagePack(), false)
  267. return
  268. }
  269. }
  270. else if baseMessage!.isKind(of: FileMessage.self) {
  271. let responseMessage = WebBlobResponse.init(request: requestBlob, fileMessage: baseMessage! as! FileMessage)
  272. responseMessage.addFile {
  273. DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
  274. completionHandler(responseMessage.messagePack(), false)
  275. return
  276. }
  277. } else {
  278. let confirmResponse = WebConfirmResponse.init(message: requestBlob, success: false, error: "internalError")
  279. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  280. completionHandler(confirmResponse.messagePack(), false)
  281. return
  282. }
  283. } else {
  284. let confirmResponse = WebConfirmResponse.init(message: requestBlob, success: false, error: "invalidMessage")
  285. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  286. completionHandler(confirmResponse.messagePack(), false)
  287. return
  288. }
  289. break
  290. case "groupSync"?:
  291. let requestGroupSync = WebGroupSyncRequest.init(message: self)
  292. requestGroupSync.syncGroup()
  293. let responseConfirmAction = WebConfirmResponse.init(webGroupSyncRequest: requestGroupSync)
  294. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  295. completionHandler(responseConfirmAction.messagePack(), false)
  296. return
  297. case "connectionAck"?:
  298. if session.connectionContext() != nil {
  299. _ = WebConnectionAckRequest.init(message: self)
  300. let responseConnectionAck = WebConnectionAckUpdateResponse.init(requestId: self.requestId, incomingSequenceNumber: session.connectionContext()!.incomingSequenceNumber)
  301. session.connectionContext()!.runTimer()
  302. DDLogVerbose("Threema Web: MessagePack -> Send update/connectionAck")
  303. completionHandler(responseConnectionAck.messagePack(), true)
  304. } else {
  305. completionHandler(nil, true)
  306. }
  307. return
  308. default:
  309. let confirmResponse = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
  310. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  311. completionHandler(confirmResponse.messagePack(), false)
  312. return
  313. }
  314. }
  315. else if messageType == "create" {
  316. switch messageSubType {
  317. case "contact"?:
  318. let createContactRequest = WebCreateContactRequest.init(message: self)
  319. let createContactResponse = WebCreateContactResponse.init(request: createContactRequest)
  320. createContactResponse.addContact {
  321. DDLogVerbose("Threema Web: MessagePack -> Sendcreate/contact")
  322. completionHandler(createContactResponse.messagePack(), false)
  323. return
  324. }
  325. break
  326. case "group"?:
  327. let createGroupRequest = WebCreateGroupRequest.init(message: self)
  328. let createGroupResponse = WebCreateGroupResponse.init(request: createGroupRequest)
  329. createGroupResponse.addGroup {
  330. DDLogVerbose("Threema Web: MessagePack -> Sendcreate/group")
  331. completionHandler(createGroupResponse.messagePack(), false)
  332. return
  333. }
  334. break
  335. case "textMessage"?:
  336. let createTextMessageRequest = WebCreateTextMessageRequest.init(message: self)
  337. session.addRequestCreateMessage(requestId: createTextMessageRequest.requestId!, abstractMessage: createTextMessageRequest)
  338. let alertArray: [String] = createTextMessageRequest.text.components(separatedBy: " ")
  339. createTextMessageRequest.sendMessage {
  340. if createTextMessageRequest.id != nil && createTextMessageRequest.id == "ECHOECHO" && alertArray.count == 3 && alertArray[0].lowercased() == "alert" && ( alertArray[1].lowercased() == "error" || alertArray[1].lowercased() == "warning" || alertArray[1].lowercased() == "info") {
  341. let webAlertUpdate = WebAlertUpdate.init(source: .device, type: WebAlertUpdate.TypeObj(rawValue: alertArray[1].lowercased())!, message:alertArray[2])
  342. DDLogVerbose("Threema Web: MessagePack -> Send update/alert")
  343. session.sendMessageToWeb(blacklisted: false, msgpack: webAlertUpdate.messagePack())
  344. }
  345. if createTextMessageRequest.tmpError != nil {
  346. // send error
  347. let createTextMessageResponse = WebCreateTextMessageResponse(request: createTextMessageRequest)
  348. let baseMessageId = createTextMessageRequest.baseMessage?.id.hexEncodedString() ?? ""
  349. let backgroundKey = kAppAckBackgroundTask + baseMessageId
  350. BackgroundTaskManager.shared.newBackgroundTask(key: backgroundKey, timeout: Int(kAppAckBackgroundTaskTime)) {
  351. DDLogVerbose("Threema Web: MessagePack -> Send create/textMessage")
  352. session.sendMessageToWeb(blacklisted: false, msgpack: createTextMessageResponse.messagePack())
  353. }
  354. session.webClientProcessQueue.async {
  355. completionHandler(nil, true)
  356. }
  357. return
  358. } else {
  359. // background task to send ack to server
  360. var id: String?
  361. if let groupId = createTextMessageRequest.groupId {
  362. id = groupId.hexEncodedString()
  363. } else {
  364. id = createTextMessageRequest.id!
  365. }
  366. let backgroundKey = kAppAckBackgroundTask + id!
  367. BackgroundTaskManager.shared.newBackgroundTask(key: backgroundKey, timeout: Int(kAppAckBackgroundTaskTime)) {
  368. session.webClientProcessQueue.async {
  369. completionHandler(nil, true)
  370. }
  371. return
  372. }
  373. }
  374. }
  375. break
  376. case "fileMessage"?:
  377. let createFileMessageRequest = WebCreateFileMessageRequest.init(message: self, session: session)
  378. session.addRequestCreateMessage(requestId: createFileMessageRequest.requestId!, abstractMessage: createFileMessageRequest)
  379. createFileMessageRequest.sendMessage {
  380. completionHandler(nil, true)
  381. return
  382. }
  383. break
  384. default:
  385. let confirmResponse = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
  386. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  387. completionHandler(confirmResponse.messagePack(), false)
  388. return
  389. }
  390. }
  391. else if messageType == "update" {
  392. switch messageSubType {
  393. case "connectionInfo"?:
  394. let updateRequestConnectionInfo = WebUpdateConnectionInfoRequest.init(message: self)
  395. session.connectionContext()?.connectionInfoRequest = updateRequestConnectionInfo
  396. if session.connectionStatus() == .connectionInfoSend {
  397. ValidationLogger.shared()?.logString("Threema Web: ConnectionInfo received maybeResume --> state: \(session.connectionStatus()!.rawValue)")
  398. session.setWCConnectionStateToReady()
  399. updateRequestConnectionInfo.maybeResume(session: session)
  400. session.messageQueue.processQueue()
  401. } else {
  402. ValidationLogger.shared()?.logString("Threema Web: ConnectionInfo received and wait for ConnectionInfoResponse --> state: \(session.connectionStatus()!.rawValue)")
  403. session.setWCConnectionStateToConnectionInfoReceived()
  404. }
  405. completionHandler(nil, true)
  406. return
  407. case "contact"?:
  408. let updateRequestContact = WebUpdateContactRequest.init(message: self)
  409. updateRequestContact.updateContact()
  410. let updateResponseContact = WebUpdateContactResponse.init(request: updateRequestContact)
  411. DDLogVerbose("Threema Web: MessagePack -> Send update/contact")
  412. completionHandler(updateResponseContact.messagePack(), false)
  413. return
  414. case "profile"?:
  415. let updateRequestProfile = WebUpdateProfileRequest.init(message: self)
  416. updateRequestProfile.updateProfile()
  417. let confirmActionResponse = WebConfirmResponse.init(webUpdateProfileRequest: updateRequestProfile)
  418. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  419. completionHandler(confirmActionResponse.messagePack(), false)
  420. return
  421. case "group"?:
  422. let updateRequestGroup = WebUpdateGroupRequest.init(message: self)
  423. updateRequestGroup.updateGroup {
  424. let updateResponseGroup = WebUpdateGroupResponse.init(groupRequest: updateRequestGroup)
  425. DDLogVerbose("Threema Web: MessagePack -> Send update/group")
  426. completionHandler(updateResponseGroup.messagePack(), false)
  427. return
  428. }
  429. break
  430. case "typing"?:
  431. let updateTyping = WebTypingUpdate.init(message: self)
  432. updateTyping.sendTypingToContact()
  433. completionHandler(nil, true)
  434. return
  435. case "conversation"?:
  436. let requestUpdateConversation = WebUpdateConversationRequest.init(message: self)
  437. requestUpdateConversation.updateConversation()
  438. let responseConfirmAction = WebConfirmResponse.init(webUpdateConversationRequest: requestUpdateConversation)
  439. DDLogVerbose("Threema Web: MessagePack -> Send response/confirmAction")
  440. completionHandler(responseConfirmAction.messagePack(), false)
  441. return
  442. case "connectionDisconnect"?:
  443. let requestUpdateConnectionDisconnect = WebUpdateConnectionDisconnectRequest.init(message: self)
  444. requestUpdateConnectionDisconnect.disconnect(session: session)
  445. completionHandler(nil, true)
  446. return
  447. case "connectionAck"?:
  448. let requestUpdateConnectionAck = WebConnectionAckUpdateRequest.init(message: self)
  449. do {
  450. // should be previousContext
  451. if let context = session.connectionContext() {
  452. if let sequenceNumber = requestUpdateConnectionAck.sequenceNumber {
  453. try context.prune(theirSequenceNumber: sequenceNumber)
  454. } else {
  455. ValidationLogger.shared().logString("Threema Web: Could not prune cache: missing sequenceNumber.")
  456. session.stop(close: true, forget: false, sendDisconnect: true, reason: .error)
  457. return
  458. }
  459. } else {
  460. ValidationLogger.shared().logString("Threema Web: Could not prune cache: missing context.")
  461. session.stop(close: true, forget: false, sendDisconnect: true, reason: .error)
  462. return
  463. }
  464. }
  465. catch {
  466. // do error stuff
  467. ValidationLogger.shared().logString("Threema Web: Could not prune cache: \(error).")
  468. session.stop(close: true, forget: false, sendDisconnect: true, reason: .error)
  469. return
  470. }
  471. return
  472. default:
  473. let responseConfirmAction = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
  474. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  475. completionHandler(responseConfirmAction.messagePack(), false)
  476. return
  477. }
  478. }
  479. else if messageType == "delete" {
  480. switch messageSubType {
  481. case "cleanReceiverConversation"?:
  482. let cleanReceiverConversationRequest = WebCleanReceiverConversationRequest.init(message: self)
  483. cleanReceiverConversationRequest.clean()
  484. let confirmActionResponse = WebConfirmResponse.init(webCleanReceiverConversationRequest: cleanReceiverConversationRequest)
  485. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  486. completionHandler(confirmActionResponse.messagePack(), false)
  487. return
  488. case "message"?:
  489. let deleteMessageRequest = WebDeleteMessageRequest.init(message: self)
  490. deleteMessageRequest.delete()
  491. let confirmActionResponse = WebConfirmResponse.init(webDeleteMessageRequest: deleteMessageRequest)
  492. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  493. completionHandler(confirmActionResponse.messagePack(), false)
  494. return
  495. case "group"?:
  496. let deleteGroupRequest = WebDeleteGroupRequest.init(message: self)
  497. deleteGroupRequest.deleteOrLeave()
  498. let confirmActionResponse = WebConfirmResponse.init(webDeleteGroupRequest: deleteGroupRequest)
  499. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  500. completionHandler(confirmActionResponse.messagePack(), false)
  501. return
  502. default:
  503. let responseConfirmAction = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
  504. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  505. completionHandler(responseConfirmAction.messagePack(), false)
  506. return
  507. }
  508. } else {
  509. let responseConfirmAction = WebConfirmResponse.init(message: self, success: false, error: "unknownType")
  510. DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
  511. completionHandler(responseConfirmAction.messagePack(), false)
  512. return
  513. }
  514. }
  515. private func updateReadStateForMessage(requestMessage: WebReadRequest) {
  516. let entityManager = EntityManager()
  517. var conversation:Conversation? = nil
  518. let baseMessage = entityManager.entityFetcher.message(withId: requestMessage.messageId)
  519. if baseMessage != nil {
  520. if baseMessage?.conversation.groupId != nil {
  521. conversation = entityManager.entityFetcher.conversation(forGroupId: baseMessage?.conversation.groupId)
  522. } else {
  523. conversation = entityManager.entityFetcher.conversation(forIdentity: baseMessage?.conversation.contact.identity)
  524. }
  525. if conversation != nil {
  526. let messageFetcher = MessageFetcher.init(for: conversation, with: entityManager.entityFetcher)
  527. var foundMessageId = false
  528. var readReceiptQueue: [BaseMessage] = []
  529. var readNotificationsKeys: [String] = []
  530. let unreadMessages: [BaseMessage] = messageFetcher!.unreadMessages() as! [BaseMessage]
  531. for case let message in unreadMessages {
  532. if message.id == requestMessage.messageId || foundMessageId {
  533. readReceiptQueue.append(message)
  534. let conversation = message.conversation
  535. if (conversation!.isGroup()) {
  536. let key = message.sender.identity + message.id.hexEncodedString()
  537. readNotificationsKeys.append(key)
  538. } else {
  539. let key = message.conversation.contact.identity + message.id.hexEncodedString()
  540. readNotificationsKeys.append(key)
  541. }
  542. foundMessageId = true
  543. }
  544. }
  545. if readReceiptQueue.count > 0 {
  546. if !baseMessage!.conversation.isGroup() {
  547. MessageSender.sendReadReceipt(forMessages: readReceiptQueue, toIdentity: baseMessage!.conversation.contact.identity, async: true, quickReply: false)
  548. }
  549. // DispatchQueue.main.sync {
  550. entityManager.performSyncBlockAndSafe({
  551. for message in readReceiptQueue {
  552. message.read = NSNumber(value: true)
  553. message.readDate = Date()
  554. let unreadCount: Int = unreadMessages.count - readReceiptQueue.count
  555. conversation!.unreadMessageCount = NSNumber.init(value: unreadCount)
  556. }
  557. })
  558. let center = UNUserNotificationCenter.current()
  559. center.removeDeliveredNotifications(withIdentifiers: readNotificationsKeys)
  560. // }
  561. } else {
  562. requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, false, "alreadyRead")
  563. }
  564. requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, true, nil)
  565. } else {
  566. requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, false, "invalidMessage")
  567. }
  568. } else {
  569. requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, false, "invalidMessage")
  570. }
  571. DispatchQueue.main.async {
  572. NotificationManager.sharedInstance().updateUnreadMessagesCount(false)
  573. }
  574. }
  575. private func updateAckForMessage(entityManager: EntityManager, requestMessage: WebAckRequest, baseMessage: BaseMessage!) {
  576. if baseMessage != nil {
  577. if baseMessage.userackDate != nil && baseMessage.userack.boolValue == requestMessage.acknowledged {
  578. return
  579. }
  580. entityManager.performSyncBlockAndSafe({
  581. if requestMessage.acknowledged {
  582. MessageSender.sendUserAck(forMessages: [baseMessage], toIdentity: requestMessage.id, async: true, quickReply: false)
  583. baseMessage.userack = NSNumber(value: true)
  584. } else {
  585. MessageSender.sendUserDecline(forMessages: [baseMessage], toIdentity: requestMessage.id, async: true, quickReply: false)
  586. baseMessage.userack = NSNumber(value: false)
  587. }
  588. baseMessage.userackDate = Date()
  589. })
  590. }
  591. }
  592. private func buildResponseReceivers(completion: @escaping (_ webResponseReceivers: WebReceiversResponse?) -> Void) {
  593. var contactResult: [Contact]?
  594. var allGroupConversations: [Conversation]?
  595. var responseReceivers: WebReceiversResponse? = nil
  596. let entityManager = EntityManager()
  597. contactResult = entityManager.entityFetcher.allContacts() as? [Contact]
  598. allGroupConversations = (entityManager.entityFetcher.allGroupConversations() as? [Conversation])!
  599. FeatureMask.updateMask(forAllContacts: contactResult) {
  600. responseReceivers = WebReceiversResponse.init(requestId: self.requestId, allContacts: contactResult!, allGroupConversations: allGroupConversations!)
  601. completion(responseReceivers)
  602. }
  603. }
  604. }
  605. public struct WebAbstractMessageAcknowledgement {
  606. var id: String?
  607. var success: Bool
  608. var error: String?
  609. init(_ id: String?, _ success: Bool, _ error: String?) {
  610. self.id = id
  611. self.success = success
  612. self.error = error
  613. }
  614. init(object: [AnyHashable: Any?]) {
  615. id = object["id"] as? String
  616. success = object["success"] as! Bool
  617. error = object["error"] as? String
  618. }
  619. func objectDict() -> [String: Any] {
  620. var valuesDict = [String: Any]()
  621. if id != nil {
  622. valuesDict.updateValue(id!, forKey: "id")
  623. }
  624. valuesDict.updateValue(success, forKey: "success")
  625. if error != nil {
  626. valuesDict.updateValue(error!, forKey: "error")
  627. }
  628. return valuesDict
  629. }
  630. }