More work related to ChatMessage file transfer

This commit is contained in:
Sylvain Berfini 2017-11-08 14:14:19 +01:00
parent 0118f6ee53
commit ba89633e45
12 changed files with 655 additions and 220 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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<Content > contents;
Content internalContent;
Content *currentFileTransferContent;
FileContent *currentFileContentToTransfer;
std::unordered_map<std::string, std::string> 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);
};

View file

@ -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<FileTransferContent&>(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<FileTransferContent&>(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<SalMessageOpInterface *>(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<FileTransferContent&>(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<ChatRoom> 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> 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

View file

@ -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);

43
src/content/content-p.h Normal file
View file

@ -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 <vector>
#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<char> body;
ContentType contentType;
std::string contentDisposition;
};
LINPHONE_END_NAMESPACE
#endif // ifndef _CONTENT_P_H_

View file

@ -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<char> 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;
}

View file

@ -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);
};

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_