/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-iphone * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #import "FileTransferDelegate.h" #import "LinphoneManager.h" #import "PhoneMainView.h" #import "Utils.h" @interface FileTransferDelegate () @property(strong) NSMutableData *data; @end @implementation FileTransferDelegate - (void)dealloc { if (_message != nil) { [self cancel]; } } + (FileTransferDelegate *)messageDelegate:(LinphoneChatMessage *)message { for (FileTransferDelegate *ftd in [LinphoneManager.instance fileTransferDelegates]) { if (ftd.message == message) { return ftd; } } return nil; } static void file_transfer_progress_indication_recv(LinphoneChatMessage *message, LinphoneContent* content, size_t offset, size_t total) { FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message]; if (offset == total) { NSString *name = [NSString stringWithUTF8String: linphone_content_get_name(content) ? : ""]; LOGI(@"Transfer of %@ (%d bytes): download finished", name, total); NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name]; dispatch_async(dispatch_get_main_queue(), ^{ [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message]; dispatch_async(dispatch_get_main_queue(), ^{ if (![VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] && [ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) { [ChatConversationView writeMediaToGallery:name fileType:fileType]; } }); }); } else { LOGD(@"Transfer of %s (%d bytes): already %ld recv", linphone_content_get_name(content), total, offset); [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferRecvUpdate object:thiz userInfo:@{ @"state" : @(linphone_chat_message_get_state(message)), @"progress" : @(offset * 1.f / total), }]; } } static void file_transfer_progress_indication_send(LinphoneChatMessage *message, LinphoneContent* content, size_t offset, size_t total) { FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message]; if (total) { size_t remaining = total - offset; NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{ @"state" : @(linphone_chat_message_get_state(message)), @"progress" : @(offset * 1.f / total), }]; LOGD(@"Transfer of %s (%d bytes): already sent %ld (%f%%), remaining %ld", linphone_content_get_name(content), total, offset, offset * 100.f / total, remaining); [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferSendUpdate object:thiz userInfo:dict]; // this is the last time we will be notified, so destroy ourselve if (offset == total) { LOGI(@"Upload ended"); thiz.message = NULL; [thiz stopAndDestroy]; } } else { LOGE(@"Transfer of %s (%d bytes): %d Error - no upload data in progress!", linphone_content_get_name(content), total, offset); } } - (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key{ if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) { LOGW(@"fileTransferDelegates has already added %p", self); return; } [LinphoneManager.instance.fileTransferDelegates addObject:self]; [ChatConversationView writeFileInImagesDirectory:data name:name]; LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom)); linphone_content_set_type(content, [type UTF8String]); linphone_content_set_subtype(content, [subtype UTF8String]); linphone_content_set_name(content, [name UTF8String]); linphone_content_set_file_path(content, [[LinphoneManager imagesDirectory] stringByAppendingPathComponent:name].UTF8String); _message = linphone_chat_room_create_file_transfer_message(chatRoom, content); BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; if (!isOneToOneChat && (_text!=nil && ![_text isEqualToString:@""])) linphone_chat_message_add_text_content(_message, [_text UTF8String]); linphone_content_unref(content); linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_send); [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:_message]; LOGI(@"%p Uploading content from message %p", self, _message); linphone_chat_message_send(_message); } - (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom { [LinphoneManager.instance.fileTransferDelegates addObject:self]; _message = linphone_chat_room_create_empty_message(chatRoom); NSMutableArray *names = [[NSMutableArray alloc] init]; NSMutableArray *types = [[NSMutableArray alloc] init]; int i = 0; for (i = 0; i < [context count]; ++i) { LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom)); NSString *type = [context.typesArray objectAtIndex:i]; NSString *name = [context.namesArray objectAtIndex:i]; NSData *data = [context.datasArray objectAtIndex:i]; [ChatConversationView writeFileInImagesDirectory:data name:name]; linphone_content_set_type(content, [type UTF8String]); linphone_content_set_subtype(content, [name.pathExtension UTF8String]); linphone_content_set_name(content, [name UTF8String]); linphone_content_set_file_path(content, [[LinphoneManager imagesDirectory] stringByAppendingPathComponent:name].UTF8String); [names addObject:name]; [types addObject:type]; linphone_chat_message_add_file_content(_message, content); linphone_content_unref(content); } if (_text!=nil && ![_text isEqualToString:@""]) linphone_chat_message_add_text_content(_message, [_text UTF8String]); // todo indication progress [LinphoneManager setValueInMessageAppData:names forKey:@"multiparts" inMessage:_message]; [LinphoneManager setValueInMessageAppData:types forKey:@"multipartstypes" inMessage:_message]; LOGI(@"%p Uploading content from message %p", self, _message); linphone_chat_message_send(_message); } - (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality { NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]]; NSData *data = UIImageJPEGRepresentation(image, quality); [self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage"]; } - (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom { NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]]; [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:@"mov" name:name key:@"localvideo"]; } - (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name { NSURL *url = [ChatConversationView getFileUrl:name]; AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; NSString *fileType = [[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0 ? @"video" : @"file"; NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name]; [self uploadData:data forChatRoom:chatRoom type:fileType subtype:name.lastPathComponent name:name key:key]; } - (BOOL)download:(LinphoneChatMessage *)message { if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) { LOGW(@"fileTransferDelegates has already added %p", self); return FALSE; } [[LinphoneManager.instance fileTransferDelegates] addObject:self]; _message = message; LinphoneContent *content = linphone_chat_message_get_file_transfer_information(_message); if (content == nil) return FALSE; LOGI(@"%p Downloading content in %p ", self, message); linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_recv); linphone_content_set_file_path(content, [[LinphoneManager imagesDirectory] stringByAppendingPathComponent:[NSString stringWithUTF8String:linphone_content_get_name(content)]].UTF8String); linphone_chat_message_download_content(_message, content); return TRUE; } - (void)stopAndDestroy { [self stopAndDestroyAndRemove:TRUE]; } - (void)stopAndDestroyAndRemove:(BOOL)remove { if (remove) [[LinphoneManager.instance fileTransferDelegates] removeObject:self]; if (_message != NULL) { LinphoneChatMessage *msg = _message; _message = NULL; LOGI(@"%p Cancelling transfer from %p", self, msg); linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(msg), NULL); // when we cancel file transfer, this will automatically trigger NotDelivered callback... recalling ourself a // second time so we have to unset message BEFORE calling this linphone_chat_message_cancel_file_transfer(msg); } _data = nil; LOGD(@"%p Destroying", self); } - (void)cancel { [self stopAndDestroy]; } @end