// _____ _ // |_ _| |_ _ _ ___ ___ _ __ __ _ // | | | ' \| '_/ -_) -_) ' \/ _` |_ // |_| |_||_|_| \___\___|_|_|_\__,_(_) // // Threema iOS Client // Copyright (c) 2012-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 . #import #import #import "AudioMessageSender.h" #import "AudioMessage.h" #import "AudioData.h" #import "Conversation.h" #import "Contact.h" #import "NaClCrypto.h" #import "MyIdentityStore.h" #import "BoxAudioMessage.h" #import "GroupAudioMessage.h" #import "MessageQueue.h" #import "EntityManager.h" #import "ContactPhotoSender.h" #ifdef DEBUG static const DDLogLevel ddLogLevel = DDLogLevelVerbose; #else static const DDLogLevel ddLogLevel = DDLogLevelWarning; #endif @interface AudioMessageSender () @property NSNumber *duration; @property uint32_t boxDataLength; @property NSData *encryptionKey; @property NSData *audioData; @property NSString *webRequestId; @end @implementation AudioMessageSender - (void)sendItem:(URLSenderItem *)item inConversation:(Conversation *)conversation { [self startWithAudioFile:item.url inConversation:conversation requestId:nil]; } - (void)startWithAudioFile:(NSURL *)audioUrl inConversation:(Conversation *)_conversation requestId:(NSString *)requestId { /* Find duration */ AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:audioUrl options:nil]; float durationF = CMTimeGetSeconds(asset.duration); NSNumber *duration = [NSNumber numberWithFloat:durationF]; NSData *audioData = [[NSData alloc] initWithContentsOfURL:audioUrl]; [self startWithAudioData:audioData duration:duration inConversation:_conversation requestId:requestId]; } - (void)startWithAudioData:(NSData *)audioData duration:(NSNumber *)duration inConversation:(Conversation *)conversation requestId:(NSString *)requestId { self.conversation = conversation; _duration = duration; _audioData = audioData; _webRequestId = requestId; [self scheduleUpload]; } - (void)retryWithAudioMessage:(AudioMessage*)message { self.message = message; self.conversation = message.conversation; _duration = message.duration; [self scheduleUpload]; } - (void)createDBMessage { EntityManager *entityManager = [[EntityManager alloc] init]; [entityManager performSyncBlockAndSafe:^{ Conversation *conversationOwnContext = (Conversation *)[entityManager.entityFetcher getManagedObjectById:self.conversation.objectID]; AudioMessage *message = [entityManager.entityCreator audioMessageForConversation:conversationOwnContext]; AudioData *dbAudio = [entityManager.entityCreator audioData]; dbAudio.data = _audioData; message.audio = dbAudio; message.duration = _duration; message.progress = nil; message.sendFailed = [NSNumber numberWithBool:NO]; message.webRequestId = _webRequestId; self.message = message; }]; } -(NSData *)encryptedData { /* Generate random symmetric key and encrypt */ _encryptionKey = [[NaClCrypto sharedCrypto] randomBytes:kBlobKeyLen]; AudioMessage *message = (AudioMessage *)self.message; NSData *boxAudioData = [[NaClCrypto sharedCrypto] symmetricEncryptData:message.audio.data withKey:_encryptionKey nonce:[NSData dataWithBytesNoCopy:kNonce_1 length:sizeof(kNonce_1) freeWhenDone:NO]]; if (boxAudioData == nil) { DDLogWarn(@"Audio encryption failed"); } _boxDataLength = (uint32_t)boxAudioData.length; return boxAudioData; } #pragma mark - BlobMessageSender - (void)sendMessageTo:(Contact *)contact blobIds:(NSArray *)blobIds { BoxAudioMessage *boxMsg = [[BoxAudioMessage alloc] init]; boxMsg.messageId = self.message.id; boxMsg.toIdentity = contact.identity; boxMsg.duration = _duration.floatValue; boxMsg.audioBlobId = blobIds[0]; boxMsg.audioSize = _boxDataLength; boxMsg.encryptionKey = _encryptionKey; [[MessageQueue sharedMessageQueue] enqueue:boxMsg]; [ContactPhotoSender sendProfilePicture:boxMsg]; } -(void)sendGroupMessageTo:(Contact *)contact blobIds:(NSArray *)blobIds { GroupAudioMessage *msg = [[GroupAudioMessage alloc] init]; msg.messageId = self.message.id; msg.date = self.message.date; msg.duration = _duration.floatValue; msg.audioBlobId = blobIds[0]; msg.audioSize = _boxDataLength; msg.encryptionKey = _encryptionKey; msg.groupId = self.conversation.groupId; if (self.conversation.contact == nil) { msg.groupCreator = [MyIdentityStore sharedMyIdentityStore].identity; } else { msg.groupCreator = self.conversation.contact.identity; } msg.toIdentity = contact.identity; [[MessageQueue sharedMessageQueue] enqueue:msg]; [ContactPhotoSender sendProfilePicture:msg]; } @end