From 8a30c727dbe925e4783dddc078a1598773321871 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 21 Mar 2018 13:20:49 +0100 Subject: [PATCH] Improved parameter use in ContentType --- coreapi/bellesip_sal/sal_impl.c | 21 ++-- include/linphone/api/c-content.h | 8 ++ src/CMakeLists.txt | 2 + src/c-wrapper/api/c-content.cpp | 27 +++-- src/c-wrapper/internal/c-sal.h | 5 +- src/chat/chat-message/chat-message.cpp | 5 +- .../multipart-chat-message-modifier.cpp | 5 +- src/content/content-manager.cpp | 2 +- src/content/content-type.cpp | 91 ++++++++++++--- src/content/content-type.h | 16 ++- src/content/header-param.cpp | 105 ++++++++++++++++++ src/content/header-param.h | 61 ++++++++++ 12 files changed, 307 insertions(+), 41 deletions(-) create mode 100644 src/content/header-param.cpp create mode 100644 src/content/header-param.h diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 71578364b..99c859f52 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -348,23 +348,26 @@ void sal_body_handler_set_subtype(SalBodyHandler *body_handler, const char *subt belle_sip_header_content_type_set_subtype(content_type, subtype); } -char * sal_body_handler_get_content_type_parameters(const SalBodyHandler *body_handler) { +const belle_sip_list_t * sal_body_handler_get_content_type_parameters_names(const SalBodyHandler *body_handler) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { - char buff[2048]; - size_t buff_size = sizeof(buff); - size_t offset = 0; - belle_sip_parameters_marshal(BELLE_SIP_PARAMETERS(content_type), buff, buff_size, &offset); - buff[offset]='\0'; - return strdup(buff); + return belle_sip_parameters_get_parameter_names(BELLE_SIP_PARAMETERS(content_type)); } return NULL; } -void sal_body_handler_set_content_type_parameters(SalBodyHandler *body_handler, const char *params) { +const char * sal_body_handler_get_content_type_parameter(const SalBodyHandler *body_handler, const char *name) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { - belle_sip_parameters_set(BELLE_SIP_PARAMETERS(content_type), params); + return belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type), name); + } + return NULL; +} + +void sal_body_handler_set_content_type_parameter(SalBodyHandler *body_handler, const char *paramName, const char *paramValue) { + belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); + if (content_type != NULL) { + belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(content_type), paramName, paramValue); } } diff --git a/include/linphone/api/c-content.h b/include/linphone/api/c-content.h index fa030a309..5cdd27663 100644 --- a/include/linphone/api/c-content.h +++ b/include/linphone/api/c-content.h @@ -88,6 +88,14 @@ LINPHONE_PUBLIC const char * linphone_content_get_subtype(const LinphoneContent */ LINPHONE_PUBLIC void linphone_content_set_subtype(LinphoneContent *content, const char *subtype); +/** + * Adds a parameter to the ContentType header. + * @param[in] content LinphoneContent object. + * @param[in] name the name of the parameter to add. + * @param[in] value the value of the parameter to add. + */ +LINPHONE_PUBLIC void linphone_content_add_content_type_parameter(LinphoneContent *content, const char *name, const char *value); + /** * Get the content data buffer, usually a string. * @param[in] content LinphoneContent object. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 11377efe9..66d42b91e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -99,6 +99,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES content/content.h content/file-content.h content/file-transfer-content.h + content/header-param.h core/core-accessor.h core/core-listener.h core/core-p.h @@ -223,6 +224,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES content/content.cpp content/file-content.cpp content/file-transfer-content.cpp + content/header-param.cpp core/core-accessor.cpp core/core-call.cpp core/core-chat-room.cpp diff --git a/src/c-wrapper/api/c-content.cpp b/src/c-wrapper/api/c-content.cpp index 572444ddf..5e6c2e94a 100644 --- a/src/c-wrapper/api/c-content.cpp +++ b/src/c-wrapper/api/c-content.cpp @@ -24,6 +24,7 @@ #include "content/content.h" #include "content/content-type.h" +#include "content/header-param.h" #include "content/content-manager.h" #include "content/file-content.h" #include "content/file-transfer-content.h" @@ -90,6 +91,12 @@ void linphone_content_set_subtype(LinphoneContent *content, const char *subtype) L_GET_CPP_PTR_FROM_C_OBJECT(content)->setContentType(ct); } +void linphone_content_add_content_type_parameter(LinphoneContent *content, const char *name, const char *value) { + LinphonePrivate::ContentType ct = L_GET_CPP_PTR_FROM_C_OBJECT(content)->getContentType(); + ct.addParameter(L_C_TO_STRING(name), L_C_TO_STRING(value)); + L_GET_CPP_PTR_FROM_C_OBJECT(content)->setContentType(ct); +} + uint8_t * linphone_content_get_buffer(const LinphoneContent *content) { return (uint8_t *)linphone_content_get_string_buffer(content); } @@ -235,13 +242,15 @@ static LinphoneContent * linphone_content_new_with_body_handler(SalBodyHandler * L_SET_CPP_PTR_FROM_C_OBJECT(content, c); if (body_handler != NULL) { - LinphonePrivate::ContentType ct = c->getContentType(); - ct.setType(sal_body_handler_get_type(body_handler)); - ct.setSubType(sal_body_handler_get_subtype(body_handler)); - ct.setParameter(sal_body_handler_get_content_type_parameters(body_handler)); - c->setContentType(ct); + linphone_content_set_type(content, sal_body_handler_get_type(body_handler)); + linphone_content_set_subtype(content, sal_body_handler_get_subtype(body_handler)); + for (const belle_sip_list_t *params = sal_body_handler_get_content_type_parameters_names(body_handler); params; params = params->next) { + const char *paramName = (const char *)(params->data); + const char *paramValue = sal_body_handler_get_content_type_parameter(body_handler, paramName); + linphone_content_add_content_type_parameter(content, paramName, paramValue); + } - if (!sal_body_handler_is_multipart(body_handler)) { + if (!linphone_content_is_multipart(content)) { linphone_content_set_string_buffer(content, (char *)sal_body_handler_get_data(body_handler)); } else { belle_sip_multipart_body_handler_t *mpbh = BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler); @@ -302,7 +311,7 @@ SalBodyHandler * sal_body_handler_from_content(const LinphoneContent *content) { if (contentType.isMultipart()) { size_t size = linphone_content_get_size(content); char *buffer = ms_strdup(L_GET_CPP_PTR_FROM_C_OBJECT(content)->getBodyAsUtf8String().c_str()); - const char *boundary = L_STRING_TO_C(contentType.getParameter()); + const char *boundary = L_STRING_TO_C(contentType.getParameter("boundary").getValue()); belle_sip_multipart_body_handler_t *bh = belle_sip_multipart_body_handler_new_from_buffer(buffer, size, boundary); body_handler = (SalBodyHandler *)BELLE_SIP_BODY_HANDLER(bh); } else { @@ -323,7 +332,9 @@ SalBodyHandler * sal_body_handler_from_content(const LinphoneContent *content) { sal_body_handler_set_type(body_handler, contentType.getType().c_str()); sal_body_handler_set_subtype(body_handler, contentType.getSubType().c_str()); sal_body_handler_set_size(body_handler, linphone_content_get_size(content)); - sal_body_handler_set_content_type_parameters(body_handler, contentType.getParameter().c_str()); + for (const auto ¶m : contentType.getParameters()) { + sal_body_handler_set_content_type_parameter(body_handler, param.getName().c_str(), param.getValue().c_str()); + } if (content->encoding) sal_body_handler_set_encoding(body_handler, linphone_content_get_encoding(content)); return body_handler; diff --git a/src/c-wrapper/internal/c-sal.h b/src/c-wrapper/internal/c-sal.h index 3f53db90c..dd2e53f8f 100644 --- a/src/c-wrapper/internal/c-sal.h +++ b/src/c-wrapper/internal/c-sal.h @@ -636,8 +636,9 @@ const char * sal_body_handler_get_type(const SalBodyHandler *body_handler); void sal_body_handler_set_type(SalBodyHandler *body_handler, const char *type); const char * sal_body_handler_get_subtype(const SalBodyHandler *body_handler); void sal_body_handler_set_subtype(SalBodyHandler *body_handler, const char *subtype); -char * sal_body_handler_get_content_type_parameters(const SalBodyHandler *body_handler); -void sal_body_handler_set_content_type_parameters(SalBodyHandler *body_handler, const char *params); +const belle_sip_list_t * sal_body_handler_get_content_type_parameters_names(const SalBodyHandler *body_handler); +const char * sal_body_handler_get_content_type_parameter(const SalBodyHandler *body_handler, const char *name); +void sal_body_handler_set_content_type_parameter(SalBodyHandler *body_handler, const char *paramName, const char *paramValue); const char * sal_body_handler_get_encoding(const SalBodyHandler *body_handler); void sal_body_handler_set_encoding(SalBodyHandler *body_handler, const char *encoding); void * sal_body_handler_get_data(const SalBodyHandler *body_handler); diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index ca15d8c6a..ff2a595e7 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -36,6 +36,7 @@ #include "chat/modifier/file-transfer-chat-message-modifier.h" #include "chat/modifier/multipart-chat-message-modifier.h" #include "content/file-content.h" +#include "content/header-param.h" #include "content/content.h" #include "core/core.h" #include "core/core-p.h" @@ -450,7 +451,7 @@ static void forceUtf8Content (Content &content) { if (contentType != ContentType::PlainText) return; - string charset = contentType.getParameter(); + string charset = contentType.getParameter("charset").getValue(); if (charset.empty()) return; @@ -469,7 +470,7 @@ static void forceUtf8Content (Content &content) { if (!utf8Body.empty()) { // TODO: use move operator if possible in the future! content.setBodyFromUtf8(utf8Body); - contentType.setParameter(string(contentType.getParameter()).replace(begin, end - begin, "UTF-8")); + contentType.addParameter("charset", "UTF-8"); content.setContentType(contentType); } } diff --git a/src/chat/modifier/multipart-chat-message-modifier.cpp b/src/chat/modifier/multipart-chat-message-modifier.cpp index 5d0a34446..8e110119e 100644 --- a/src/chat/modifier/multipart-chat-message-modifier.cpp +++ b/src/chat/modifier/multipart-chat-message-modifier.cpp @@ -24,6 +24,7 @@ #include "chat/chat-message/chat-message.h" #include "chat/chat-room/chat-room.h" #include "content/content-type.h" +#include "content/header-param.h" #include "content/file-transfer-content.h" #include "logger/logger.h" #include "core/core.h" @@ -60,7 +61,7 @@ ChatMessageModifier::Result MultipartChatMessageModifier::encode ( Content newContent; ContentType newContentType(ContentType::Multipart); - newContentType.setParameter("boundary=" + boundary); + newContentType.addParameter("boundary", boundary); newContent.setContentType(newContentType); newContent.setBody(multipartMessage.str()); message->setInternalContent(newContent); @@ -70,7 +71,7 @@ ChatMessageModifier::Result MultipartChatMessageModifier::encode ( ChatMessageModifier::Result MultipartChatMessageModifier::decode (const shared_ptr &message, int &errorCode) { if (message->getInternalContent().getContentType().getType() == "multipart") { - string boundary = message->getInternalContent().getContentType().getParameter(); + string boundary = message->getInternalContent().getContentType().getParameter("boundary").getValue(); if (boundary.empty()) { lError() << "Boundary parameter of content-type not found: " << message->getInternalContent().getContentType().asString(); return ChatMessageModifier::Result::Error; diff --git a/src/content/content-manager.cpp b/src/content/content-manager.cpp index d4c286c79..8e3908310 100644 --- a/src/content/content-manager.cpp +++ b/src/content/content-manager.cpp @@ -112,7 +112,7 @@ Content ContentManager::contentListToMultipart (const list &contents) { belle_sip_object_unref(mpbh); ContentType contentType = ContentType::Multipart; - contentType.setParameter("boundary=" + string(MultipartBoundary)); + contentType.addParameter("boundary", string(MultipartBoundary)); content.setContentType(contentType); return content; diff --git a/src/content/content-type.cpp b/src/content/content-type.cpp index e457ff828..72fe78fd2 100644 --- a/src/content/content-type.cpp +++ b/src/content/content-type.cpp @@ -18,8 +18,10 @@ */ #include "linphone/utils/utils.h" +#include "linphone/utils/algorithm.h" #include "content-type.h" +#include "header-param.h" #include "object/clonable-object-p.h" // ============================================================================= @@ -34,7 +36,7 @@ class ContentTypePrivate : public ClonableObjectPrivate { public: string type; string subType; - string parameter; + std::list parameters; }; // ----------------------------------------------------------------------------- @@ -68,8 +70,15 @@ ContentType::ContentType (const string &contentType) : ClonableObject(*new Conte d->type.clear(); } - if (posParam != string::npos) - setParameter(Utils::trim(contentType.substr(posParam + 1))); + if (posParam != string::npos) { + string params = contentType.substr(posParam, end); + string token; + while ((pos = params.find(";")) != std::string::npos) { + token = params.substr(0, pos); + addParameter(HeaderParam(token)); + params.erase(0, pos + 1); + } + } } ContentType::ContentType (const string &type, const string &subType) : ClonableObject(*new ContentTypePrivate) { @@ -82,22 +91,34 @@ ContentType::ContentType (const string &type, const string &subType) : ClonableO ContentType::ContentType ( const string &type, const string &subType, - const string ¶meter + const HeaderParam ¶meter ) : ClonableObject(*new ContentTypePrivate) { L_D(); if (setType(type) && !setSubType(subType)) d->type.clear(); - setParameter(parameter); + addParameter(parameter); } -ContentType::ContentType (const ContentType &other) : ContentType(other.getType(), other.getSubType(), other.getParameter()) {} +ContentType::ContentType ( + const string &type, + const string &subType, + const std::list ¶meters +) : ClonableObject(*new ContentTypePrivate) { + L_D(); + + if (setType(type) && !setSubType(subType)) + d->type.clear(); + addParameters(parameters); +} + +ContentType::ContentType (const ContentType &other) : ContentType(other.getType(), other.getSubType(), other.getParameters()) {} ContentType &ContentType::operator= (const ContentType &other) { if (this != &other) { setType(other.getType()); setSubType(other.getSubType()); - setParameter(other.getParameter()); + addParameters(other.getParameters()); } return *this; @@ -106,7 +127,7 @@ ContentType &ContentType::operator= (const ContentType &other) { bool ContentType::operator== (const ContentType &other) const { return getType() == other.getType() && getSubType() == other.getSubType() && - getParameter() == other.getParameter(); + getParameters() == other.getParameters(); } bool ContentType::operator!= (const ContentType &other) const { @@ -141,14 +162,54 @@ bool ContentType::setSubType (const string &subType) { return false; } -const string &ContentType::getParameter () const { +const std::list &ContentType::getParameters () const { L_D(); - return d->parameter; + + return d->parameters; } -void ContentType::setParameter (const string ¶meter) { +void ContentType::addParameter (const std::string ¶mName, const std::string ¶mValue) { + addParameter(HeaderParam(paramName, paramValue)); +} + +void ContentType::addParameter (const HeaderParam ¶m) { L_D(); - d->parameter = parameter; + removeParameter(param); + d->parameters.push_back(param); +} + +void ContentType::addParameters(const std::list ¶ms) { + for (auto it = std::begin(params); it!=std::end(params); ++it) { + HeaderParam param = *it; + addParameter(param.getName(), param.getValue()); + } +} + +void ContentType::removeParameter (const std::string ¶mName) { + L_D(); + auto it = findParameter(paramName); + if (it != d->parameters.cend()) + d->parameters.remove(*it); +} + +void ContentType::removeParameter (const HeaderParam ¶m) { + removeParameter(param.getName()); +} + +std::list::const_iterator ContentType::findParameter (const std::string ¶mName) const { + L_D(); + return findIf(d->parameters, [¶mName](const HeaderParam ¶m) { + return param.getName() == paramName; + }); +} + +const HeaderParam &ContentType::getParameter (const std::string ¶mName) const { + L_D(); + std::list::const_iterator it = findParameter(paramName); + if (it != d->parameters.cend()) { + return *it; + } + return Utils::getEmptyConstRefObject(); } bool ContentType::isEmpty () const { @@ -165,8 +226,10 @@ string ContentType::asString () const { L_D(); if (isValid()) { string asString = d->type + "/" + d->subType; - if (!d->parameter.empty()) - asString += "; " + d->parameter; + for (auto it = std::begin(getParameters()); it!=std::end(getParameters()); ++it) { + HeaderParam param = *it; + asString += param.asString(); + } return asString; } return ""; diff --git a/src/content/content-type.h b/src/content/content-type.h index bdf51c75d..3b155e4cd 100644 --- a/src/content/content-type.h +++ b/src/content/content-type.h @@ -20,6 +20,8 @@ #ifndef _L_CONTENT_TYPE_H_ #define _L_CONTENT_TYPE_H_ +#include + #include "object/clonable-object.h" // ============================================================================= @@ -27,12 +29,14 @@ LINPHONE_BEGIN_NAMESPACE class ContentTypePrivate; +class HeaderParam; class LINPHONE_PUBLIC ContentType : public ClonableObject { public: explicit ContentType (const std::string &contentType = ""); ContentType (const std::string &type, const std::string &subType); - ContentType (const std::string &type, const std::string &subType, const std::string ¶meter); + ContentType (const std::string &type, const std::string &subType, const HeaderParam ¶meter); + ContentType (const std::string &type, const std::string &subType, const std::list ¶meters); ContentType (const ContentType &other); ContentType &operator= (const ContentType &other); @@ -55,8 +59,14 @@ public: const std::string &getSubType () const; bool setSubType (const std::string &subType); - const std::string &getParameter () const; - void setParameter (const std::string ¶meter); + const std::list &getParameters () const; + void addParameter (const std::string ¶mName, const std::string ¶mValue); + void addParameter (const HeaderParam ¶m); + void addParameters(const std::list ¶ms); + void removeParameter (const std::string ¶mName); + void removeParameter (const HeaderParam ¶m); + std::list::const_iterator findParameter (const std::string ¶mName) const; + const HeaderParam &getParameter (const std::string ¶mName) const; std::string asString () const; diff --git a/src/content/header-param.cpp b/src/content/header-param.cpp new file mode 100644 index 000000000..31c89caa5 --- /dev/null +++ b/src/content/header-param.cpp @@ -0,0 +1,105 @@ +/* + * header-param.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 "header-param.h" +#include "object/clonable-object-p.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +// ----------------------------------------------------------------------------- + +class HeaderParamPrivate : public ClonableObjectPrivate { +public: + string name; + string value; +}; + +// ----------------------------------------------------------------------------- + +HeaderParam::HeaderParam (const string ¶m) : ClonableObject(*new HeaderParamPrivate) { + size_t pos = param.find("="); + size_t end = param.length(); + + if (pos == string::npos) + return; + + setName(param.substr(0, pos)); + setValue(param.substr(pos + 1, end)); +} + +HeaderParam::HeaderParam (const string &name, const string &value) : ClonableObject(*new HeaderParamPrivate) { + setName(name); + setValue(value); +} + +HeaderParam::HeaderParam (const HeaderParam &other) : HeaderParam(other.getName(), other.getValue()) {} + +HeaderParam &HeaderParam::operator= (const HeaderParam &other) { + if (this != &other) { + setName(other.getName()); + setValue(other.getValue()); + } + + return *this; +} + +bool HeaderParam::operator== (const HeaderParam &other) const { + return getName() == other.getName() && + getValue() == other.getValue(); +} + +bool HeaderParam::operator!= (const HeaderParam &other) const { + return !(*this == other); +} + +const string &HeaderParam::getName () const { + L_D(); + return d->name; +} + +bool HeaderParam::setName (const string &name) { + L_D(); + d->name = name; + return true; +} + +const string &HeaderParam::getValue () const { + L_D(); + return d->value; +} + +bool HeaderParam::setValue (const string &value) { + L_D(); + d->value = value; + return true; +} + +string HeaderParam::asString () const { + L_D(); + string asString = ";" + d->name + "=" + d->value; + return asString; +} + +LINPHONE_END_NAMESPACE diff --git a/src/content/header-param.h b/src/content/header-param.h new file mode 100644 index 000000000..a9471b486 --- /dev/null +++ b/src/content/header-param.h @@ -0,0 +1,61 @@ +/* + * header-param.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_HEADER_PARAM_H_ +#define _L_HEADER_PARAM_H_ + +#include "object/clonable-object.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class HeaderParamPrivate; + +class LINPHONE_PUBLIC HeaderParam : public ClonableObject { +public: + explicit HeaderParam (const std::string &header = ""); + HeaderParam (const std::string &name, const std::string &value); + HeaderParam (const HeaderParam &other); + + HeaderParam &operator= (const HeaderParam &other); + + bool operator== (const HeaderParam &other) const; + bool operator!= (const HeaderParam &other) const; + + // Delete these operators to prevent putting complicated content-type strings + // in the code. Instead define static const HeaderParam objects below. + bool operator== (const std::string &other) const = delete; + bool operator!= (const std::string &other) const = delete; + + const std::string &getName () const; + bool setName (const std::string &name); + + const std::string &getValue () const; + bool setValue (const std::string &value); + + std::string asString () const; + +private: + L_DECLARE_PRIVATE(HeaderParam); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_HEADER_PARAM_H_