123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2019-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/>.
- import Foundation
- import CocoaLumberjackSwift
- @objc public class WCSession: NSObject, NSCoding {
-
- internal var privateKey: Data?
- internal var webClientSession: WebClientSession?
- internal var messageQueue: WebMessageQueue
-
- private var connection: WCConnection?
-
- private var requestedConversations = [String]()
- private var requestedThumbnails = [Data]()
- private var lastLoadedMessageIndexes = [Data: Int]()
- private var requestCreateMessagesFromWeb = [String: WebAbstractMessage]()
-
- private(set) var webClientProcessQueue:DispatchQueue
-
- public init(webClientSession: WebClientSession) {
- self.webClientSession = webClientSession
- self.privateKey = webClientSession.privateKey
- var hash = webClientSession.initiatorPermanentPublicKeyHash
-
- if hash == nil {
- hash = WCSession.ccSha256(data: webClientSession.initiatorPermanentPublicKey!).hexEncodedString()
- WebClientSessionStore.shared.updateWebClientSession(session: webClientSession, hash: hash!)
- }
- webClientProcessQueue = DispatchQueue(label: "ch.threema.webClientProcessQueue", attributes: [])
- messageQueue = WebMessageQueue.init()
- super.init()
- messageQueue.delegate = self
- }
-
- // MARK: NSCoding
- required public init?(coder aDecoder: NSCoder) {
- // super.init(coder:) is optional, see notes below
- self.privateKey = aDecoder.decodeObject(forKey: "privateKey") as? Data
- self.connection = aDecoder.decodeObject(forKey: "connection") as? WCConnection
- let entityManager = EntityManager()
- if privateKey != nil {
- webClientSession = entityManager.entityFetcher.webClientSession(forPrivateKey: privateKey!)
- } else {
- webClientSession = entityManager.entityFetcher.activeWebClientSession()
- }
- self.messageQueue = aDecoder.decodeObject(forKey: "messageQueue") as! WebMessageQueue
- self.requestedConversations = aDecoder.decodeObject(forKey: "requestedConversations") as! [String]
- self.requestedThumbnails = aDecoder.decodeObject(forKey: "requestedThumbnails") as! [Data]
- self.lastLoadedMessageIndexes = aDecoder.decodeObject(forKey: "lastLoadedMessageIndexes") as! [Data: Int]
- self.requestedConversations = aDecoder.decodeObject(forKey: "requestedConversations") as! [String]
- self.webClientProcessQueue = DispatchQueue(label: "ch.threema.webClientProcessQueue", attributes: [])
- }
-
- public func encode(with aCoder: NSCoder) {
- // super.encodeWithCoder(aCoder) is optional, see notes below
- aCoder.encode(privateKey, forKey: "privateKey")
- aCoder.encode(connection, forKey: "connection")
- aCoder.encode(messageQueue, forKey: "messageQueue")
- aCoder.encode(requestedConversations, forKey: "requestedConversations")
- aCoder.encode(requestedThumbnails, forKey: "requestedThumbnails")
- aCoder.encode(lastLoadedMessageIndexes, forKey: "lastLoadedMessageIndexes")
- }
- }
- extension WCSession {
- // MARK: class functions
-
- class func ccSha256(data: Data) -> Data {
- var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
- digest.withUnsafeMutableBytes { digestBuffer in
- data.withUnsafeBytes { buffer in
- let _ = CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), digestBuffer.bindMemory(to: UInt8.self).baseAddress)
- }
- }
- return digest
- }
- }
- extension WCSession {
- // MARK: public functions
-
- public func connect(authToken: Data?) {
- let newConnection = WCConnection.init(delegate: self)
- if connection != nil {
- if connection!.context != nil {
- newConnection.context = connection!.context!.copy() as? WebConnectionContext
- }
-
- let oldConnection = connection
- ValidationLogger.shared().logString("Threema Web: Close old session")
- oldConnection?.close(close: false, forget: false, sendDisconnect: false, reason: .replace)
- }
- connection = newConnection
- connection?.connect(authToken: authToken)
- }
-
- public func sendMessage() {
-
- }
-
- public func receive() {
-
- }
-
- public func stop(close: Bool, forget: Bool, sendDisconnect: Bool, reason: WCConnection.WCConnectionStopReason) {
- connection?.close(close: close, forget: forget, sendDisconnect: sendDisconnect, reason: reason)
- }
-
- internal func setWCConnectionStateToReady() {
- connection?.setWCConnectionStateToReady()
- }
-
- internal func setWCConnectionStateToConnectionInfoReceived() {
- connection?.setWCConnectionStateToConnectionInfoReceived()
- }
-
- internal func connectionContext() -> WebConnectionContext? {
- return connection?.context
- }
-
- internal func connectionInfoResponse() -> WebUpdateConnectionInfoResponse? {
- return connection?.connectionInfoResponse
- }
-
- internal func sendChunk(chunk: [UInt8], msgpack: Data?, connectionInfo: Bool) {
- connection?.sendChunk(chunk: chunk, msgpack: msgpack, connectionInfo: connectionInfo)
- }
-
- internal func connectionWca() -> String? {
- return connection?.wca
- }
-
- internal func setWcaForConnection(wca: String) {
- connection?.wca = wca
- }
-
- internal func addRequestCreateMessage(requestId: String, abstractMessage: WebAbstractMessage) {
- requestCreateMessagesFromWeb[requestId] = abstractMessage
- }
-
- internal func removeRequestCreateMessage(requestId: String) {
- requestCreateMessagesFromWeb.removeValue(forKey: requestId)
- }
-
- internal func requestMessage(for requestId: String) -> WebAbstractMessage? {
- return requestCreateMessagesFromWeb[requestId]
- }
- }
- extension WCSession {
- // MARK: Requested lists
-
- public func requestedConversations(contains conversationId: String) -> Bool {
- return requestedConversations.contains(conversationId)
- }
-
- public func addRequestedConversation(conversationId: String) {
- if !requestedConversations(contains: conversationId) {
- requestedConversations.append(conversationId)
- }
- }
-
- public func requestedThumbnails(contains messageId: Data) -> Bool {
- return requestedThumbnails.contains(messageId)
- }
-
- public func addRequestedThumbnail(messageId: Data) {
- if !requestedThumbnails(contains: messageId) {
- requestedThumbnails.append(messageId)
- }
- }
-
- public func lastLoadedMessageIndexes(contains messageId: Data) -> Int? {
- return lastLoadedMessageIndexes[messageId]
- }
-
- public func addLastLoadedMessageIndex(messageId: Data, index: Int) {
- lastLoadedMessageIndexes[messageId] = index
- }
-
- public func clearAllRequestedLists() {
- requestedConversations.removeAll()
- requestedThumbnails.removeAll()
- lastLoadedMessageIndexes.removeAll()
- }
- }
- extension WCSession: MessageCompleteDelegate {
- func messageComplete(message: Data) {
- do {
- let object = try message.unpack() as! Dictionary<AnyHashable, Any?>
-
- let webMessage = WebAbstractMessage.init(dictionary: object)
- DDLogVerbose("Threema Web: MessagePack -> Received \(webMessage.messageType)/\(webMessage.messageSubType ?? "")")
- self.webClientProcessQueue.async {
- webMessage.getResponseMsgpack(session: self, completionHandler: { (responseMsgpack, blackListed) in
- if responseMsgpack != nil {
- self.messageQueue.enqueue(data: responseMsgpack, blackListed: blackListed)
- } else {
- print("ResponseMsgpack is nil")
- }
- })
- }
- } catch {
- print("Something went wrong while unpacking data: \(error)")
- }
- }
- }
- extension WCSession: WCConnectionDelegate {
- internal func currentWebClientSession() -> WebClientSession? {
- return webClientSession
- }
-
- internal func currentWCSession() -> WCSession {
- return self
- }
-
- internal func currentMessageQueue() -> WebMessageQueue {
- return messageQueue
- }
- }
- extension WCSession: WebMessageQueueDelegate {
- internal func sendMessageToWeb(blacklisted: Bool, msgpack: Data, _ connectionInfo: Bool = false) {
- self.connection?.sendMessageToWeb(blacklisted: blacklisted, msgpack: msgpack, connectionInfo)
- }
-
- internal func connectionStatus() -> WCConnectionState? {
- return connection?.connectionStatus
- }
- }
|