123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2018-2020 Threema GmbH
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License, version 3,
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- //
- // WebAbstractMessage.swift
- // Threema
- //
- // Copyright © 2018 Threema GmbH. All rights reserved.
- //
- import Foundation
- import SwiftMsgPack
- import CocoaLumberjackSwift
- public class WebAbstractMessage: NSObject {
-
- var messageType: String
- var messageSubType: String?
- var requestId: String?
- var ack: WebAbstractMessageAcknowledgement?
-
- var args: [AnyHashable:Any?]?
- var data: Any?
-
- public init(messageType: String, messageSubType: String?, requestId: String?, ack: WebAbstractMessageAcknowledgement?, args: [AnyHashable:Any?]?, data: Any?) {
- self.messageType = messageType
- self.messageSubType = messageSubType
- self.requestId = requestId
- self.ack = ack
- self.args = args
- self.data = data
- }
-
- public init(dictionary: [AnyHashable: Any?]) {
- messageType = dictionary["type"] as! String
- messageSubType = dictionary["subType"] as? String
- requestId = dictionary["id"] as? String
- if let tmpId = dictionary["ack"] as? [AnyHashable: Any?] {
- ack = WebAbstractMessageAcknowledgement.init(object: tmpId)
- }
- args = dictionary["args"] as? [AnyHashable:Any?]
- if dictionary["id"] != nil {
- requestId = dictionary["id"] as? String
- }
- if dictionary["data"] != nil {
- data = dictionary["data"]! as Any?
- }
- }
-
- public init(message: WebAbstractMessage) {
- messageType = message.messageType
- messageSubType = message.messageSubType
- requestId = message.requestId
- ack = message.ack
- args = message.args
- data = message.data
- }
-
- public func addIds(message: WebAbstractMessage) {
- requestId = message.requestId
- ack = message.ack
- }
-
- public func messagePack() -> Data
- {
- var msgData = Data()
- do {
- var dict: [AnyHashable: Any?] = ["type": messageType, "subType": messageSubType ?? ""]
- if requestId != nil {
- dict.updateValue(requestId, forKey: "id")
- }
- if ack != nil {
- dict.updateValue(ack?.objectDict(), forKey: "ack")
- }
- if args != nil {
- dict.updateValue(args, forKey: "args")
- }
- if data != nil {
- dict.updateValue(data, forKey: "data")
- }
- try msgData.pack(dict)
- return msgData
- } catch {
- print("Something went wrong while packing data: \(error)")
- return msgData
- }
- }
-
- func getResponseMsgpack(session: WCSession, completionHandler: @escaping (_ msgPack: Data?, _ blacklisted: Bool) -> Void) {
- if session.connectionStatus() != .ready && messageType != "update" && messageSubType != "connectionInfo" {
- if messageSubType != nil {
- ValidationLogger.shared()?.logString("Threema Web: Message received in invalid state: \(session.connectionStatus()?.rawValue ?? 0) \(messageType) \(messageSubType!)")
- completionHandler(nil, true)
- } else {
- ValidationLogger.shared()?.logString("Threema Web: Message received in invalid state: \(session.connectionStatus()?.rawValue ?? 0) \(messageType)")
- completionHandler(nil, true)
- }
- return;
- }
- if messageType == "request" {
- switch messageSubType {
- case "clientInfo"?:
- let requestClientInfo = WebClientInfoRequest.init(message: self)
- let browserName = requestClientInfo.browserName ?? "Unknown"
- let broserVersion = requestClientInfo.browserVersion != nil ? NSNumber.init(value: requestClientInfo.browserVersion!) : NSNumber.init(value: 0)
- if session.webClientSession != nil {
- WebClientSessionStore.shared.updateWebClientSession(session: session.webClientSession!, browserName: browserName, browserVersion: broserVersion)
- }
- let responseClientInfo = WebClientInfoResponse.init(requestId: self.requestId)
- DDLogVerbose("Threema Web: MessagePack -> Send response/clientInfo")
- completionHandler(responseClientInfo.messagePack(), false)
- return
- case "profile"?:
- let responseProfile = WebProfileResponse.init(requestId: self.requestId)
- DDLogVerbose("Threema Web: MessagePack -> Send response/profile")
- completionHandler(responseProfile.messagePack(), false)
- return
- case "receivers"?:
- self.buildResponseReceivers { (responseReceivers) in
- DDLogVerbose("Threema Web: MessagePack -> Send response/receivers")
- completionHandler(responseReceivers!.messagePack(), false)
- return
- }
- break
- case "conversations"?:
- let requestConversations = WebConversationsRequest.init(message: self)
- let responseConversations = WebConversationsResponse.init(requestId: self.requestId, conversationRequest: requestConversations, session: session)
- DDLogVerbose("Threema Web: MessagePack -> Send response/conversations")
- completionHandler(responseConversations.messagePack(), false)
- return
- case "batteryStatus"?:
- let responseBatteryStatus = WebBatteryStatusUpdate.init(self.requestId)
- DDLogVerbose("Threema Web: MessagePack -> Send update/batteryStatus")
- completionHandler(responseBatteryStatus.messagePack(), true)
- return
- case "messages"?:
- let requestMessages = WebMessagesRequest.init(message: self)
- let responseMessages = WebMessagesResponse.init(requestMessage: requestMessages, session: session)
- session.addRequestedConversation(conversationId: responseMessages.identity)
- DDLogVerbose("Threema Web: MessagePack -> Send response/messages")
- completionHandler(responseMessages.messagePack(), false)
- return
- case "avatar"?:
- let requestAvatar = WebAvatarRequest.init(message: self)
- let responseAvatar = WebAvatarResponse.init(request: requestAvatar)
- DDLogVerbose("Threema Web: MessagePack -> Send response/avatar")
- completionHandler(responseAvatar.messagePack(), false)
- return
- case "thumbnail"?:
- let requestThumbnail = WebThumbnailRequest.init(message: self)
- var baseMessage: BaseMessage? = nil
- let entityManager = EntityManager()
- baseMessage = entityManager.entityFetcher.message(withId: requestThumbnail.messageId)
- if baseMessage != nil {
- if baseMessage!.isKind(of: ImageMessage.self) {
- let imageMessage = baseMessage as! ImageMessage
- if imageMessage.image == nil && imageMessage.thumbnail == nil {
- let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "internalError")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- } else {
- if imageMessage.image != nil && imageMessage.image.data == nil {
- let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "internalError")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- else if imageMessage.thumbnail != nil && imageMessage.thumbnail.data == nil {
- let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "internalError")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- }
- let responseThumbnail = WebThumbnailResponse.init(request: requestThumbnail, imageMessage: baseMessage!)
- DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
- completionHandler(responseThumbnail.messagePack(), false)
- return
- }
- else if baseMessage!.isKind(of: VideoMessage.self) {
- let responseThumbnail = WebThumbnailResponse.init(request: requestThumbnail, videoMessage: baseMessage!)
- DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
- completionHandler(responseThumbnail.messagePack(), false)
- return
- }
- else if baseMessage!.isKind(of: FileMessage.self) {
- let responseThumbnail = WebThumbnailResponse.init(request: requestThumbnail, fileMessage: baseMessage!)
- DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
- completionHandler(responseThumbnail.messagePack(), false)
- return
- }
- } else {
- let confirmResponse = WebConfirmResponse.init(message: requestThumbnail, success: false, error: "invalidMessage")
- DDLogVerbose("Threema Web: MessagePack -> Send response/thumbnail")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- break
- case "contactDetail"?:
- let requestContactDetail = WebContactDetailRequest.init(message: self)
- var contact: Contact? = nil
- let entityManager = EntityManager()
- contact = entityManager.entityFetcher.contact(forId: requestContactDetail.identity)
- let responseContactDetail = WebContactDetailResponse.init(contact: contact, contactDetailRequest: requestContactDetail)
- DDLogVerbose("Threema Web: MessagePack -> Send response/contactDetail")
- completionHandler(responseContactDetail.messagePack(), false)
- return
- case "read"?:
- let requestRead = WebReadRequest.init(message: self)
- updateReadStateForMessage(requestMessage: requestRead)
- let confirmResponse = WebConfirmResponse.init(webReadRequest: requestRead)
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- case "ack"?:
- let requestAck = WebAckRequest.init(message: self)
- var baseMessage: BaseMessage? = nil
- var entityManager: EntityManager? = nil
- entityManager = EntityManager()
- baseMessage = entityManager?.entityFetcher.message(withId: requestAck.messageId)
- if baseMessage != nil {
- updateAckForMessage(entityManager: entityManager!, requestMessage: requestAck, baseMessage: baseMessage)
- let responseMessage = WebMessagesUpdate.init(self.requestId, baseMessage: baseMessage!, conversation: baseMessage!.conversation, objectMode: .modified, session: session)
- DDLogVerbose("Threema Web: MessagePack -> Send update/messages")
- completionHandler(responseMessage.messagePack(), false)
- return
- } else {
- let confirmResponse = WebConfirmResponse.init(message: requestAck, success: false, error: "invalidMessage")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- case "blob"?:
- let requestBlob = WebBlobRequest.init(message: self)
- var baseMessage: BaseMessage? = nil
- let entityManager = EntityManager()
- baseMessage = entityManager.entityFetcher.message(withId: requestBlob.messageId)
-
- if baseMessage != nil {
- if baseMessage!.isKind(of: ImageMessage.self) {
- let responseMessage = WebBlobResponse.init(request: requestBlob, imageMessage: baseMessage! as! ImageMessage)
- responseMessage.addImage {
- DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
- completionHandler(responseMessage.messagePack(), false)
- return
- }
- }
- else if baseMessage!.isKind(of: VideoMessage.self) {
- let responseMessage = WebBlobResponse.init(request: requestBlob, videoMessage: baseMessage! as! VideoMessage)
- responseMessage.addVideo {
- DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
- completionHandler(responseMessage.messagePack(), false)
- return
- }
- }
- else if baseMessage!.isKind(of: AudioMessage.self) {
- let responseMessage = WebBlobResponse.init(request: requestBlob, audioMessage: baseMessage! as! AudioMessage)
- responseMessage.addAudio {
- DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
- completionHandler(responseMessage.messagePack(), false)
- return
- }
- }
- else if baseMessage!.isKind(of: FileMessage.self) {
- let responseMessage = WebBlobResponse.init(request: requestBlob, fileMessage: baseMessage! as! FileMessage)
- responseMessage.addFile {
- DDLogVerbose("Threema Web: MessagePack -> Send response/blob")
- completionHandler(responseMessage.messagePack(), false)
- return
- }
- } else {
- let confirmResponse = WebConfirmResponse.init(message: requestBlob, success: false, error: "internalError")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- } else {
- let confirmResponse = WebConfirmResponse.init(message: requestBlob, success: false, error: "invalidMessage")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- break
- case "groupSync"?:
- let requestGroupSync = WebGroupSyncRequest.init(message: self)
- requestGroupSync.syncGroup()
- let responseConfirmAction = WebConfirmResponse.init(webGroupSyncRequest: requestGroupSync)
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(responseConfirmAction.messagePack(), false)
- return
- case "connectionAck"?:
- if session.connectionContext() != nil {
- _ = WebConnectionAckRequest.init(message: self)
- let responseConnectionAck = WebConnectionAckUpdateResponse.init(requestId: self.requestId, incomingSequenceNumber: session.connectionContext()!.incomingSequenceNumber)
- session.connectionContext()!.runTimer()
- DDLogVerbose("Threema Web: MessagePack -> Send update/connectionAck")
- completionHandler(responseConnectionAck.messagePack(), true)
- } else {
- completionHandler(nil, true)
- }
- return
- default:
- let confirmResponse = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- }
- else if messageType == "create" {
- switch messageSubType {
- case "contact"?:
- let createContactRequest = WebCreateContactRequest.init(message: self)
- let createContactResponse = WebCreateContactResponse.init(request: createContactRequest)
- createContactResponse.addContact {
- DDLogVerbose("Threema Web: MessagePack -> Sendcreate/contact")
- completionHandler(createContactResponse.messagePack(), false)
- return
- }
- break
- case "group"?:
- let createGroupRequest = WebCreateGroupRequest.init(message: self)
- let createGroupResponse = WebCreateGroupResponse.init(request: createGroupRequest)
- createGroupResponse.addGroup {
- DDLogVerbose("Threema Web: MessagePack -> Sendcreate/group")
- completionHandler(createGroupResponse.messagePack(), false)
- return
- }
- break
- case "textMessage"?:
- let createTextMessageRequest = WebCreateTextMessageRequest.init(message: self)
- session.addRequestCreateMessage(requestId: createTextMessageRequest.requestId!, abstractMessage: createTextMessageRequest)
- let alertArray: [String] = createTextMessageRequest.text.components(separatedBy: " ")
- createTextMessageRequest.sendMessage {
- 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") {
- let webAlertUpdate = WebAlertUpdate.init(source: .device, type: WebAlertUpdate.TypeObj(rawValue: alertArray[1].lowercased())!, message:alertArray[2])
- DDLogVerbose("Threema Web: MessagePack -> Send update/alert")
- session.sendMessageToWeb(blacklisted: false, msgpack: webAlertUpdate.messagePack())
- }
-
- if createTextMessageRequest.tmpError != nil {
- // send error
- let createTextMessageResponse = WebCreateTextMessageResponse(request: createTextMessageRequest)
- let baseMessageId = createTextMessageRequest.baseMessage?.id.hexEncodedString() ?? ""
- let backgroundKey = kAppAckBackgroundTask + baseMessageId
- BackgroundTaskManager.shared.newBackgroundTask(key: backgroundKey, timeout: Int(kAppAckBackgroundTaskTime)) {
- DDLogVerbose("Threema Web: MessagePack -> Send create/textMessage")
- session.sendMessageToWeb(blacklisted: false, msgpack: createTextMessageResponse.messagePack())
- }
-
- session.webClientProcessQueue.async {
- completionHandler(nil, true)
- }
- return
- } else {
- // background task to send ack to server
- var id: String?
- if let groupId = createTextMessageRequest.groupId {
- id = groupId.hexEncodedString()
- } else {
- id = createTextMessageRequest.id!
- }
- let backgroundKey = kAppAckBackgroundTask + id!
-
- BackgroundTaskManager.shared.newBackgroundTask(key: backgroundKey, timeout: Int(kAppAckBackgroundTaskTime)) {
- session.webClientProcessQueue.async {
- completionHandler(nil, true)
- }
- return
- }
- }
- }
- break
- case "fileMessage"?:
- let createFileMessageRequest = WebCreateFileMessageRequest.init(message: self, session: session)
- session.addRequestCreateMessage(requestId: createFileMessageRequest.requestId!, abstractMessage: createFileMessageRequest)
- createFileMessageRequest.sendMessage {
- completionHandler(nil, true)
- return
- }
- break
- default:
- let confirmResponse = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmResponse.messagePack(), false)
- return
- }
- }
- else if messageType == "update" {
- switch messageSubType {
- case "connectionInfo"?:
- let updateRequestConnectionInfo = WebUpdateConnectionInfoRequest.init(message: self)
- session.connectionContext()?.connectionInfoRequest = updateRequestConnectionInfo
- if session.connectionStatus() == .connectionInfoSend {
- ValidationLogger.shared()?.logString("Threema Web: ConnectionInfo received maybeResume --> state: \(session.connectionStatus()!.rawValue)")
- session.setWCConnectionStateToReady()
- updateRequestConnectionInfo.maybeResume(session: session)
- session.messageQueue.processQueue()
- } else {
- ValidationLogger.shared()?.logString("Threema Web: ConnectionInfo received and wait for ConnectionInfoResponse --> state: \(session.connectionStatus()!.rawValue)")
- session.setWCConnectionStateToConnectionInfoReceived()
- }
- completionHandler(nil, true)
- return
- case "contact"?:
- let updateRequestContact = WebUpdateContactRequest.init(message: self)
- updateRequestContact.updateContact()
- let updateResponseContact = WebUpdateContactResponse.init(request: updateRequestContact)
- DDLogVerbose("Threema Web: MessagePack -> Send update/contact")
- completionHandler(updateResponseContact.messagePack(), false)
- return
- case "profile"?:
- let updateRequestProfile = WebUpdateProfileRequest.init(message: self)
- updateRequestProfile.updateProfile()
- let confirmActionResponse = WebConfirmResponse.init(webUpdateProfileRequest: updateRequestProfile)
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmActionResponse.messagePack(), false)
- return
- case "group"?:
- let updateRequestGroup = WebUpdateGroupRequest.init(message: self)
- updateRequestGroup.updateGroup {
- let updateResponseGroup = WebUpdateGroupResponse.init(groupRequest: updateRequestGroup)
- DDLogVerbose("Threema Web: MessagePack -> Send update/group")
- completionHandler(updateResponseGroup.messagePack(), false)
- return
- }
- break
- case "typing"?:
- let updateTyping = WebTypingUpdate.init(message: self)
- updateTyping.sendTypingToContact()
- completionHandler(nil, true)
- return
- case "conversation"?:
- let requestUpdateConversation = WebUpdateConversationRequest.init(message: self)
- requestUpdateConversation.updateConversation()
- let responseConfirmAction = WebConfirmResponse.init(webUpdateConversationRequest: requestUpdateConversation)
- DDLogVerbose("Threema Web: MessagePack -> Send response/confirmAction")
- completionHandler(responseConfirmAction.messagePack(), false)
- return
- case "connectionDisconnect"?:
- let requestUpdateConnectionDisconnect = WebUpdateConnectionDisconnectRequest.init(message: self)
- requestUpdateConnectionDisconnect.disconnect(session: session)
- completionHandler(nil, true)
- return
- case "connectionAck"?:
- let requestUpdateConnectionAck = WebConnectionAckUpdateRequest.init(message: self)
- do {
- // should be previousContext
- if let context = session.connectionContext() {
- if let sequenceNumber = requestUpdateConnectionAck.sequenceNumber {
- try context.prune(theirSequenceNumber: sequenceNumber)
- } else {
- ValidationLogger.shared().logString("Threema Web: Could not prune cache: missing sequenceNumber.")
- session.stop(close: true, forget: false, sendDisconnect: true, reason: .error)
- return
- }
- } else {
- ValidationLogger.shared().logString("Threema Web: Could not prune cache: missing context.")
- session.stop(close: true, forget: false, sendDisconnect: true, reason: .error)
- return
- }
- }
- catch {
- // do error stuff
- ValidationLogger.shared().logString("Threema Web: Could not prune cache: \(error).")
- session.stop(close: true, forget: false, sendDisconnect: true, reason: .error)
- return
- }
- return
- default:
- let responseConfirmAction = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(responseConfirmAction.messagePack(), false)
- return
- }
- }
- else if messageType == "delete" {
- switch messageSubType {
- case "cleanReceiverConversation"?:
- let cleanReceiverConversationRequest = WebCleanReceiverConversationRequest.init(message: self)
- cleanReceiverConversationRequest.clean()
- let confirmActionResponse = WebConfirmResponse.init(webCleanReceiverConversationRequest: cleanReceiverConversationRequest)
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmActionResponse.messagePack(), false)
- return
- case "message"?:
- let deleteMessageRequest = WebDeleteMessageRequest.init(message: self)
- deleteMessageRequest.delete()
- let confirmActionResponse = WebConfirmResponse.init(webDeleteMessageRequest: deleteMessageRequest)
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmActionResponse.messagePack(), false)
- return
- case "group"?:
- let deleteGroupRequest = WebDeleteGroupRequest.init(message: self)
- deleteGroupRequest.deleteOrLeave()
- let confirmActionResponse = WebConfirmResponse.init(webDeleteGroupRequest: deleteGroupRequest)
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(confirmActionResponse.messagePack(), false)
- return
- default:
- let responseConfirmAction = WebConfirmResponse.init(message: self, success: false, error: "unknownSubtype")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(responseConfirmAction.messagePack(), false)
- return
- }
- } else {
- let responseConfirmAction = WebConfirmResponse.init(message: self, success: false, error: "unknownType")
- DDLogVerbose("Threema Web: MessagePack -> Send update/confirm")
- completionHandler(responseConfirmAction.messagePack(), false)
- return
- }
- }
-
- private func updateReadStateForMessage(requestMessage: WebReadRequest) {
- let entityManager = EntityManager()
-
- var conversation:Conversation? = nil
- let baseMessage = entityManager.entityFetcher.message(withId: requestMessage.messageId)
-
- if baseMessage != nil {
- if baseMessage?.conversation.groupId != nil {
- conversation = entityManager.entityFetcher.conversation(forGroupId: baseMessage?.conversation.groupId)
- } else {
- conversation = entityManager.entityFetcher.conversation(forIdentity: baseMessage?.conversation.contact.identity)
- }
-
- if conversation != nil {
- let messageFetcher = MessageFetcher.init(for: conversation, with: entityManager.entityFetcher)
- var foundMessageId = false
-
- var readReceiptQueue: [BaseMessage] = []
- var readNotificationsKeys: [String] = []
- let unreadMessages: [BaseMessage] = messageFetcher!.unreadMessages() as! [BaseMessage]
-
- for case let message in unreadMessages {
- if message.id == requestMessage.messageId || foundMessageId {
- readReceiptQueue.append(message)
- let conversation = message.conversation
- if (conversation!.isGroup()) {
- let key = message.sender.identity + message.id.hexEncodedString()
- readNotificationsKeys.append(key)
- } else {
- let key = message.conversation.contact.identity + message.id.hexEncodedString()
- readNotificationsKeys.append(key)
- }
- foundMessageId = true
- }
- }
-
- if readReceiptQueue.count > 0 {
- if !baseMessage!.conversation.isGroup() {
- MessageSender.sendReadReceipt(forMessages: readReceiptQueue, toIdentity: baseMessage!.conversation.contact.identity, async: true, quickReply: false)
- }
- // DispatchQueue.main.sync {
- entityManager.performSyncBlockAndSafe({
- for message in readReceiptQueue {
- message.read = NSNumber(value: true)
- message.readDate = Date()
- let unreadCount: Int = unreadMessages.count - readReceiptQueue.count
- conversation!.unreadMessageCount = NSNumber.init(value: unreadCount)
- }
- })
-
- let center = UNUserNotificationCenter.current()
- center.removeDeliveredNotifications(withIdentifiers: readNotificationsKeys)
- // }
- } else {
- requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, false, "alreadyRead")
- }
- requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, true, nil)
- } else {
- requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, false, "invalidMessage")
- }
- } else {
- requestMessage.ack = WebAbstractMessageAcknowledgement.init(requestMessage.requestId, false, "invalidMessage")
- }
- DispatchQueue.main.async {
- NotificationManager.sharedInstance().updateUnreadMessagesCount(false)
- }
- }
-
- private func updateAckForMessage(entityManager: EntityManager, requestMessage: WebAckRequest, baseMessage: BaseMessage!) {
- if baseMessage != nil {
- if baseMessage.userackDate != nil && baseMessage.userack.boolValue == requestMessage.acknowledged {
- return
- }
- entityManager.performSyncBlockAndSafe({
- if requestMessage.acknowledged {
- MessageSender.sendUserAck(forMessages: [baseMessage], toIdentity: requestMessage.id, async: true, quickReply: false)
- baseMessage.userack = NSNumber(value: true)
- } else {
- MessageSender.sendUserDecline(forMessages: [baseMessage], toIdentity: requestMessage.id, async: true, quickReply: false)
- baseMessage.userack = NSNumber(value: false)
- }
- baseMessage.userackDate = Date()
- })
- }
- }
-
- private func buildResponseReceivers(completion: @escaping (_ webResponseReceivers: WebReceiversResponse?) -> Void) {
- var contactResult: [Contact]?
- var allGroupConversations: [Conversation]?
- var responseReceivers: WebReceiversResponse? = nil
- let entityManager = EntityManager()
- contactResult = entityManager.entityFetcher.allContacts() as? [Contact]
- allGroupConversations = (entityManager.entityFetcher.allGroupConversations() as? [Conversation])!
- FeatureMask.updateMask(forAllContacts: contactResult) {
- responseReceivers = WebReceiversResponse.init(requestId: self.requestId, allContacts: contactResult!, allGroupConversations: allGroupConversations!)
- completion(responseReceivers)
- }
- }
- }
-
- public struct WebAbstractMessageAcknowledgement {
- var id: String?
- var success: Bool
- var error: String?
-
- init(_ id: String?, _ success: Bool, _ error: String?) {
- self.id = id
- self.success = success
- self.error = error
- }
-
- init(object: [AnyHashable: Any?]) {
- id = object["id"] as? String
- success = object["success"] as! Bool
- error = object["error"] as? String
- }
-
- func objectDict() -> [String: Any] {
- var valuesDict = [String: Any]()
-
- if id != nil {
- valuesDict.updateValue(id!, forKey: "id")
- }
- valuesDict.updateValue(success, forKey: "success")
- if error != nil {
- valuesDict.updateValue(error!, forKey: "error")
- }
- return valuesDict
- }
- }
|