123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // 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/>.
- import UIKit
- import ThreemaFramework
- class WebMessageObject: NSObject {
- var baseMessage: BaseMessage
- var type: String
- var id: String
- var body: String?
- var thumbnail: Dictionary<AnyHashable, Any>?
- var date: Int
- var events: [[String: Any]]?
- var sortKey: UInt64
- var partnerId: String?
- var isOutbox: Bool
- var isStatus: Bool = false
- var caption: String?
- var statusType: String?
- var unread: Bool?
- var state: String?
- var quote: Dictionary<AnyHashable, Any>?
- var file: Dictionary<AnyHashable, Any>?
- var video: Dictionary<AnyHashable, Any>?
- var audio: Dictionary<AnyHashable, Any>?
- var location: Dictionary<AnyHashable, Any>?
- var voip: Dictionary<AnyHashable, Any>?
-
- init(message:BaseMessage, conversation: Conversation, forConversationsRequest: Bool, session: WCSession) {
- baseMessage = message
- type = conversation.isGroup() ? "group" : "contact"
-
- id = message.id.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
- let currentDate = message.dateForCurrentState() ?? message.date
- date = Int((currentDate?.timeIntervalSince1970)!)
- let messageEvents = MessageEvents.init(baseMessage: baseMessage)
- events = messageEvents.events
- sortKey = UInt64((message.date.timeIntervalSince1970 * 1000.0).rounded())
-
- if message.sender != nil {
- if message.isOwn.boolValue {
- partnerId = MyIdentityStore.shared().identity
- } else {
- partnerId = message.sender.identity
- }
- }
- else if conversation.contact != nil {
- if message.isOwn.boolValue {
- partnerId = MyIdentityStore.shared().identity
- } else {
- partnerId = conversation.contact.identity
- }
- }
-
- isOutbox = message.isOwn.boolValue
- unread = !message.read.boolValue
- let messageState = message.messageState
- switch messageState {
- case MESSAGE_STATE_SENDING:
- state = "sending"
- break
- case MESSAGE_STATE_SENT:
- state = "sent"
- break
- case MESSAGE_STATE_DELIVERED:
- state = "delivered"
- break
- case MESSAGE_STATE_READ:
- state = "read"
- break
- case MESSAGE_STATE_USER_ACK:
- state = "user-ack"
- break
- case MESSAGE_STATE_USER_DECLINED:
- state = "user-dec"
- break
- case MESSAGE_STATE_FAILED:
- state = "send-failed"
- break
- default:
- state = nil
- }
-
- super.init()
-
- switch message {
- case is TextMessage:
- addTextMessage(message)
- case is ImageMessage:
- addImageMessage(message, forConversationsRequest: forConversationsRequest, session: session)
- case is VideoMessage:
- addVideoMessage(message, forConversationsRequest: forConversationsRequest, session: session)
- case is AudioMessage:
- addAudioMessage(message)
- case is LocationMessage:
- addLocationMessage(message)
- case is FileMessage:
- addFileMessage(message, forConversationsRequest: forConversationsRequest, session: session)
- case is SystemMessage:
- addSystemMessage(message)
- case is BallotMessage:
- addBallotMessage(message)
- default:
- break
- }
- }
-
- init(message:BaseMessage, conversation: Conversation) {
- baseMessage = message
- if conversation.isGroup() {
- type = "group"
- } else {
- type = "contact"
- }
-
- id = message.id.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
- var currentDate = message.dateForCurrentState()
- if currentDate == nil {
- currentDate = message.date
- }
- date = Int((currentDate?.timeIntervalSince1970)!)
- sortKey = UInt64((currentDate?.timeIntervalSince1970)!)
-
- isOutbox = message.isOwn.boolValue
-
- if message is SystemMessage {
- let systemMessage = message as! SystemMessage
- if !systemMessage.isCallType() {
- isStatus = true
- }
- }
- }
-
- func objectDict() -> [String: Any] {
-
- var objectDict:[String: Any] = ["type": type, "id": id, "date": date, "isOutbox": isOutbox, "isStatus": isStatus, "sortKey": sortKey]
-
- if events != nil {
- objectDict.updateValue(events!, forKey: "events")
- }
-
- if body != nil {
- objectDict.updateValue(body!, forKey: "body")
- }
-
- if thumbnail != nil {
- objectDict.updateValue(thumbnail!, forKey: "thumbnail")
- }
-
- if partnerId != nil {
- objectDict.updateValue(partnerId!, forKey: "partnerId")
- }
-
- if caption != nil {
- objectDict.updateValue(caption!, forKey: "caption")
- }
-
- if statusType != nil {
- objectDict.updateValue(statusType!, forKey: "statusType")
- }
-
- if unread != nil {
- objectDict.updateValue(unread!, forKey: "unread")
- }
-
- if state != nil {
- objectDict.updateValue(state!, forKey: "state")
- }
-
- if quote != nil {
- objectDict.updateValue(quote!, forKey: "quote")
- }
-
- if file != nil {
- objectDict.updateValue(file!, forKey: "file")
- }
-
- if video != nil {
- objectDict.updateValue(video!, forKey: "video")
- }
-
- if audio != nil {
- objectDict.updateValue(audio!, forKey: "audio")
- }
-
- if location != nil {
- objectDict.updateValue(location!, forKey: "location")
- }
-
- if voip != nil {
- objectDict.updateValue(voip!, forKey: "voip")
- }
-
- return objectDict
- }
-
- func removedObjectDict() -> [String: Any] {
- return ["type": type, "id": id, "date": date, "isOutbox": isOutbox, "isStatus": isStatus, "sortKey": sortKey]
- }
-
- private func addTextMessage(_ message: BaseMessage) {
- let textMessage = message as! TextMessage
- type = "text"
- body = textMessage.text
- thumbnail = nil
- caption = nil
- statusType = "text"
-
- var quotedIdentity: NSString?
- var remainingBody: NSString?
- let quotedText = QuoteParser.parseQuote(fromMessage: textMessage.text, quotedIdentity: "edIdentity, remainingBody: &remainingBody)
- if quotedText != nil {
- quote = ["identity": quotedIdentity!, "text": quotedText!]
- body = remainingBody as String?
- }
- }
-
- private func addImageMessage(_ message: BaseMessage, forConversationsRequest: Bool, session: WCSession) {
- let imageMessage = message as! ImageMessage
- type = "image"
- body = nil
- statusType = "text"
-
- if imageMessage.image == nil && imageMessage.thumbnail == nil {
- return
- }
- else if imageMessage.image == nil {
- if imageMessage.thumbnail.data == nil {
- return
- }
- }
- else if imageMessage.thumbnail == nil {
- if imageMessage.image.data == nil {
- return
- }
- }
- else if imageMessage.image.data == nil && imageMessage.thumbnail.data == nil {
- return
- }
-
- if !session.requestedThumbnails(contains: baseMessage.id) {
- let webThumbnail = WebThumbnail.init(imageMessage: imageMessage, onlyThumbnail: true)
- thumbnail = webThumbnail.objectDict()
-
- if !forConversationsRequest {
- session.addRequestedThumbnail(messageId: baseMessage.id)
- }
- }
-
- if let image = imageMessage.image, let imageCaption = image.getCaption() {
- caption = imageCaption
- }
- }
-
- private func addVideoMessage(_ message: BaseMessage, forConversationsRequest: Bool, session: WCSession) {
- let videoMessage = message as! VideoMessage
- type = "video"
- body = nil
- if (videoMessage.thumbnail != nil), !session.requestedThumbnails(contains: baseMessage.id) {
- let webThumbnail = WebThumbnail.init(videoMessage, onlyThumbnail: true)
- thumbnail = webThumbnail.objectDict()
-
- if !forConversationsRequest {
- session.addRequestedThumbnail(messageId: baseMessage.id)
- }
- }
- caption = nil
- statusType = "text"
- let webVideo = WebVideo.init(videoMessage)
- video = webVideo.objectDict()
- }
-
- private func addAudioMessage(_ message: BaseMessage) {
- let audioMessage = message as! AudioMessage
- type = "audio"
- body = nil
- thumbnail = nil
- caption = nil
- statusType = "text"
- let webAudio = WebAudio.init(audioMessage)
- audio = webAudio.objectDict()
- }
-
- private func addLocationMessage(_ message: BaseMessage) {
- let locationMessage = message as! LocationMessage
- type = "location"
- body = nil
- thumbnail = nil
- caption = nil
- statusType = "text"
- let webLocation = WebLocation.init(locationMessage)
- location = webLocation.objectDict()
- }
-
- private func addFileMessage(_ message: BaseMessage, forConversationsRequest: Bool, session: WCSession) {
- let fileMessage = message as! FileMessage
- body = nil
- thumbnail = nil
-
- if let fileThumbnail = fileMessage.thumbnail, fileThumbnail.data != nil {
- if fileMessage.blobThumbnailId != nil {
- if !session.requestedThumbnails(contains: fileMessage.blobThumbnailId) {
- let webThumbnail = WebThumbnail.init(fileMessage, onlyThumbnail: true)
- thumbnail = webThumbnail.objectDict()
-
- if !forConversationsRequest {
- session.addRequestedThumbnail(messageId: fileMessage.blobThumbnailId)
- }
- }
- } else {
- let webThumbnail = WebThumbnail.init(fileMessage, onlyThumbnail: true)
- thumbnail = webThumbnail.objectDict()
- }
- }
-
- if let fileCaption = fileMessage.getCaption() {
- caption = fileCaption
- }
- statusType = "text"
-
- if fileMessage.renderFileImageMessage() {
- type = "image"
- }
- else if fileMessage.renderFileVideoMessage() {
- type = "video"
- let webVideo = WebVideo.init(fileMessage)
- video = webVideo.objectDict()
- }
- else {
- type = "file"
- let webFile = WebFile.init(fileMessage)
- file = webFile.objectDict()
- }
- }
-
- private func addSystemMessage(_ message: BaseMessage) {
- let systemMessage = message as! SystemMessage
- if systemMessage.isCallType() {
- // voip
- type = "voipStatus"
- body = nil
- thumbnail = nil
- caption = nil
- state = nil
- statusType = "text"
-
- if systemMessage.arg != nil {
- do {
- let argDict = try JSONSerialization.jsonObject(with: systemMessage.arg, options: .allowFragments) as! [AnyHashable: Any]
- isOutbox = argDict["CallInitiator"] as! Bool
- }
- catch {
- isOutbox = false
- }
- } else {
- isOutbox = true
- }
-
- let webVoip = WebVoip.init(systemMessage)
- voip = webVoip.objectDict()
- } else {
- type = "text"
- body = systemMessage.format()
- thumbnail = nil
- isStatus = true
- caption = nil
- statusType = "text"
- }
- }
-
- private func addBallotMessage(_ message: BaseMessage) {
- type = "ballot"
- isStatus = false
- statusType = "text"
- }
- }
- struct MessageEvents {
-
- var events = [[String: Any]]()
-
- init(baseMessage: BaseMessage) {
- if baseMessage.isOwn.boolValue == true {
- if baseMessage.remoteSentDate != nil {
- var event = [String: Any]()
- event.updateValue("sent", forKey: "type")
- event.updateValue(Int((baseMessage.remoteSentDate.timeIntervalSince1970)), forKey: "date")
- events.append(event)
- }
- if baseMessage.deliveryDate != nil {
- var event = [String: Any]()
- event.updateValue("delivered", forKey: "type")
- event.updateValue(Int((baseMessage.deliveryDate.timeIntervalSince1970)), forKey: "date")
- events.append(event)
- }
- if baseMessage.readDate != nil {
- var event = [String: Any]()
- event.updateValue("read", forKey: "type")
- event.updateValue(Int((baseMessage.readDate.timeIntervalSince1970)), forKey: "date")
- events.append(event)
- }
- } else {
- if baseMessage.date != nil {
- var event = [String: Any]()
- event.updateValue("sent", forKey: "type")
- event.updateValue(Int((baseMessage.remoteSentDate.timeIntervalSince1970)), forKey: "date")
- events.append(event)
- }
- if baseMessage.deliveryDate != nil {
- var event = [String: Any]()
- event.updateValue("delivered", forKey: "type")
- event.updateValue(Int((baseMessage.deliveryDate.timeIntervalSince1970)), forKey: "date")
- events.append(event)
- }
- if baseMessage.readDate != nil {
- var event = [String: Any]()
- event.updateValue("read", forKey: "type")
- event.updateValue(Int((baseMessage.readDate.timeIntervalSince1970)), forKey: "date")
- events.append(event)
- }
- if baseMessage.userackDate != nil {
- var event = [String: Any]()
- event.updateValue("acked", forKey: "type")
- event.updateValue(Int((baseMessage.userackDate.timeIntervalSince1970)), forKey: "date")
- events.append(event)
- }
- }
- }
- }
- struct WebBlob {
- var blob: Data?
- var type: String
- var name: String
-
- init(imageMessage: ImageMessage) {
- if imageMessage.image.data != nil {
- blob = imageMessage.image.data
- }
- name = imageMessage.blobGetWebFilename()
- type = "image/\(MEDIA_EXTENSION_IMAGE)"
- }
-
- init(videoMessage: VideoMessage) {
- if videoMessage.video.data != nil {
- blob = videoMessage.video.data
- }
- name = videoMessage.blobGetWebFilename()
- type = "video/\(MEDIA_EXTENSION_VIDEO)"
- }
-
- init(audioMessage: AudioMessage) {
- if audioMessage.audio.data != nil {
- blob = audioMessage.audio.data
- }
- name = audioMessage.blobGetWebFilename()
- type = "audio/\(MEDIA_EXTENSION_VIDEO)"
- }
-
- init(fileMessage: FileMessage) {
- if fileMessage.data.data != nil {
- blob = fileMessage.data.data
- }
- name = fileMessage.blobGetWebFilename()
- type = fileMessage.mimeType
- }
-
- func objectDict() -> [String: Any] {
- var objectDict:[String: Any] = ["type": type, "name": name]
-
- if blob != nil {
- objectDict.updateValue(blob!, forKey: "blob")
- }
-
- return objectDict
- }
- }
- struct WebThumbnail {
- var height: Int
- var width: Int
- var preview: Data
- var image: Data?
-
- init(imageMessage: ImageMessage, onlyThumbnail: Bool) {
- var size: CGSize
- var useThumbnail: Bool = true
-
- if !onlyThumbnail && imageMessage.image != nil {
- if imageMessage.image.data != nil {
- useThumbnail = false
- }
- } else {
- if imageMessage.thumbnail == nil {
- useThumbnail = false
- }
-
- if imageMessage.thumbnail != nil {
- if imageMessage.thumbnail.data == nil {
- useThumbnail = false
- }
- }
- }
- if useThumbnail == true {
- size = MediaConverter.getWebThumbnailSize(forImageData: imageMessage.thumbnail.data)
- preview = MediaConverter.getWebPreviewData(imageMessage.thumbnail.data)
- } else {
- size = MediaConverter.getWebThumbnailSize(forImageData: imageMessage.image.data)
- preview = MediaConverter.getWebPreviewData(imageMessage.image.data)
- }
- height = Int(size.height)
- width = Int(size.width)
-
- if !onlyThumbnail {
- if useThumbnail == true {
- image = MediaConverter.getWebThumbnailData(imageMessage.thumbnail.data)
- } else {
- image = MediaConverter.getWebThumbnailData(imageMessage.image.data)
- }
- }
- }
-
- init(_ videoMessage: VideoMessage, onlyThumbnail: Bool) {
- let size = MediaConverter.getWebThumbnailSize(forImageData: videoMessage.thumbnail.data)
- height = Int(size.height)
- width = Int(size.width)
- if let tmpPreview = MediaConverter.getWebPreviewData(videoMessage.thumbnail.data) {
- preview = tmpPreview
- } else {
- height = Int(44)
- width = Int(44)
- preview = UIImage.init(named: "Thumbnail")!.pngData()!
- }
-
- if !onlyThumbnail {
- image = MediaConverter.getWebThumbnailData(videoMessage.thumbnail.data)
- }
- }
-
- init(_ fileMessage: FileMessage, onlyThumbnail: Bool) {
- let size = MediaConverter.getWebThumbnailSize(forImageData: fileMessage.thumbnail.data)
- height = Int(size.height)
- width = Int(size.width)
- if let tmpPreview = MediaConverter.getWebPreviewData(fileMessage.thumbnail.data) {
- preview = tmpPreview
- } else {
- height = Int(44)
- width = Int(44)
- preview = UIImage.init(named: "Thumbnail")!.pngData()!
- }
-
- if !onlyThumbnail {
- image = MediaConverter.getWebThumbnailData(fileMessage.thumbnail.data)
- }
- }
-
- func objectDict() -> [String: Any] {
- return ["height": height, "width": width, "preview": preview]
- }
- }
- struct WebVideo {
- var duration: Int
- var size: Int
-
- init(_ videoMessage: VideoMessage) {
- duration = videoMessage.duration.intValue
- size = videoMessage.videoSize.intValue
- }
-
- init (_ fileMessage: FileMessage) {
- if let videoDuration = fileMessage.duration {
- duration = videoDuration.intValue
- } else {
- duration = 0
- }
- if let videoSize = fileMessage.fileSize {
- size = videoSize.intValue
- } else {
- size = 0
- }
- }
-
- func objectDict() -> [String: Any] {
- return ["duration": duration, "size": size]
- }
- }
- struct WebAudio {
- var duration: Int
-
- init(_ audioMessage: AudioMessage) {
- duration = audioMessage.duration.intValue
- }
-
- func objectDict() -> [String: Any] {
- return ["duration": duration]
- }
- }
- struct WebLocation {
- var lat: Float
- var lon: Float
- var accuracy: Float
- var address: String?
- var description: String?
-
- init(_ locationMessage: LocationMessage) {
- lat = locationMessage.latitude.floatValue
- lon = locationMessage.longitude.floatValue
- accuracy = locationMessage.accuracy.floatValue
- address = nil
- description = locationMessage.poiName
- }
-
- func objectDict() -> [String: Any] {
- var objectDict:[String: Any] = ["lat": lat, "lon": lon, "accuracy": accuracy]
-
- if address != nil {
- objectDict.updateValue(address!, forKey: "address")
- }
- if description != nil {
- objectDict.updateValue(description!, forKey: "description")
- }
- return objectDict
- }
- }
- struct WebFile {
- var name: String
- var size: Int
- var type: String
- var inApp: Bool
-
- init(_ fileMessage: FileMessage) {
- name = fileMessage.fileName
- size = fileMessage.fileSize.intValue
- type = fileMessage.mimeType
- inApp = false
- }
-
- func objectDict() -> [String: Any] {
- return ["name": name, "size": size, "type": type, "inApp": inApp]
- }
- }
- struct WebVoip {
- var status: Int
- var duration: Int?
- var reason: Int?
-
- init(_ systemMessage: SystemMessage) {
- switch systemMessage.type.intValue {
- case kSystemMessageCallMissed:
- status = 1
- break
- case kSystemMessageCallRejected:
- status = 3
- break
- case kSystemMessageCallRejectedBusy:
- status = 3
- break
- case kSystemMessageCallRejectedTimeout:
- status = 3
- break
- case kSystemMessageCallEnded:
- if systemMessage.haveCallTime() {
- status = 2
- } else {
- status = 4
- }
- break
- case kSystemMessageCallRejectedDisabled:
- status = 3
- break
- default:
- status = 2
- }
-
- if let callTime = systemMessage.callTime() {
- duration = Int(DateFormatter.totalSeconds(callTime))
- }
- switch systemMessage.type.intValue {
- case kSystemMessageCallRejected:
- reason = 3
- break
- case kSystemMessageCallRejectedBusy:
- reason = 1
- break
- case kSystemMessageCallRejectedTimeout:
- reason = 2
- break
- case kSystemMessageCallRejectedDisabled:
- reason = 4
- break
- default:
- break
- }
- }
-
- func objectDict() -> [String: Any] {
- var objectDict:[String: Any] = ["status": status]
-
- if duration != nil {
- objectDict.updateValue(duration!, forKey: "duration")
- }
- if reason != nil {
- objectDict.updateValue(reason!, forKey: "reason")
- }
- return objectDict
- }
- }
- extension Data {
- var integer: UInt64 {
- return withUnsafeBytes { $0.pointee }
- }
- }
- extension Numeric {
- var makeData: Data {
- var source = self
- // This will return 1 byte for 8-bit, 2 bytes for 16-bit, 4 bytes for 32-bit and 8 bytes for 64-bit binary integers. For floating point types it will return 4 bytes for single-precision, 8 bytes for double-precision and 16 bytes for extended precision.
- return Data(bytes: &source, count: MemoryLayout<Self>.size)
- }
- }
|