From 5ec972c98dad2b92d0e3665f8d4b7422b60f3cb6 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Fri, 16 Mar 2018 14:32:41 +0100 Subject: [PATCH] Improve Content-Type and Content-Disposition handling. --- src/CMakeLists.txt | 2 + src/chat/chat-message/chat-message.cpp | 2 +- src/chat/chat-room/chat-room.cpp | 4 +- src/chat/chat-room/client-group-chat-room.cpp | 6 +- src/chat/chat-room/server-group-chat-room-p.h | 2 +- src/conference/local-conference.cpp | 3 +- src/content/content-disposition.cpp | 86 +++++++++++++++++++ src/content/content-disposition.h | 59 +++++++++++++ src/content/content-p.h | 3 +- src/content/content.cpp | 9 +- src/content/content.h | 6 +- src/sal/call-op.cpp | 13 ++- tester/multipart-tester.cpp | 4 +- 13 files changed, 175 insertions(+), 24 deletions(-) create mode 100644 src/content/content-disposition.cpp create mode 100644 src/content/content-disposition.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fbca16af0..ca182139f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,6 +92,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES conference/session/media-session.h conference/session/port-config.h containers/lru-cache.h + content/content-disposition.h content/content-manager.h content/content-p.h content/content-type.h @@ -215,6 +216,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES conference/remote-conference.cpp conference/session/call-session.cpp conference/session/media-session.cpp + content/content-disposition.cpp content/content-manager.cpp content/content-type.cpp content/content.cpp diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index 579f44343..eb2d1f029 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -427,7 +427,7 @@ void ChatMessagePrivate::sendImdn (Imdn::Type imdnType, LinphoneReason reason) { shared_ptr msg = q->getChatRoom()->createChatMessage(); Content *content = new Content(); - content->setContentType("message/imdn+xml"); + content->setContentType(ContentType::Imdn); content->setBody(Imdn::createXml(imdnId, time, imdnType, reason)); msg->addContent(*content); diff --git a/src/chat/chat-room/chat-room.cpp b/src/chat/chat-room/chat-room.cpp index 1eb64ce00..28106b50d 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -194,12 +194,12 @@ LinphoneReason ChatRoomPrivate::onSipMessageReceived (SalOp *op, const SalMessag ); Content content; - if (message->url && strcmp(message->content_type, ContentType::ExternalBody.asString().c_str()) == 0) { + if (message->url && (ContentType(message->content_type) == ContentType::ExternalBody)) { lInfo() << "Received a message with an external body URL " << message->url; content.setContentType(ContentType::FileTransfer); content.setBody(msg->getPrivate()->createFakeFileTransferFromUrl(message->url)); } else { - content.setContentType(message->content_type); + content.setContentType(ContentType(message->content_type)); content.setBodyFromUtf8(message->text ? message->text : ""); } msg->setInternalContent(content); diff --git a/src/chat/chat-room/client-group-chat-room.cpp b/src/chat/chat-room/client-group-chat-room.cpp index 496be28ac..29c872d30 100644 --- a/src/chat/chat-room/client-group-chat-room.cpp +++ b/src/chat/chat-room/client-group-chat-room.cpp @@ -27,6 +27,8 @@ #include "conference/participant-p.h" #include "conference/remote-conference-p.h" #include "conference/session/call-session-p.h" +#include "content/content-disposition.h" +#include "content/content-type.h" #include "core/core-p.h" #include "logger/logger.h" #include "sal/refer-op.h" @@ -308,8 +310,8 @@ void ClientGroupChatRoom::addParticipants ( Content content; content.setBody(getResourceLists(addressesList)); - content.setContentType("application/resource-lists+xml"); - content.setContentDisposition("recipient-list"); + content.setContentType(ContentType::ResourceLists); + content.setContentDisposition(ContentDisposition::RecipientList); // TODO: Activate compression //if (linphone_core_content_encoding_supported(getCore()->getCCore(), "deflate")) // content.setContentEncoding("deflate"); diff --git a/src/chat/chat-room/server-group-chat-room-p.h b/src/chat/chat-room/server-group-chat-room-p.h index f31326ad0..911f31f67 100644 --- a/src/chat/chat-room/server-group-chat-room-p.h +++ b/src/chat/chat-room/server-group-chat-room-p.h @@ -68,7 +68,7 @@ public: private: struct Message { - Message (const std::string &from, const std::string &contentType, const std::string &text, const SalCustomHeader *salCustomHeaders) + Message (const std::string &from, const ContentType &contentType, const std::string &text, const SalCustomHeader *salCustomHeaders) : fromAddr(from) { content.setContentType(contentType); diff --git a/src/conference/local-conference.cpp b/src/conference/local-conference.cpp index fe06f6464..847400131 100644 --- a/src/conference/local-conference.cpp +++ b/src/conference/local-conference.cpp @@ -18,6 +18,7 @@ */ #include "content/content.h" +#include "content/content-disposition.h" #include "content/content-type.h" #include "handlers/local-conference-event-handler.h" #include "local-conference-p.h" @@ -70,7 +71,7 @@ void LocalConference::removeParticipant (const shared_ptr &pa list LocalConference::parseResourceLists (const Content &content) { if ((content.getContentType() == ContentType::ResourceLists) - && (content.getContentDisposition() == "recipient-list") + && (content.getContentDisposition() == ContentDisposition::RecipientList) ) { istringstream data(content.getBodyAsString()); unique_ptr rl(Xsd::ResourceLists::parseResourceLists( diff --git a/src/content/content-disposition.cpp b/src/content/content-disposition.cpp new file mode 100644 index 000000000..f8dc53ce4 --- /dev/null +++ b/src/content/content-disposition.cpp @@ -0,0 +1,86 @@ +/* + * content-disposition.cpp + * Copyright (C) 2010-2018 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 "linphone/utils/utils.h" + +#include "content-disposition.h" +#include "object/clonable-object-p.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +// ----------------------------------------------------------------------------- + +class ContentDispositionPrivate : public ClonableObjectPrivate { +public: + string disposition; +}; + +// ----------------------------------------------------------------------------- + +const ContentDisposition ContentDisposition::RecipientList("recipient-list"); + +// ----------------------------------------------------------------------------- + +ContentDisposition::ContentDisposition (const string &disposition) : ClonableObject(*new ContentDispositionPrivate) { + L_D(); + d->disposition = disposition; +} + +ContentDisposition::ContentDisposition (const ContentDisposition &other) + : ContentDisposition(other.getPrivate()->disposition) {} + +ContentDisposition &ContentDisposition::operator= (const ContentDisposition &other) { + L_D(); + if (this != &other) { + d->disposition = other.getPrivate()->disposition; + } + return *this; +} + +bool ContentDisposition::operator== (const ContentDisposition &other) const { + L_D(); + return d->disposition == other.getPrivate()->disposition; +} + +bool ContentDisposition::operator!= (const ContentDisposition &other) const { + return !(*this == other); +} + +bool ContentDisposition::isEmpty () const { + L_D(); + return d->disposition.empty(); +} + +bool ContentDisposition::isValid () const { + L_D(); + return !d->disposition.empty(); +} + +string ContentDisposition::asString () const { + L_D(); + if (isValid()) + return d->disposition; + return ""; +} + +LINPHONE_END_NAMESPACE diff --git a/src/content/content-disposition.h b/src/content/content-disposition.h new file mode 100644 index 000000000..c9668d379 --- /dev/null +++ b/src/content/content-disposition.h @@ -0,0 +1,59 @@ +/* + * content-disposition.h + * Copyright (C) 2010-2018 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 _L_CONTENT_DISPOSITION_H_ +#define _L_CONTENT_DISPOSITION_H_ + +#include "object/clonable-object.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class ContentDispositionPrivate; + +class LINPHONE_PUBLIC ContentDisposition : public ClonableObject { +public: + explicit ContentDisposition (const std::string &contentDisposition = ""); + ContentDisposition (const ContentDisposition &other); + + ContentDisposition &operator= (const ContentDisposition &other); + + bool operator== (const ContentDisposition &other) const; + bool operator!= (const ContentDisposition &other) const; + + // Delete these operators to prevent putting complicated content-disposition strings + // in the code. Instead define static const ContentDisposition objects below. + bool operator== (const std::string &other) const = delete; + bool operator!= (const std::string &other) const = delete; + + bool isEmpty () const; + bool isValid () const; + + std::string asString () const; + + static const ContentDisposition RecipientList; + +private: + L_DECLARE_PRIVATE(ContentDisposition); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_CONTENT_DISPOSITION_H_ diff --git a/src/content/content-p.h b/src/content/content-p.h index d0b8738c6..167712313 100644 --- a/src/content/content-p.h +++ b/src/content/content-p.h @@ -20,6 +20,7 @@ #ifndef _L_CONTENT_P_H_ #define _L_CONTENT_P_H_ +#include "content-disposition.h" #include "content-type.h" #include "content.h" #include "object/clonable-object-p.h" @@ -32,7 +33,7 @@ class ContentPrivate : public ClonableObjectPrivate { private: std::vector body; ContentType contentType; - std::string contentDisposition; + ContentDisposition contentDisposition; std::string contentEncoding; std::list> headers; diff --git a/src/content/content.cpp b/src/content/content.cpp index c538141cf..aa30503d4 100644 --- a/src/content/content.cpp +++ b/src/content/content.cpp @@ -110,17 +110,12 @@ void Content::setContentType (const ContentType &contentType) { d->contentType = contentType; } -void Content::setContentType (const string &contentType) { - L_D(); - d->contentType = ContentType(contentType); -} - -const string &Content::getContentDisposition () const { +const ContentDisposition &Content::getContentDisposition () const { L_D(); return d->contentDisposition; } -void Content::setContentDisposition (const string &contentDisposition) { +void Content::setContentDisposition (const ContentDisposition &contentDisposition) { L_D(); d->contentDisposition = contentDisposition; } diff --git a/src/content/content.h b/src/content/content.h index d3be11f51..c570670c5 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -32,6 +32,7 @@ L_DECL_C_STRUCT(LinphoneContent); LINPHONE_BEGIN_NAMESPACE +class ContentDisposition; class ContentType; class ContentPrivate; @@ -49,10 +50,9 @@ public: const ContentType &getContentType () const; void setContentType (const ContentType &contentType); - void setContentType (const std::string &contentType); - const std::string &getContentDisposition () const; - void setContentDisposition (const std::string &contentDisposition); + const ContentDisposition &getContentDisposition () const; + void setContentDisposition (const ContentDisposition &contentDisposition); const std::string &getContentEncoding () const; void setContentEncoding (const std::string &contentEncoding); diff --git a/src/sal/call-op.cpp b/src/sal/call-op.cpp index 5a8583479..a73c021ea 100644 --- a/src/sal/call-op.cpp +++ b/src/sal/call-op.cpp @@ -24,6 +24,7 @@ #include #include +#include "content/content-disposition.h" #include "content/content-type.h" using namespace std; @@ -102,7 +103,7 @@ belle_sip_header_allow_t *SalCallOp::create_allow(bool_t enable_update) { int SalCallOp::set_custom_body(belle_sip_message_t *msg, const Content &body) { ContentType contentType = body.getContentType(); - string contentDisposition = body.getContentDisposition(); + auto contentDisposition = body.getContentDisposition(); string contentEncoding = body.getContentEncoding(); size_t bodySize = body.getBody().size(); @@ -115,8 +116,10 @@ int SalCallOp::set_custom_body(belle_sip_message_t *msg, const Content &body) { belle_sip_header_content_type_t *content_type = belle_sip_header_content_type_create(contentType.getType().c_str(), contentType.getSubType().c_str()); belle_sip_message_add_header(msg, BELLE_SIP_HEADER(content_type)); } - if (!contentDisposition.empty()) { - belle_sip_header_content_disposition_t *contentDispositionHeader = belle_sip_header_content_disposition_create(contentDisposition.c_str()); + if (contentDisposition.isValid()) { + belle_sip_header_content_disposition_t *contentDispositionHeader = belle_sip_header_content_disposition_create( + contentDisposition.asString().c_str() + ); belle_sip_message_add_header(msg, BELLE_SIP_HEADER(contentDispositionHeader)); } if (!contentEncoding.empty()) @@ -243,7 +246,9 @@ Content SalCallOp::extract_body(belle_sip_message_t *message) { if (type_str && subtype_str) body.setContentType(ContentType(type_str, subtype_str)); if (contentDisposition) - body.setContentDisposition(belle_sip_header_content_disposition_get_content_disposition(contentDisposition)); + body.setContentDisposition( + ContentDisposition(belle_sip_header_content_disposition_get_content_disposition(contentDisposition)) + ); if (length > 0 && body_str) body.setBody(body_str, length); return body; } diff --git a/tester/multipart-tester.cpp b/tester/multipart-tester.cpp index ad5d731f3..3fdd9073a 100644 --- a/tester/multipart-tester.cpp +++ b/tester/multipart-tester.cpp @@ -50,7 +50,7 @@ static void chat_message_multipart_modifier_base(bool first_file_transfer, bool if (first_file_transfer) { char *send_filepath = bc_tester_res("sounds/sintel_trailer_opus_h264.mkv"); FileContent *content = new FileContent(); - content->setContentType("video/mkv"); + content->setContentType(ContentType("video/mkv")); content->setFilePath(send_filepath); content->setFileName("sintel_trailer_opus_h264.mkv"); marieMessage->addContent(*content); @@ -65,7 +65,7 @@ static void chat_message_multipart_modifier_base(bool first_file_transfer, bool if (second_file_transfer) { char *send_filepath = bc_tester_res("vcards/vcards.vcf"); FileContent *content = new FileContent(); - content->setContentType("file/vcf"); + content->setContentType(ContentType("file/vcf")); content->setFilePath(send_filepath); content->setFileName("vcards.vcf"); marieMessage->addContent(*content);