From ba89633e453376728db146127d760df427fa1101 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 8 Nov 2017 14:14:19 +0100 Subject: [PATCH] More work related to ChatMessage file transfer --- src/CMakeLists.txt | 5 + src/c-wrapper/api/c-chat-message.cpp | 6 +- src/chat/chat-message/chat-message-p.h | 11 +- src/chat/chat-message/chat-message.cpp | 401 ++++++++++++++----------- src/chat/chat-message/chat-message.h | 5 +- src/content/content-p.h | 43 +++ src/content/content.cpp | 30 +- src/content/content.h | 8 +- src/content/file-content.cpp | 126 ++++++++ src/content/file-content.h | 58 ++++ src/content/file-transfer-content.cpp | 123 ++++++++ src/content/file-transfer-content.h | 59 ++++ 12 files changed, 655 insertions(+), 220 deletions(-) create mode 100644 src/content/content-p.h create mode 100644 src/content/file-content.cpp create mode 100644 src/content/file-content.h create mode 100644 src/content/file-transfer-content.cpp create mode 100644 src/content/file-transfer-content.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 10c0ed793..899677ab1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,6 +84,9 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES content/content-manager.h content/content-type.h content/content.h + content/content-p.h + content/file-content.h + content/file-transfer-content.h core/core-accessor.h core/core-p.h core/core.h @@ -184,6 +187,8 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES content/content-manager.cpp content/content-type.cpp content/content.cpp + content/file-content.cpp + content/file-transfer-content.cpp core/core-accessor.cpp core/core-chat-room.cpp core/core.cpp diff --git a/src/c-wrapper/api/c-chat-message.cpp b/src/c-wrapper/api/c-chat-message.cpp index 8cdd4d4c1..4f64cb6cd 100644 --- a/src/c-wrapper/api/c-chat-message.cpp +++ b/src/c-wrapper/api/c-chat-message.cpp @@ -252,12 +252,8 @@ const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChat // Methods // ============================================================================= -int linphone_chat_room_upload_file(LinphoneChatMessage *msg) { - return ((LinphoneStatus)L_GET_CPP_PTR_FROM_C_OBJECT(msg)->uploadFile()); -} - LinphoneStatus linphone_chat_message_download_file(LinphoneChatMessage *msg) { - return ((LinphoneStatus)L_GET_CPP_PTR_FROM_C_OBJECT(msg)->downloadFile()); + return ((LinphoneStatus)L_GET_PRIVATE_FROM_C_OBJECT(msg)->downloadFile()); } void linphone_chat_message_cancel_file_transfer(LinphoneChatMessage *msg) { diff --git a/src/chat/chat-message/chat-message-p.h b/src/chat/chat-message/chat-message-p.h index a83781a53..733fc5a42 100644 --- a/src/chat/chat-message/chat-message-p.h +++ b/src/chat/chat-message/chat-message-p.h @@ -25,6 +25,8 @@ #include "chat/chat-message/chat-message.h" #include "chat/notification/imdn.h" #include "content/content.h" +#include "content/file-content.h" +#include "content/file-transfer-content.h" #include "content/content-type.h" #include "object/object-p.h" #include "sal/sal.h" @@ -92,6 +94,8 @@ public: LinphoneContent *getFileTransferInformation() const; void setFileTransferInformation(const LinphoneContent *content); + + int downloadFile (); // ----------------------------------------------------------------------------- // Need to be public to be called from static C callbacks @@ -138,7 +142,7 @@ private: bool isReadOnly = false; std::list contents; Content internalContent; - Content *currentFileTransferContent; + FileContent *currentFileContentToTransfer; std::unordered_map customHeaders; mutable LinphoneErrorInfo * errorInfo = nullptr; belle_http_request_t *httpRequest = nullptr; @@ -153,7 +157,8 @@ private: std::string cText; // ----------------------------------------------------------------------------- - + + int uploadFile (); std::string createImdnXml(Imdn::Type imdnType, LinphoneReason reason); void fileUploadEndBackgroundTask(); @@ -165,7 +170,7 @@ private: belle_http_request_listener_callbacks_t *cbs ); void releaseHttpRequest(); - void createFileTransferInformationsFromVndGsmaRcsFtHttpXml(const std::string &body); + void createFileTransferInformationsFromVndGsmaRcsFtHttpXml(FileTransferContent &content); L_DECLARE_PUBLIC(ChatMessage); }; diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index ddea3c8c2..209b83c79 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -31,6 +31,7 @@ #include "chat/modifier/cpim-chat-message-modifier.h" #include "chat/modifier/encryption-chat-message-modifier.h" #include "chat/modifier/multipart-chat-message-modifier.h" +#include "content/file-content.h" #include "content/content.h" #include "core/core.h" @@ -151,6 +152,8 @@ string ChatMessagePrivate::getSalCustomHeaderValue (const string &name) { return L_C_TO_STRING(sal_custom_header_find(salCustomHeaders, name.c_str())); } +// ----------------------------------------------------------------------------- +// Below methods are only for C API backward compatibility... // ----------------------------------------------------------------------------- const ContentType &ChatMessagePrivate::getContentType () { @@ -208,7 +211,6 @@ void ChatMessagePrivate::setText (const string &text) { LinphoneContent *ChatMessagePrivate::getFileTransferInformation () const { L_Q(); - //TODO cache and unref to prevent leak if (q->hasFileTransferContent()) { return q->getFileTransferContent().toLinphoneContent(); } @@ -218,16 +220,29 @@ LinphoneContent *ChatMessagePrivate::getFileTransferInformation () const { void ChatMessagePrivate::setFileTransferInformation (const LinphoneContent *c_content) { L_Q(); - Content content; + // Create a FileContent, it will create the FileTransferContent at upload time + FileContent fileContent; ContentType contentType(linphone_content_get_type(c_content), linphone_content_get_subtype(c_content)); - content.setContentType(contentType); + fileContent.setContentType(contentType); + fileContent.setFileSize(linphone_content_get_size(c_content)); + fileContent.setFileName(linphone_content_get_name(c_content)); if (linphone_content_get_string_buffer(c_content) != NULL) { - content.setBody(linphone_content_get_string_buffer(c_content)); + fileContent.setBody(linphone_content_get_string_buffer(c_content)); } - content.setContentDisposition(linphone_content_get_name(c_content)); - content.setExpectedSize(linphone_content_get_size(c_content)); - q->addContent(content); + q->addContent(fileContent); +} + +int ChatMessagePrivate::downloadFile () { + L_Q(); + + for (Content& content : contents) { + if (content.getContentType() == ContentType::FileTransfer) { + return q->downloadFile(static_cast(content)); + } + } + + return 0; } // ----------------------------------------------------------------------------- @@ -367,7 +382,7 @@ void ChatMessagePrivate::fileTransferOnProgress ( LinphoneChatMessage *msg = L_GET_C_BACK_PTR(q); LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg); - LinphoneContent *content = currentFileTransferContent->toLinphoneContent(); + LinphoneContent *content = currentFileContentToTransfer->toLinphoneContent(); if (linphone_chat_message_cbs_get_file_transfer_progress_indication(cbs)) { linphone_chat_message_cbs_get_file_transfer_progress_indication(cbs)(msg, content, offset, total); } else { @@ -420,12 +435,12 @@ int ChatMessagePrivate::onSendBody ( // if we've not reach the end of file yet, ask for more data // in case of file body handler, won't be called - if (fileTransferFilePath.empty() && offset < currentFileTransferContent->getExpectedSize()) { + if (currentFileContentToTransfer->getFilePath().empty() && offset < currentFileContentToTransfer->getFileSize()) { // get data from call back LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg); LinphoneChatMessageCbsFileTransferSendCb file_transfer_send_cb = linphone_chat_message_cbs_get_file_transfer_send(cbs); - LinphoneContent *content = currentFileTransferContent->toLinphoneContent(); + LinphoneContent *content = currentFileContentToTransfer->toLinphoneContent(); if (file_transfer_send_cb) { LinphoneBuffer *lb = file_transfer_send_cb(msg, content, offset, *size); if (lb == nullptr) { @@ -564,10 +579,10 @@ void ChatMessagePrivate::onRecvBody (belle_sip_user_body_handler_t *bh, belle_si ms_free(decrypted_buffer); if (retval <= 0) { - if (fileTransferFilePath.empty()) { + if (currentFileContentToTransfer->getFilePath().empty()) { LinphoneChatMessage *msg = L_GET_C_BACK_PTR(q); LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg); - LinphoneContent *content = currentFileTransferContent->toLinphoneContent(); + LinphoneContent *content = currentFileContentToTransfer->toLinphoneContent(); if (linphone_chat_message_cbs_get_file_transfer_recv(cbs)) { LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size); linphone_chat_message_cbs_get_file_transfer_recv(cbs)(msg, content, lb); @@ -608,10 +623,10 @@ void ChatMessagePrivate::onRecvEnd (belle_sip_user_body_handler_t *bh) { } if (retval <= 0) { - if (fileTransferFilePath.empty()) { + if (currentFileContentToTransfer->getFilePath().empty()) { LinphoneChatMessage *msg = L_GET_C_BACK_PTR(q); LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg); - LinphoneContent *content = currentFileTransferContent->toLinphoneContent(); + LinphoneContent *content = currentFileContentToTransfer->toLinphoneContent(); if (linphone_chat_message_cbs_get_file_transfer_recv(cbs)) { LinphoneBuffer *lb = linphone_buffer_new(); linphone_chat_message_cbs_get_file_transfer_recv(cbs)(msg, content, lb); @@ -625,6 +640,18 @@ void ChatMessagePrivate::onRecvEnd (belle_sip_user_body_handler_t *bh) { } if (retval <= 0 && state != ChatMessage::State::FileTransferError) { + // Remove the FileTransferContent from the message and store the FileContent + FileContent fileContent = *currentFileContentToTransfer; + q->addContent(fileContent); + for (Content &content : contents) { + if (content.getContentType() == ContentType::FileTransfer) { + FileTransferContent fileTransferContent = static_cast(content); + if (fileTransferContent.getFileContent() == fileContent) { + q->removeContent(content); + break; + } + } + } setState(ChatMessage::State::FileTransferDone); } } @@ -689,38 +716,38 @@ void ChatMessagePrivate::processResponseFromPostFile (const belle_http_response_ first_part_header = "form-data; name=\"File\"; filename=\"filename.txt\""; } else { // temporary storage for the Content-disposition header value - first_part_header = "form-data; name=\"File\"; filename=\"" + currentFileTransferContent->getContentDisposition() + "\""; + first_part_header = "form-data; name=\"File\"; filename=\"" + currentFileContentToTransfer->getContentDisposition() + "\""; } // create a user body handler to take care of the file and add the content disposition and content-type headers - first_part_bh = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(currentFileTransferContent->getExpectedSize(), + first_part_bh = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(currentFileContentToTransfer->getFileSize(), _chat_message_file_transfer_on_progress, nullptr, nullptr, _chat_message_on_send_body, _chat_message_on_send_end, this); - if (!fileTransferFilePath.empty()) { + if (!currentFileContentToTransfer->getFilePath().empty()) { belle_sip_user_body_handler_t *body_handler = (belle_sip_user_body_handler_t *)first_part_bh; // No need to add again the callback for progression, otherwise it will be called twice - first_part_bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(fileTransferFilePath.c_str(), nullptr, this); + first_part_bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(currentFileContentToTransfer->getFilePath().c_str(), nullptr, this); //linphone_content_set_size(cFileTransferInformation, belle_sip_file_body_handler_get_file_size((belle_sip_file_body_handler_t *)first_part_bh)); belle_sip_file_body_handler_set_user_body_handler((belle_sip_file_body_handler_t *)first_part_bh, body_handler); - } else if (!currentFileTransferContent->isEmpty()) { + } else if (!currentFileContentToTransfer->isEmpty()) { first_part_bh = (belle_sip_body_handler_t *)belle_sip_memory_body_handler_new_from_buffer( - ms_strdup(currentFileTransferContent->getBodyAsString().c_str()), - currentFileTransferContent->getSize(), _chat_message_file_transfer_on_progress, this); + ms_strdup(currentFileContentToTransfer->getBodyAsString().c_str()), + currentFileContentToTransfer->getSize(), _chat_message_file_transfer_on_progress, this); } belle_sip_body_handler_add_header(first_part_bh, belle_sip_header_create("Content-disposition", first_part_header.c_str())); belle_sip_body_handler_add_header(first_part_bh, (belle_sip_header_t *)belle_sip_header_content_type_create( - currentFileTransferContent->getContentType().getType().c_str(), - currentFileTransferContent->getContentType().getSubType().c_str())); + currentFileContentToTransfer->getContentType().getType().c_str(), + currentFileContentToTransfer->getContentType().getSubType().c_str())); // insert it in a multipart body handler which will manage the boundaries of multipart msg bh = belle_sip_multipart_body_handler_new(_chat_message_file_transfer_on_progress, this, first_part_bh, nullptr); releaseHttpRequest(); fileUploadBeginBackgroundTask(); - q->uploadFile(); + uploadFile(); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(httpRequest), BELLE_SIP_BODY_HANDLER(bh)); } else if (code == 200) { // file has been uploaded correctly, get server reply and send it const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response); @@ -784,9 +811,15 @@ void ChatMessagePrivate::processResponseFromPostFile (const belle_http_response_ setText(body); } setContentType(ContentType::FileTransfer);*/ - - (*currentFileTransferContent).setContentType(ContentType::FileTransfer); - (*currentFileTransferContent).setBody(body); + FileContent fileContent = *currentFileContentToTransfer; + + FileTransferContent fileTransferContent; + fileTransferContent.setContentType(ContentType::FileTransfer); + fileTransferContent.setFileContent(fileContent); + fileTransferContent.setBody(body); + + q->removeContent(fileContent); + q->addContent(fileTransferContent); q->updateState(ChatMessage::State::FileTransferDone); releaseHttpRequest(); @@ -812,8 +845,8 @@ static void _chat_process_response_headers_from_get_file (void *data, const bell d->processResponseHeadersFromGetFile(event); } -static Content createFileTransferInformationFromHeaders (const belle_sip_message_t *m) { - Content content; +static FileContent createFileTransferInformationFromHeaders (const belle_sip_message_t *m) { + FileContent content; belle_sip_header_content_length_t *content_length_hdr = BELLE_SIP_HEADER_CONTENT_LENGTH(belle_sip_message_get_header(m, "Content-Length")); belle_sip_header_content_type_t *content_type_hdr = BELLE_SIP_HEADER_CONTENT_TYPE(belle_sip_message_get_header(m, "Content-Type")); @@ -824,10 +857,11 @@ static Content createFileTransferInformationFromHeaders (const belle_sip_message subtype = belle_sip_header_content_type_get_subtype(content_type_hdr); lInfo() << "Extracted content type " << type << " / " << subtype << " from header"; ContentType contentType(type, subtype); + content.setContentType(contentType); } if (content_length_hdr) { - content.setExpectedSize(belle_sip_header_content_length_get_content_length(content_length_hdr)); - lInfo() << "Extracted content length " << content.getExpectedSize() << " from header"; + content.setFileSize(belle_sip_header_content_length_get_content_length(content_length_hdr)); + lInfo() << "Extracted content length " << content.getFileSize() << " from header"; } return content; @@ -843,26 +877,26 @@ void ChatMessagePrivate::processResponseHeadersFromGetFile (const belle_http_res belle_sip_body_handler_t *body_handler = nullptr; size_t body_size = 0; - if (currentFileTransferContent == nullptr) { + if (currentFileContentToTransfer == nullptr) { lWarning() << "No file transfer information for msg [" << this << "]: creating..."; - Content content = createFileTransferInformationFromHeaders(response); + FileContent content = createFileTransferInformationFromHeaders(response); q->addContent(content); } else { belle_sip_header_content_length_t *content_length_hdr = BELLE_SIP_HEADER_CONTENT_LENGTH(belle_sip_message_get_header(response, "Content-Length")); - currentFileTransferContent->setExpectedSize(belle_sip_header_content_length_get_content_length(content_length_hdr)); - lInfo() << "Extracted content length " << currentFileTransferContent->getExpectedSize() << " from header"; + currentFileContentToTransfer->setFileSize(belle_sip_header_content_length_get_content_length(content_length_hdr)); + lInfo() << "Extracted content length " << currentFileContentToTransfer->getFileSize() << " from header"; } - if (q->hasFileTransferContent()) { - body_size = q->getFileTransferContent().getExpectedSize(); + if (currentFileContentToTransfer) { + body_size = currentFileContentToTransfer->getFileSize(); } body_handler = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(body_size, _chat_message_file_transfer_on_progress, nullptr, _chat_message_on_recv_body, nullptr, _chat_message_on_recv_end, this); - if (!fileTransferFilePath.empty()) { + if (!currentFileContentToTransfer->getFilePath().empty()) { belle_sip_user_body_handler_t *bh = (belle_sip_user_body_handler_t *)body_handler; - body_handler = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(fileTransferFilePath.c_str(), _chat_message_file_transfer_on_progress, this); + body_handler = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(currentFileContentToTransfer->getFilePath().c_str(), _chat_message_file_transfer_on_progress, this); if (belle_sip_body_handler_get_size((belle_sip_body_handler_t *)body_handler) == 0) { // If the size of the body has not been initialized from the file stat, use the one from the // file_transfer_information. @@ -1008,14 +1042,38 @@ void ChatMessagePrivate::releaseHttpRequest () { } } -void ChatMessagePrivate::createFileTransferInformationsFromVndGsmaRcsFtHttpXml (const string &body) { +int ChatMessagePrivate::uploadFile () { L_Q(); + + if (httpRequest) { + lError() << "linphone_chat_room_upload_file(): there is already an upload in progress."; + return -1; + } + + // THIS IS ONLY FOR BACKWARD C API COMPAT + if (currentFileContentToTransfer->getFilePath().empty() && !q->getFileTransferFilepath().empty()) { + currentFileContentToTransfer->setFilePath(q->getFileTransferFilepath()); + } + + belle_http_request_listener_callbacks_t cbs = { 0 }; + cbs.process_response = _chat_message_process_response_from_post_file; + cbs.process_io_error = _chat_message_process_io_error_upload; + cbs.process_auth_requested = _chat_message_process_auth_requested_upload; + + int err = startHttpTransfer(linphone_core_get_file_transfer_server(q->getCore()->getCCore()), "POST", &cbs); + if (err == -1) + setState(ChatMessage::State::NotDelivered); + + return err; +} + +void ChatMessagePrivate::createFileTransferInformationsFromVndGsmaRcsFtHttpXml (FileTransferContent &fileTransferContent) { xmlChar *file_url = nullptr; xmlDocPtr xmlMessageBody; xmlNodePtr cur; /* parse the msg body to get all informations from it */ - xmlMessageBody = xmlParseDoc((const xmlChar *)body.c_str()); - Content content; + xmlMessageBody = xmlParseDoc((const xmlChar *)fileTransferContent.getBodyAsString().c_str()); + FileContent fileContent; cur = xmlDocGetRootElement(xmlMessageBody); if (cur != nullptr) { @@ -1030,13 +1088,13 @@ void ChatMessagePrivate::createFileTransferInformationsFromVndGsmaRcsFtHttpXml ( if (!xmlStrcmp(cur->name, (const xmlChar *)"file-size")) { xmlChar *fileSizeString = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1); size_t size = (size_t)strtol((const char *)fileSizeString, nullptr, 10); - content.setExpectedSize(size); + fileContent.setFileSize(size); xmlFree(fileSizeString); } if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) { xmlChar *filename = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1); - content.setContentDisposition((char *)filename); + fileContent.setFileName((char *)filename); xmlFree(filename); } if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) { @@ -1050,7 +1108,7 @@ void ChatMessagePrivate::createFileTransferInformationsFromVndGsmaRcsFtHttpXml ( type = ms_strndup((char *)content_type, contentTypeIndex); subtype = ms_strdup(((char *)content_type + contentTypeIndex + 1)); ContentType contentType(type, subtype); - content.setContentType(contentType); + fileContent.setContentType(contentType); ms_free(subtype); ms_free(type); ms_free(content_type); @@ -1086,8 +1144,12 @@ void ChatMessagePrivate::createFileTransferInformationsFromVndGsmaRcsFtHttpXml ( } xmlFreeDoc(xmlMessageBody); - externalBodyUrl = string((const char *)file_url); - q->addContent(content); + fileContent.setFilePath(fileTransferContent.getFilePath()); // Copy file path from file transfer content to file content for file body handler + fileTransferContent.setFileUrl(string((const char *)file_url)); // Set file url in the file transfer content for the download + + // Link the FileContent to the FileTransferContent + fileTransferContent.setFileContent(fileContent); + xmlFree(file_url); } @@ -1163,12 +1225,10 @@ LinphoneReason ChatMessagePrivate::receive () { } bool messageToBeStored = false; - for (const auto &c : contents) { - if (c.getContentType() == ContentType::FileTransfer) { - messageToBeStored = true; - createFileTransferInformationsFromVndGsmaRcsFtHttpXml(c.getBodyAsString()); - } else if (c.getContentType() == ContentType::PlainText) + for (Content &c : contents) { + if (c.getContentType() == ContentType::FileTransfer || c.getContentType() == ContentType::PlainText) { messageToBeStored = true; + } } if (messageToBeStored) q->store(); @@ -1185,23 +1245,24 @@ void ChatMessagePrivate::send () { if ((currentSendStep & ChatMessagePrivate::Step::FileUpload) == ChatMessagePrivate::Step::FileUpload) { lInfo() << "File upload step already done, skipping"; } else { - currentFileTransferContent = nullptr; + currentFileContentToTransfer = nullptr; + // For each FileContent, upload it and create a FileTransferContent for (Content &content : contents) { ContentType contentType = content.getContentType(); - //TODO Improve ! + //TODO Improve if (contentType != ContentType::FileTransfer && contentType != ContentType::PlainText && contentType != ContentType::ExternalBody && contentType != ContentType::Imdn && contentType != ContentType::ImIsComposing && contentType != ContentType::ResourceLists && contentType != ContentType::Sdp && contentType != ContentType::ConferenceInfo && contentType != ContentType::Cpim) { lInfo() << "Found content with type " << contentType.asString() << ", set it for file upload"; - currentFileTransferContent = &content; + currentFileContentToTransfer = (FileContent *)&content; break; } } - if (currentFileTransferContent != nullptr) { + if (currentFileContentToTransfer != nullptr) { /* Open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */ - if (q->uploadFile() == 0) { + if (uploadFile() == 0) { setState(ChatMessage::State::InProgress); } return; @@ -1250,18 +1311,6 @@ void ChatMessagePrivate::send () { // Start of message modification // --------------------------------------- - // TODO Remove : This won't be necessary once we store the contentsList - string clearTextMessage; - ContentType clearTextContentType; - - if (!getText().empty()) { - clearTextMessage = getText().c_str(); - } - if (getContentType().isValid()) { - clearTextContentType = getContentType(); - } - // End of TODO Remove - if (applyModifiers) { if ((currentSendStep &ChatMessagePrivate::Step::Multipart) == ChatMessagePrivate::Step::Multipart) { lInfo() << "Multipart step already done, skipping"; @@ -1309,7 +1358,7 @@ void ChatMessagePrivate::send () { internalContent = contents.front(); } - if (!externalBodyUrl.empty()) { + if (!externalBodyUrl.empty()) { // Deprecated way of sending files char *content_type = ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"", externalBodyUrl.c_str()); auto msgOp = dynamic_cast(op); msgOp->send_message(from.asString().c_str(), to.asString().c_str(), content_type, nullptr, nullptr); @@ -1323,16 +1372,14 @@ void ChatMessagePrivate::send () { } } - // TODO Remove : This won't be necessary once we store the contentsList - if (!getText().empty() && getText() == clearTextMessage) { - /* We replace the encrypted message by the original one so it can be correctly stored and displayed by the application */ - setText(clearTextMessage); + for (Content &content : contents) { + // Restore FileContents and remove FileTransferContents + if (content.getContentType() == ContentType::FileTransfer) { + FileTransferContent fileTransferContent = static_cast(content); + q->removeContent(content); + q->addContent(fileTransferContent.getFileContent()); + } } - if (getContentType().isValid() && (getContentType() != clearTextContentType)) { - /* We replace the encrypted content type by the original one */ - setContentType(clearTextContentType); - } - // End of TODO Remove q->setImdnMessageId(op->get_call_id()); /* must be known at that time */ @@ -1373,16 +1420,6 @@ shared_ptr ChatMessage::getChatRoom () const { // ----------------------------------------------------------------------------- -const string &ChatMessage::getExternalBodyUrl () const { - L_D(); - return d->externalBodyUrl; -} - -void ChatMessage::setExternalBodyUrl (const string &url) { - L_D(); - d->externalBodyUrl = url; -} - time_t ChatMessage::getTime () const { L_D(); return d->time; @@ -1434,19 +1471,6 @@ bool ChatMessage::isRead () const { return d->state == State::Delivered || d->state == State::Displayed || d->state == State::DeliveredToUser; } -const string &ChatMessage::getAppdata () const { - L_D(); - return d->appData; -} - -void ChatMessage::setAppdata (const string &appData) { - L_D(); - d->appData = appData; - - // TODO: history. - // linphone_chat_message_store_appdata(L_GET_C_BACK_PTR(this)); -} - const Address &ChatMessage::getFromAddress () const { L_D(); return d->from; @@ -1475,16 +1499,6 @@ const Address &ChatMessage::getRemoteAddress () const { return getDirection() != Direction::Incoming ? getToAddress() : getFromAddress(); } -const string &ChatMessage::getFileTransferFilepath () const { - L_D(); - return d->fileTransferFilePath; -} - -void ChatMessage::setFileTransferFilepath (const string &path) { - L_D(); - d->fileTransferFilePath = path; -} - // ----------------------------------------------------------------------------- const LinphoneErrorInfo *ChatMessage::getErrorInfo () const { @@ -1559,48 +1573,6 @@ void ChatMessage::removeCustomHeader (const string &headerName) { d->customHeaders.erase(headerName); } -bool ChatMessage::hasTextContent() const { - L_D(); - for (const auto &c : d->contents) { - if (c.getContentType() == ContentType::PlainText) { - return true; - } - } - return false; -} - -const Content &ChatMessage::getTextContent() const { - L_D(); - for (const auto &c : d->contents) { - if (c.getContentType() == ContentType::PlainText) { - return c; - } - } - return Content::Empty; -} - -bool ChatMessage::hasFileTransferContent() const { - L_D(); - for (const auto &c : d->contents) { - if (c.getContentType() == ContentType::FileTransfer) { - return true; - } - } - return false; -} - -const Content &ChatMessage::getFileTransferContent() const { - L_D(); - for (const auto &c : d->contents) { - if (c.getContentType() == ContentType::FileTransfer) { - return c; - } - } - return Content::Empty; -} - -// ----------------------------------------------------------------------------- - void ChatMessage::store () { L_D(); @@ -1660,44 +1632,32 @@ void ChatMessage::sendDisplayNotification () { d->sendImdn(Imdn::Type::Display, LinphoneReasonNone); } -int ChatMessage::uploadFile () { +int ChatMessage::downloadFile(FileTransferContent& fileTransferContent) { L_D(); - - if (d->httpRequest) { - lError() << "linphone_chat_room_upload_file(): there is already an upload in progress."; - return -1; - } - - belle_http_request_listener_callbacks_t cbs = { 0 }; - cbs.process_response = _chat_message_process_response_from_post_file; - cbs.process_io_error = _chat_message_process_io_error_upload; - cbs.process_auth_requested = _chat_message_process_auth_requested_upload; - - int err = d->startHttpTransfer(linphone_core_get_file_transfer_server(getCore()->getCCore()), "POST", &cbs); - if (err == -1) - d->setState(State::NotDelivered); - - return err; -} - -int ChatMessage::downloadFile () { - L_D(); - + if (d->httpRequest) { lError() << "linphone_chat_message_download_file(): there is already a download in progress"; return -1; } - if (hasFileTransferContent()) { - d->currentFileTransferContent = (Content *)&getFileTransferContent(); + if (fileTransferContent.getContentType() != ContentType::FileTransfer) { + lError() << "linphone_chat_message_download_file(): content type is not FileTransfer"; + return -1; } + d->createFileTransferInformationsFromVndGsmaRcsFtHttpXml(fileTransferContent); + FileContent fileContent = fileTransferContent.getFileContent(); + d->currentFileContentToTransfer = &fileContent; + if (d->currentFileContentToTransfer == nullptr) { + return -1; + } + belle_http_request_listener_callbacks_t cbs = { 0 }; cbs.process_response_headers = _chat_process_response_headers_from_get_file; cbs.process_response = _chat_message_process_response_from_get_file; cbs.process_io_error = _chat_message_process_io_error_download; cbs.process_auth_requested = _chat_message_process_auth_requested_download; - int err = d->startHttpTransfer(d->externalBodyUrl, "GET", &cbs); + int err = d->startHttpTransfer(fileTransferContent.getFileUrl(), "GET", &cbs); // File URL has been set by createFileTransferInformationsFromVndGsmaRcsFtHttpXml if (err == -1) return -1; // start the download, status is In Progress @@ -1716,9 +1676,9 @@ void ChatMessage::cancelFileTransfer () { if (chatRoom) { shared_ptr core = getCore(); lInfo() << "Canceling file transfer " << ( - d->externalBodyUrl.empty() + d->currentFileContentToTransfer->getFilePath().empty() ? linphone_core_get_file_transfer_server(core->getCCore()) - : d->externalBodyUrl.c_str() + : d->currentFileContentToTransfer->getFilePath().c_str() ); belle_http_provider_cancel_request(core->getCCore()->http_provider, d->httpRequest); } else { @@ -1780,4 +1740,83 @@ int ChatMessage::putCharacter (uint32_t character) { return -1; } +// ----------------------------------------------------------------------------- +// Below methods are only for C API backward compatibility... +// ----------------------------------------------------------------------------- + +bool ChatMessage::hasTextContent() const { + L_D(); + for (const auto &c : d->contents) { + if (c.getContentType() == ContentType::PlainText) { + return true; + } + } + return false; +} + +const Content &ChatMessage::getTextContent() const { + L_D(); + for (const auto &c : d->contents) { + if (c.getContentType() == ContentType::PlainText) { + return c; + } + } + return Content::Empty; +} + +bool ChatMessage::hasFileTransferContent() const { + L_D(); + for (const auto &c : d->contents) { + if (c.getContentType() == ContentType::FileTransfer) { + return true; + } + } + return false; +} + +const Content &ChatMessage::getFileTransferContent() const { + L_D(); + for (const auto &c : d->contents) { + if (c.getContentType() == ContentType::FileTransfer) { + return c; + } + } + return Content::Empty; +} + +const string &ChatMessage::getFileTransferFilepath () const { + L_D(); + return d->fileTransferFilePath; +} + +void ChatMessage::setFileTransferFilepath (const string &path) { + L_D(); + d->fileTransferFilePath = path; +} + +const string &ChatMessage::getAppdata () const { + L_D(); + return d->appData; +} + +void ChatMessage::setAppdata (const string &appData) { + L_D(); + d->appData = appData; + + // TODO: history. + // linphone_chat_message_store_appdata(L_GET_C_BACK_PTR(this)); +} + +const string &ChatMessage::getExternalBodyUrl () const { + L_D(); + return d->externalBodyUrl; +} + +void ChatMessage::setExternalBodyUrl (const string &url) { + L_D(); + d->externalBodyUrl = url; +} + +// ----------------------------------------------------------------------------- + LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-message/chat-message.h b/src/chat/chat-message/chat-message.h index f4227665c..4db4998c0 100644 --- a/src/chat/chat-message/chat-message.h +++ b/src/chat/chat-message/chat-message.h @@ -35,6 +35,7 @@ LINPHONE_BEGIN_NAMESPACE class Address; class ChatRoom; class Content; +class FileTransferContent; class ChatMessagePrivate; class LINPHONE_PUBLIC ChatMessage : public Object, public CoreAccessor { @@ -61,8 +62,6 @@ public: void setAppdata (const std::string &appData); const std::string &getExternalBodyUrl () const; void setExternalBodyUrl (const std::string &url); - int uploadFile (); - int downloadFile (); void cancelFileTransfer (); int putCharacter (uint32_t character); void updateState (State state); @@ -115,6 +114,8 @@ public: void addCustomHeader (const std::string &headerName, const std::string &headerValue); void removeCustomHeader (const std::string &headerName); + int downloadFile (FileTransferContent& content); + private: L_DECLARE_PRIVATE(ChatMessage); L_DISABLE_COPY(ChatMessage); diff --git a/src/content/content-p.h b/src/content/content-p.h new file mode 100644 index 000000000..b3045b429 --- /dev/null +++ b/src/content/content-p.h @@ -0,0 +1,43 @@ +/* + * content-p.h + * Copyright (C) 2010-2017 Belledonne Communications SARL + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _CONTENT_P_H_ +#define _CONTENT_P_H_ + +#include + +#include "object/clonable-object-p.h" +#include "object/object-p.h" +#include "content-type.h" +#include "content.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class ContentPrivate : public ClonableObjectPrivate { +public: + std::vector body; + ContentType contentType; + std::string contentDisposition; +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _CONTENT_P_H_ diff --git a/src/content/content.cpp b/src/content/content.cpp index 327dbfe90..dd646d198 100644 --- a/src/content/content.cpp +++ b/src/content/content.cpp @@ -18,8 +18,8 @@ */ #include "content-type.h" -#include "object/clonable-object-p.h" +#include "content-p.h" #include "content.h" #include "linphone/core.h" @@ -29,14 +29,6 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE -class ContentPrivate : public ClonableObjectPrivate { -public: - vector body; - ContentType contentType; - string contentDisposition; - size_t expectedSize; -}; - const Content Content::Empty; // ----------------------------------------------------------------------------- @@ -48,7 +40,6 @@ Content::Content (const Content &src) : ClonableObject(*new ContentPrivate), App d->body = src.getBody(); d->contentType = src.getContentType(); d->contentDisposition = src.getContentDisposition(); - d->expectedSize = src.getExpectedSize(); } Content::Content (Content &&src) : ClonableObject(*new ContentPrivate), AppDataContainer(move(src)) { @@ -56,7 +47,10 @@ Content::Content (Content &&src) : ClonableObject(*new ContentPrivate), AppDataC d->body = move(src.getPrivate()->body); d->contentType = move(src.getPrivate()->contentType); d->contentDisposition = move(src.getPrivate()->contentDisposition); - d->expectedSize = move(src.getExpectedSize()); +} + +Content::Content (ContentPrivate &p) : ClonableObject(p) { + } Content &Content::operator= (const Content &src) { @@ -65,7 +59,6 @@ Content &Content::operator= (const Content &src) { d->body = src.getBody(); d->contentType = src.getContentType(); d->contentDisposition = src.getContentDisposition(); - d->expectedSize = src.getExpectedSize(); AppDataContainer::operator=(src); } @@ -77,7 +70,6 @@ Content &Content::operator= (Content &&src) { d->body = move(src.getPrivate()->body); d->contentType = move(src.getPrivate()->contentType); d->contentDisposition = move(src.getPrivate()->contentDisposition); - d->expectedSize = move(src.getExpectedSize()); AppDataContainer::operator=(move(src)); return *this; } @@ -150,16 +142,6 @@ size_t Content::getSize () const { return d->body.size(); } -void Content::setExpectedSize(size_t expectedSize) { - L_D(); - d->expectedSize = expectedSize; -} - -size_t Content::getExpectedSize() const { - L_D(); - return d->expectedSize; -} - bool Content::isEmpty () const { return getSize() == 0; } @@ -174,8 +156,6 @@ LinphoneContent * Content::toLinphoneContent() const { content = linphone_core_create_content(NULL); linphone_content_set_type(content, getContentType().getType().c_str()); linphone_content_set_subtype(content, getContentType().getSubType().c_str()); - linphone_content_set_size(content, getExpectedSize()); - linphone_content_set_name(content, getContentDisposition().c_str()); return content; } diff --git a/src/content/content.h b/src/content/content.h index 450f1c0eb..0047f6a46 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -60,17 +60,17 @@ public: size_t getSize () const; - void setExpectedSize(size_t expectedSize); - size_t getExpectedSize() const; - bool isValid() const; bool isEmpty () const; - LinphoneContent * toLinphoneContent() const; + virtual LinphoneContent * toLinphoneContent() const; static const Content Empty; +protected: + explicit Content (ContentPrivate &p); + private: L_DECLARE_PRIVATE(Content); }; diff --git a/src/content/file-content.cpp b/src/content/file-content.cpp new file mode 100644 index 000000000..e78252533 --- /dev/null +++ b/src/content/file-content.cpp @@ -0,0 +1,126 @@ +/* + * file-content.cpp + * Copyright (C) 2010-2017 Belledonne Communications SARL + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "content-p.h" +#include "file-content.h" +#include "linphone/core.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +class FileContentPrivate : public ContentPrivate { +public: + string fileName; + string filePath; + size_t fileSize; +}; + +// ----------------------------------------------------------------------------- + +FileContent::FileContent() : Content(*new FileContentPrivate()) { + +} + +FileContent::FileContent (const FileContent &src) : Content(*new FileContentPrivate) { + L_D(); + d->fileName = src.getFileName(); + d->filePath = src.getFilePath(); + d->fileSize = src.getFileSize(); +} + +FileContent::FileContent (FileContent &&src) : Content(*new FileContentPrivate) { + L_D(); + d->fileName = move(src.getPrivate()->fileName); + d->filePath = move(src.getPrivate()->filePath); + d->fileSize = move(src.getPrivate()->fileSize); +} + +FileContent &FileContent::operator= (const FileContent &src) { + L_D(); + if (this != &src) { + Content::operator=(src); + d->fileName = src.getFileName(); + d->filePath = src.getFilePath(); + d->fileSize = src.getFileSize(); + } + + return *this; +} + +FileContent &FileContent::operator= (FileContent &&src) { + L_D(); + Content::operator=(move(src)); + d->fileName = move(src.getPrivate()->fileName); + d->filePath = move(src.getPrivate()->filePath); + d->fileSize = move(src.getPrivate()->fileSize); + return *this; +} + +bool FileContent::operator== (const FileContent &content) const { + L_D(); + return Content::operator==(content) && + d->fileName == content.getFileName() && + d->filePath == content.getFilePath() && + d->fileSize == content.getFileSize(); +} + +void FileContent::setFileSize(size_t size) { + L_D(); + d->fileSize = size; +} + +size_t FileContent::getFileSize() const { + L_D(); + return d->fileSize; +} + +void FileContent::setFileName(const string &name) { + L_D(); + d->fileName = name; +} + +string FileContent::getFileName() const { + L_D(); + return d->fileName; +} + +void FileContent::setFilePath(const string &path) { + L_D(); + d->filePath = path; +} + +string FileContent::getFilePath() const { + L_D(); + return d->filePath; +} + +LinphoneContent * FileContent::toLinphoneContent() const { + LinphoneContent* content; + content = linphone_core_create_content(NULL); + linphone_content_set_type(content, getContentType().getType().c_str()); + linphone_content_set_subtype(content, getContentType().getSubType().c_str()); + linphone_content_set_name(content, getFileName().c_str()); + linphone_content_set_size(content, getFileSize()); + return content; +} + +LINPHONE_END_NAMESPACE diff --git a/src/content/file-content.h b/src/content/file-content.h new file mode 100644 index 000000000..41ecab838 --- /dev/null +++ b/src/content/file-content.h @@ -0,0 +1,58 @@ +/* + * file-content.h + * Copyright (C) 2010-2017 Belledonne Communications SARL + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _FILE_CONTENT_H_ +#define _FILE_CONTENT_H_ + +#include "content.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class FileContentPrivate; + +class LINPHONE_PUBLIC FileContent : public Content { +public: + FileContent(); + FileContent (const FileContent &src); + FileContent (FileContent &&src); + + FileContent &operator= (const FileContent &src); + FileContent &operator= (FileContent &&src); + bool operator== (const FileContent &content) const; + + void setFileSize(size_t size); + size_t getFileSize() const; + + void setFileName(const std::string &name); + std::string getFileName() const; + + void setFilePath(const std::string &path); + std::string getFilePath() const; + + LinphoneContent * toLinphoneContent() const override; + +private: + L_DECLARE_PRIVATE(FileContent); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _FILE_CONTENT_H_ diff --git a/src/content/file-transfer-content.cpp b/src/content/file-transfer-content.cpp new file mode 100644 index 000000000..9f96eba67 --- /dev/null +++ b/src/content/file-transfer-content.cpp @@ -0,0 +1,123 @@ +/* + * file-transfer-content.cpp + * Copyright (C) 2010-2017 Belledonne Communications SARL + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "content-p.h" +#include "file-transfer-content.h" +#include "linphone/core.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +class FileTransferContentPrivate : public ContentPrivate { +public: + string fileUrl; + string filePath; + FileContent fileContent; +}; + +// ----------------------------------------------------------------------------- + +FileTransferContent::FileTransferContent() : Content(*new FileTransferContentPrivate()) { + +} + +FileTransferContent::FileTransferContent (const FileTransferContent &src) : Content(*new FileTransferContentPrivate) { + L_D(); + d->fileUrl = src.getFileUrl(); + d->filePath = src.getFilePath(); + d->fileContent = src.getFileContent(); +} + +FileTransferContent::FileTransferContent (FileTransferContent &&src) : Content(*new FileTransferContentPrivate) { + L_D(); + d->fileUrl = move(src.getPrivate()->fileUrl); + d->filePath = move(src.getPrivate()->filePath); + d->fileContent = move(src.getPrivate()->fileContent); +} + +FileTransferContent &FileTransferContent::operator= (const FileTransferContent &src) { + L_D(); + if (this != &src) { + Content::operator=(src); + d->fileUrl = src.getFileUrl(); + d->filePath = src.getFilePath(); + d->fileContent = src.getFileContent(); + } + + return *this; +} + +FileTransferContent &FileTransferContent::operator= (FileTransferContent &&src) { + L_D(); + Content::operator=(move(src)); + d->fileUrl = move(src.getPrivate()->fileUrl); + d->filePath = move(src.getPrivate()->filePath); + d->fileContent = move(src.getPrivate()->fileContent); + return *this; +} + +bool FileTransferContent::operator== (const FileTransferContent &content) const { + L_D(); + return Content::operator==(content) && + d->fileUrl == content.getFileUrl() && + d->filePath == content.getFilePath(); +} + +void FileTransferContent::setFileUrl(const string &url) { + L_D(); + d->fileUrl = url; +} + +string FileTransferContent::getFileUrl() const { + L_D(); + return d->fileUrl; +} + +void FileTransferContent::setFilePath(const string &path) { + L_D(); + d->filePath = path; +} + +string FileTransferContent::getFilePath() const { + L_D(); + return d->filePath; +} + +void FileTransferContent::setFileContent(const FileContent &content) { + L_D(); + d->fileContent = content; +} + +FileContent FileTransferContent::getFileContent() const { + L_D(); + return d->fileContent; +} + +LinphoneContent * FileTransferContent::toLinphoneContent() const { + LinphoneContent* content; + content = linphone_core_create_content(NULL); + linphone_content_set_type(content, getContentType().getType().c_str()); + linphone_content_set_subtype(content, getContentType().getSubType().c_str()); + return content; +} + +LINPHONE_END_NAMESPACE diff --git a/src/content/file-transfer-content.h b/src/content/file-transfer-content.h new file mode 100644 index 000000000..61b13089b --- /dev/null +++ b/src/content/file-transfer-content.h @@ -0,0 +1,59 @@ +/* + * file-transfer-content.h + * Copyright (C) 2010-2017 Belledonne Communications SARL + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _FILE_TRANSFER_CONTENT_H_ +#define _FILE_TRANSFER_CONTENT_H_ + +#include "content.h" +#include "file-content.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class FileTransferContentPrivate; + +class LINPHONE_PUBLIC FileTransferContent : public Content { +public: + FileTransferContent(); + FileTransferContent (const FileTransferContent &src); + FileTransferContent (FileTransferContent &&src); + + FileTransferContent &operator= (const FileTransferContent &src); + FileTransferContent &operator= (FileTransferContent &&src); + bool operator== (const FileTransferContent &content) const; + + void setFileUrl(const std::string &url); + std::string getFileUrl() const; + + void setFilePath(const std::string &path); + std::string getFilePath() const; + + void setFileContent(const FileContent &content); + FileContent getFileContent() const; + + LinphoneContent * toLinphoneContent() const override; + +private: + L_DECLARE_PRIVATE(FileTransferContent); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _FILE_TRANSFER_CONTENT_H_