diff --git a/include/linphone/api/c-content.h b/include/linphone/api/c-content.h index f75e0b3279a74910036ba0aaf99db310cfe5a40b..63b6c4818c0ee7b31b2953eb97c8d31426e7ca17 100644 --- a/include/linphone/api/c-content.h +++ b/include/linphone/api/c-content.h @@ -330,16 +330,25 @@ LINPHONE_PUBLIC bool_t linphone_content_is_file_transfer(const LinphoneContent * /** * Tells whether or not this content contains an encrypted file + * @param content #LinphoneContent object. @notnil * @return True is this content contains a file and this file is encrypted, false otherwise. */ LINPHONE_PUBLIC bool_t linphone_content_is_file_encrypted(const LinphoneContent *content); /** * Returns the creation timestamp if this content is a FileContent (received or sent by chat). + * @param content #LinphoneContent object. @notnil * @return The timestamp at which this content was created if available, -1 otherwise. */ LINPHONE_PUBLIC time_t linphone_content_get_creation_timestamp(const LinphoneContent *content); +/** + * Returns the chat message id for which this content is related to, if any. + * @param content #LinphoneContent object. @notnil + * @return The chat message ID if this content is related to a chat message, NULL otherwise. @maybenil + */ +LINPHONE_PUBLIC const char *linphone_content_get_related_chat_message_id(const LinphoneContent *content); + /************ */ /* DEPRECATED */ /* ********** */ diff --git a/src/c-wrapper/api/c-content.cpp b/src/c-wrapper/api/c-content.cpp index c156fee8cbf11ede35634ff0aeb307efbb0eb7bd..602f62f69e474f918457976bb710f53dbfaf6072 100644 --- a/src/c-wrapper/api/c-content.cpp +++ b/src/c-wrapper/api/c-content.cpp @@ -380,6 +380,11 @@ time_t linphone_content_get_creation_timestamp(const LinphoneContent *content) { return -1; } +const char *linphone_content_get_related_chat_message_id(const LinphoneContent *content) { + const auto c = Content::toCpp(content); + return L_STRING_TO_C(c->getRelatedChatMessageId()); +} + // ============================================================================= // Private functions. // ============================================================================= diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index 00160523f6b547ab5b44c45e620505d4f74c801a..7152e1a4d09e1971f6f215e4aa686b3a0dd659c5 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -1198,6 +1198,8 @@ void ChatMessagePrivate::handleAutoDownload() { } for (auto &c : contents) { + c->setRelatedChatMessageId(imdnId); + ContentType contentType = c->getContentType(); if (contentType.strongEqual(ContentType::Icalendar)) { LinphoneConferenceInfo *cConfInfo = @@ -1250,7 +1252,9 @@ void ChatMessagePrivate::restoreFileTransferContentAsFileContent() { if (content && content->isFileTransfer()) { auto fileTransferContent = static_pointer_cast<FileTransferContent>(content); auto fileContent = fileTransferContent->getFileContent(); + if (fileContent) { + fileContent->setRelatedChatMessageId(imdnId); it = contents.erase(it); it = contents.insert(it, fileContent); } else { @@ -1712,6 +1716,10 @@ const string &ChatMessage::getImdnMessageId() const { void ChatMessagePrivate::setImdnMessageId(const string &id) { imdnId = id; + + for (auto &content : contents) { + content->setRelatedChatMessageId(id); + } } const string &ChatMessagePrivate::getCallId() const { diff --git a/src/chat/modifier/file-transfer-chat-message-modifier.cpp b/src/chat/modifier/file-transfer-chat-message-modifier.cpp index 258f53185787171ffc8000a40c3a4ba6efa0da8f..fb735584c5b7c00e465747616cedca002deb7a6f 100644 --- a/src/chat/modifier/file-transfer-chat-message-modifier.cpp +++ b/src/chat/modifier/file-transfer-chat-message-modifier.cpp @@ -620,6 +620,7 @@ ChatMessageModifier::Result FileTransferChatMessageModifier::decode(const shared fileTransferContent->setBody(internalContent.getBody()); string xml_body = fileTransferContent->getBodyAsUtf8String(); parseFileTransferXmlIntoContent(xml_body.c_str(), fileTransferContent); + fileTransferContent->setRelatedChatMessageId(message->getImdnMessageId()); message->addContent(fileTransferContent); return ChatMessageModifier::Result::Done; } @@ -629,6 +630,7 @@ ChatMessageModifier::Result FileTransferChatMessageModifier::decode(const shared auto fileTransferContent = static_pointer_cast<FileTransferContent>(content); string xml_body = fileTransferContent->getBodyAsUtf8String(); parseFileTransferXmlIntoContent(xml_body.c_str(), fileTransferContent); + fileTransferContent->setRelatedChatMessageId(message->getImdnMessageId()); } } return ChatMessageModifier::Result::Done; @@ -881,6 +883,7 @@ void FileTransferChatMessageModifier::processResponseHeadersFromGetFile(const be } else { lWarning() << "No file transfer information for message [" << message << "]: creating..."; auto content = createFileTransferInformationFromHeaders(response); + content->setRelatedChatMessageId(message->getImdnMessageId()); message->addContent(content); } @@ -993,6 +996,7 @@ static void createFileContentFromFileTransferContent(std::shared_ptr<FileTransfe fileContent->setFilePath(fileTransferContent->getFilePath()); fileContent->setContentType(fileTransferContent->getFileContentType()); fileContent->setFileDuration(fileTransferContent->getFileDuration()); + fileContent->setRelatedChatMessageId(fileTransferContent->getRelatedChatMessageId()); // Link the FileContent to the FileTransferContent fileTransferContent->setFileContent(fileContent); diff --git a/src/chat/modifier/multipart-chat-message-modifier.cpp b/src/chat/modifier/multipart-chat-message-modifier.cpp index 2536c34cd3287da0649ab8a87bd584bb4b109b3f..863a327750a7e3dc27a9c8a06704352d6b34b08e 100644 --- a/src/chat/modifier/multipart-chat-message-modifier.cpp +++ b/src/chat/modifier/multipart-chat-message-modifier.cpp @@ -64,6 +64,7 @@ ChatMessageModifier::Result MultipartChatMessageModifier::decode(const shared_pt } else { content = Content::create(c); } + content->setRelatedChatMessageId(message->getImdnMessageId()); message->addContent(content); } return ChatMessageModifier::Result::Done; diff --git a/src/content/content.cpp b/src/content/content.cpp index 3a3e3830be8877299239871cab9b3c16baa9ef9f..99b0c302696c66624ae651c8178abfacb5ab8d20 100644 --- a/src/content/content.cpp +++ b/src/content/content.cpp @@ -281,6 +281,14 @@ void Content::setFilePath(const std::string &path) { mCache.filePath = path; } +void Content::setRelatedChatMessageId(const string &messageId) { + mMessageId = messageId; +} + +const string &Content::getRelatedChatMessageId() const { + return mMessageId; +} + void Content::addHeader(const string &headerName, const string &headerValue) { removeHeader(headerName); Header header = Header(headerName, headerValue); diff --git a/src/content/content.h b/src/content/content.h index c31085053dead9325448693dad64222e332f6ba0..fb508573909fe40417fed7e0d47bc3ca9cfa912a 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -110,6 +110,9 @@ public: std::list<Header>::const_iterator findHeader(const std::string &headerName) const; const std::string &getCustomHeader(const std::string &headerName) const; + void setRelatedChatMessageId(const std::string &messageId); + const std::string &getRelatedChatMessageId() const; + void setUserData(const Variant &userData); Variant getUserData() const; @@ -142,6 +145,7 @@ private: void *mCryptoContext = nullptr; // Used to encrypt file for RCS file transfer. bool mIsDirty = false; SalBodyHandler *mBodyHandler = nullptr; + std::string mMessageId; struct Cache { std::string name; diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index b83614ad2f411d422c6137f82e79710a0832137c..09a3772534d65907f25bf50aeed23fcb79ad9572 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -5026,7 +5026,8 @@ list<shared_ptr<Content>> MainDb::getMediaContents(const ConferenceId &conferenc list<shared_ptr<Content>> result = list<shared_ptr<Content>>(); #ifdef HAVE_DB_STORAGE static const string query = - "SELECT name, path, size, content_type.value, conference_chat_message_event.time " + "SELECT name, path, size, content_type.value, conference_chat_message_event.time, " + "conference_chat_message_event.imdn_message_id " " FROM chat_message_file_content " " JOIN chat_message_content ON chat_message_content.id = chat_message_file_content.chat_message_content_id " " JOIN content_type ON content_type.id = chat_message_content.content_type_id " @@ -5047,6 +5048,8 @@ list<shared_ptr<Content>> MainDb::getMediaContents(const ConferenceId &conferenc int size = row.get<int>(2); ContentType contentType(row.get<string>(3)); time_t creation = d->dbSession.getTime(row, 4); + string messageId = row.get<string>(5); + lDebug() << "Fetched media content [" << name << "] message id is [" << messageId << "]"; auto fileContent = FileContent::create<FileContent>(); fileContent->setFileName(name); @@ -5054,6 +5057,7 @@ list<shared_ptr<Content>> MainDb::getMediaContents(const ConferenceId &conferenc fileContent->setFilePath(path); fileContent->setContentType(contentType); fileContent->setCreationTimestamp(creation); + fileContent->setRelatedChatMessageId(messageId); result.push_back(fileContent); } @@ -5068,7 +5072,8 @@ list<shared_ptr<Content>> MainDb::getDocumentContents(const ConferenceId &confer list<shared_ptr<Content>> result = list<shared_ptr<Content>>(); #ifdef HAVE_DB_STORAGE static const string query = - "SELECT name, path, size, content_type.value, conference_chat_message_event.time " + "SELECT name, path, size, content_type.value, conference_chat_message_event.time, " + "conference_chat_message_event.imdn_message_id " " FROM chat_message_file_content " " JOIN chat_message_content ON chat_message_content.id = chat_message_file_content.chat_message_content_id " " JOIN content_type ON content_type.id = chat_message_content.content_type_id " @@ -5088,6 +5093,8 @@ list<shared_ptr<Content>> MainDb::getDocumentContents(const ConferenceId &confer int size = row.get<int>(2); ContentType contentType(row.get<string>(3)); time_t creation = d->dbSession.getTime(row, 4); + string messageId = row.get<string>(5); + lDebug() << "Fetched document content [" << name << "] message id is [" << messageId << "]"; auto fileContent = FileContent::create<FileContent>(); fileContent->setFileName(name); @@ -5095,6 +5102,7 @@ list<shared_ptr<Content>> MainDb::getDocumentContents(const ConferenceId &confer fileContent->setFilePath(path); fileContent->setContentType(contentType); fileContent->setCreationTimestamp(creation); + fileContent->setRelatedChatMessageId(messageId); result.push_back(fileContent); } @@ -5871,6 +5879,8 @@ void MainDb::loadChatMessageContents(const shared_ptr<ChatMessage> &chatMessage) if (bodyEncodingType == 1) content->setBodyFromUtf8(row.get<string>(3)); else content->setBodyFromLocale(row.get<string>(3)); + content->setRelatedChatMessageId(chatMessage->getImdnMessageId()); + // 1.2 - Fetch contents' app data. // TODO: Do not test backend, encapsulate!!! if (getBackend() == MainDb::Backend::Sqlite3) { diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index 96818e65b85ee0f309196ff228c2f0f9e68f320d..25e4e5bf01fc519db2d6d3791f4d921551a8ca9d 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -61,7 +61,9 @@ static bool_t wait_for_chat_room_participants(bctbx_list_t *lcs, LinphoneChatRoo linphone_core_iterate((LinphoneCore *)(iterator->data)); } #ifdef LINPHONE_WINDOWS_UWP - { bc_tester_process_events(); } + { + bc_tester_process_events(); + } #elif defined(LINPHONE_WINDOWS_DESKTOP) { MSG msg; @@ -457,6 +459,7 @@ void _send_file_plus_text( msg = linphone_chat_room_create_empty_message(cr); linphone_chat_message_add_file_content(msg, content); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content)); linphone_content_unref(content); if (text) linphone_chat_message_add_utf8_text_content(msg, text); @@ -473,6 +476,7 @@ void _send_file_plus_text( linphone_content_set_file_path(content2, sendFilepath2); } linphone_chat_message_add_file_content(msg, content2); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content2)); linphone_content_unref(content2); } @@ -532,6 +536,10 @@ void _receive_file_plus_text(bctbx_list_t *coresList, if (!use_buffer) { linphone_content_set_file_path(fileTransferContent, downloaded_file); } + + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(fileTransferContent), + linphone_chat_message_get_message_id(msg)); + linphone_chat_message_download_content(msg, fileTransferContent); BC_ASSERT_EQUAL(linphone_chat_message_get_state(msg), LinphoneChatMessageStateFileTransferInProgress, int, "%d"); @@ -566,6 +574,8 @@ void _receive_file_plus_text(bctbx_list_t *coresList, if (!use_buffer) { linphone_content_set_file_path(fileTransferContent2, downloaded_file); } + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(fileTransferContent2), + linphone_chat_message_get_message_id(msg)); linphone_chat_message_download_content(msg, fileTransferContent2); BC_ASSERT_EQUAL(linphone_chat_message_get_state(msg), LinphoneChatMessageStateFileTransferInProgress, int, "%d"); @@ -4309,11 +4319,15 @@ static void group_chat_room_send_multipart_custom_content_types(void) { BC_ASSERT_STRING_EQUAL(linphone_content_get_type(content1), "application"); BC_ASSERT_STRING_EQUAL(linphone_content_get_subtype(content1), "vnd.3gpp.mcptt-info+xml"); BC_ASSERT_STRING_EQUAL(linphone_content_get_utf8_text(content1), data1); + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(content1), + linphone_chat_message_get_message_id(pauline->stat.last_received_chat_message)); LinphoneContent *content2 = bctbx_list_nth_data(contents, 1); BC_ASSERT_STRING_EQUAL(linphone_content_get_type(content2), "application"); BC_ASSERT_STRING_EQUAL(linphone_content_get_subtype(content2), "vnd.3gpp.mcptt-location-info+xml"); BC_ASSERT_STRING_EQUAL(linphone_content_get_utf8_text(content2), data2); + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(content2), + linphone_chat_message_get_message_id(pauline->stat.last_received_chat_message)); } // Clean db from chat room diff --git a/tester/message_tester.c b/tester/message_tester.c index ef155b9700a8385b6684e9a3a21391ee131373bc..72cf5dbd832309d78fcce5e270a74fcec2866056 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -1416,6 +1416,7 @@ void transfer_message_base4(LinphoneCoreManager *marie, BC_ASSERT_PTR_NOT_NULL(content); file_transfer_size = (int)linphone_content_get_file_size(content); BC_ASSERT_NOT_EQUAL(0, file_transfer_size, int, "%d"); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content)); if (two_files) { FILE *file_to_send = NULL; @@ -1433,6 +1434,7 @@ void transfer_message_base4(LinphoneCoreManager *marie, linphone_content_set_size(content, file_size); /*total size to be transferred*/ linphone_content_set_name(content, "ahbahouaismaisbon.wav"); linphone_content_set_user_data(content, file_to_send); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content)); linphone_chat_message_add_file_content(msg, content); BC_ASSERT_PTR_NOT_NULL(linphone_content_get_user_data(content)); @@ -1641,6 +1643,8 @@ void transfer_message_base4(LinphoneCoreManager *marie, BC_ASSERT_STRING_EQUAL(linphone_content_get_name(content), expected_filename); compare_files(send_filepath, linphone_content_get_file_path(content)); BC_ASSERT_STRING_NOT_EQUAL(linphone_content_get_subtype(content), "vnd.gsma.rcs-ft-http+xml"); + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(content), + linphone_chat_message_get_message_id(msg)); if (linphone_factory_is_imdn_available(linphone_factory_get())) { BC_ASSERT_FALSE(wait_for_until(pauline->lc, marie->lc,