From 7e5ee05263388560205d0a0d88a0f737a2e03099 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 26 Mar 2018 17:19:04 +0200 Subject: [PATCH 1/7] Improved b64 tests --- tester/message_tester.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tester/message_tester.c b/tester/message_tester.c index 396f4b9c4..8922adf20 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -2294,15 +2294,19 @@ void text_message_with_custom_content_type_and_lime(void) { static int im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) { + ms_debug("IM encryption process incoming message with content type %s", linphone_chat_message_get_content_type(msg)); if (linphone_chat_message_get_content_type(msg)) { if (strcmp(linphone_chat_message_get_content_type(msg), "cipher/b64") == 0) { size_t b64Size = 0; unsigned char *output; - bctbx_base64_decode(NULL, &b64Size, (unsigned char *)linphone_chat_message_get_text(msg), strlen(linphone_chat_message_get_text(msg))); + const char *data = linphone_chat_message_get_text(msg); + ms_debug("IM encryption process incoming message crypted message is %s", data); + bctbx_base64_decode(NULL, &b64Size, (unsigned char *)data, strlen(data)); output = (unsigned char *)ms_malloc(b64Size+1), - bctbx_base64_decode(output, &b64Size, (unsigned char *)linphone_chat_message_get_text(msg), strlen(linphone_chat_message_get_text(msg))); + bctbx_base64_decode(output, &b64Size, (unsigned char *)data, strlen(data)); output[b64Size] = '\0'; linphone_chat_message_set_text(msg, (char *)output); + ms_debug("IM encryption process incoming message decrypted message is %s", output); ms_free(output); linphone_chat_message_set_content_type(msg, "text/plain"); return 0; From 2081f71c78aeef2a3b6605b79669b06c5e8c6a7e Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 27 Mar 2018 10:10:22 +0200 Subject: [PATCH 2/7] Added more tests for ContentType parsing --- tester/content-manager-tester.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tester/content-manager-tester.cpp b/tester/content-manager-tester.cpp index 9509b5442..296231086 100644 --- a/tester/content-manager-tester.cpp +++ b/tester/content-manager-tester.cpp @@ -21,6 +21,7 @@ #include "content/content-manager.h" #include "content/content-type.h" #include "content/content.h" +#include "content/header/header-param.h" #include "liblinphone_tester.h" #include "tester_utils.h" @@ -353,10 +354,31 @@ void list_to_multipart () { } static void content_type_parsing(void) { - const string type = "message/external-body;access-type=URL;URL=\"https://www.linphone.org/img/linphone-open-source-voip-projectX2.png\""; + string type = "message/external-body;access-type=URL;URL=\"https://www.linphone.org/img/linphone-open-source-voip-projectX2.png\""; ContentType contentType = ContentType(type); BC_ASSERT_STRING_EQUAL("message", contentType.getType().c_str()); BC_ASSERT_STRING_EQUAL("external-body", contentType.getSubType().c_str()); + BC_ASSERT_STRING_EQUAL("URL", contentType.getParameter("access-type").getValue().c_str()); + BC_ASSERT_STRING_EQUAL("\"https://www.linphone.org/img/linphone-open-source-voip-projectX2.png\"", contentType.getParameter("URL").getValue().c_str()); + BC_ASSERT_STRING_EQUAL("", contentType.getParameter("boundary").getValue().c_str()); + BC_ASSERT_EQUAL(2, contentType.getParameters().size(), int, "%d"); + BC_ASSERT_TRUE(type == contentType.asString()); + + type = "multipart/mixed;boundary=-----------------------------14737809831466499882746641450"; + contentType = ContentType(type); + BC_ASSERT_STRING_EQUAL("multipart", contentType.getType().c_str()); + BC_ASSERT_STRING_EQUAL("mixed", contentType.getSubType().c_str()); + BC_ASSERT_STRING_EQUAL("-----------------------------14737809831466499882746641450", contentType.getParameter("boundary").getValue().c_str()); + BC_ASSERT_STRING_EQUAL("", contentType.getParameter("access-type").getValue().c_str()); + BC_ASSERT_EQUAL(1, contentType.getParameters().size(), int, "%d"); + BC_ASSERT_TRUE(type == contentType.asString()); + + type = "plain/text"; + contentType = ContentType(type); + BC_ASSERT_STRING_EQUAL("plain", contentType.getType().c_str()); + BC_ASSERT_STRING_EQUAL("text", contentType.getSubType().c_str()); + BC_ASSERT_STRING_EQUAL("", contentType.getParameter("boundary").getValue().c_str()); + BC_ASSERT_EQUAL(0, contentType.getParameters().size(), int, "%d"); BC_ASSERT_TRUE(type == contentType.asString()); } From 813ab614ebdcdadf79af64a9dbde9c12cc3644da Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 27 Mar 2018 11:48:40 +0200 Subject: [PATCH 3/7] Use Header class instead of pair in Content + improved tester --- coreapi/bellesip_sal/sal_impl.c | 4 ++ src/c-wrapper/api/c-content.cpp | 15 +++-- src/c-wrapper/internal/c-sal.h | 1 + .../multipart-chat-message-modifier.cpp | 5 +- src/content/content-manager.cpp | 8 +-- src/content/content-p.h | 4 +- src/content/content-type.cpp | 3 +- src/content/content.cpp | 18 ++++-- src/content/content.h | 6 +- src/content/header/header-p.h | 2 + src/content/header/header.cpp | 62 ++++++++++++++++++- src/content/header/header.h | 19 +++++- tester/content-manager-tester.cpp | 26 +++++--- 13 files changed, 140 insertions(+), 33 deletions(-) diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 771155918..bdf3683dd 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -446,3 +446,7 @@ const char * sal_body_handler_get_header(const SalBodyHandler *body_handler, con } return NULL; } + +const belle_sip_list_t* sal_body_handler_get_headers(const SalBodyHandler *body_handler) { + return belle_sip_body_handler_get_headers(BELLE_SIP_BODY_HANDLER(body_handler)); +} diff --git a/src/c-wrapper/api/c-content.cpp b/src/c-wrapper/api/c-content.cpp index 2b68cbbde..580c78ca3 100644 --- a/src/c-wrapper/api/c-content.cpp +++ b/src/c-wrapper/api/c-content.cpp @@ -25,6 +25,7 @@ #include "content/content.h" #include "content/content-type.h" #include "content/header/header-param.h" +#include "content/header/header.h" #include "content/content-manager.h" #include "content/file-content.h" #include "content/file-transfer-content.h" @@ -259,6 +260,13 @@ static LinphoneContent * linphone_content_new_with_body_handler(SalBodyHandler * belle_sip_free(body); } + belle_sip_list_t *headers = (belle_sip_list_t *)sal_body_handler_get_headers(body_handler); + while (headers) { + belle_sip_header_t *cHeader = BELLE_SIP_HEADER(headers->data); + LinphonePrivate::Header header = LinphonePrivate::Header(belle_sip_header_get_name(cHeader), belle_sip_header_get_unparsed_value(cHeader)); + L_GET_CPP_PTR_FROM_C_OBJECT(content)->addHeader(header); + headers = headers->next; + } if (sal_body_handler_get_encoding(body_handler)) linphone_content_set_encoding(content, sal_body_handler_get_encoding(body_handler)); } @@ -307,12 +315,7 @@ SalBodyHandler * sal_body_handler_from_content(const LinphoneContent *content) { } for (const auto &header : L_GET_CPP_PTR_FROM_C_OBJECT(content)->getHeaders()) { - belle_sip_header_t *additionalHeader = BELLE_SIP_HEADER( - belle_sip_header_create( - header.first.c_str(), - header.second.c_str() - ) - ); + belle_sip_header_t *additionalHeader = belle_sip_header_parse(header.asString().c_str()); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), additionalHeader); } diff --git a/src/c-wrapper/internal/c-sal.h b/src/c-wrapper/internal/c-sal.h index ebf373229..f34c4ee5f 100644 --- a/src/c-wrapper/internal/c-sal.h +++ b/src/c-wrapper/internal/c-sal.h @@ -650,6 +650,7 @@ SalBodyHandler * sal_body_handler_get_part(const SalBodyHandler *body_handler, i const belle_sip_list_t * sal_body_handler_get_parts(const SalBodyHandler *body_handler); SalBodyHandler * sal_body_handler_find_part_by_header(const SalBodyHandler *body_handler, const char *header_name, const char *header_value); const char * sal_body_handler_get_header(const SalBodyHandler *body_handler, const char *header_name); +const belle_sip_list_t* sal_body_handler_get_headers(const SalBodyHandler *body_handler); /*this function parses a document with key=value pairs separated by new lines, and extracts the value for a given key*/ int sal_lines_get_value(const char *data, const char *key, char *value, size_t value_size); diff --git a/src/chat/modifier/multipart-chat-message-modifier.cpp b/src/chat/modifier/multipart-chat-message-modifier.cpp index 13041bdb4..8ed8312d9 100644 --- a/src/chat/modifier/multipart-chat-message-modifier.cpp +++ b/src/chat/modifier/multipart-chat-message-modifier.cpp @@ -22,6 +22,7 @@ #include "chat/chat-message/chat-message.h" #include "content/content-type.h" +#include "content/header/header.h" #include "content/content-manager.h" #include "content/file-transfer-content.h" @@ -55,8 +56,8 @@ ChatMessageModifier::Result MultipartChatMessageModifier::decode (const shared_p content->setContentType(c.getContentType()); content->setContentDisposition(c.getContentDisposition()); content->setContentEncoding(c.getContentEncoding()); - for (const pair &pair : c.getHeaders()) { - content->addHeader(pair.first, pair.second); + for (const Header &header : c.getHeaders()) { + content->addHeader(header); } content->setBodyFromUtf8(c.getBodyAsUtf8String()); } else { diff --git a/src/content/content-manager.cpp b/src/content/content-manager.cpp index cbecfaa83..ff341bcf2 100644 --- a/src/content/content-manager.cpp +++ b/src/content/content-manager.cpp @@ -29,14 +29,14 @@ // ============================================================================= -using namespace std; - -LINPHONE_BEGIN_NAMESPACE - namespace { constexpr const char MultipartBoundary[] = "---------------------------14737809831466499882746641449"; } +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + // ----------------------------------------------------------------------------- list ContentManager::multipartToContentList (const Content &content) { diff --git a/src/content/content-p.h b/src/content/content-p.h index 167712313..9dc4e1d31 100644 --- a/src/content/content-p.h +++ b/src/content/content-p.h @@ -29,13 +29,15 @@ LINPHONE_BEGIN_NAMESPACE +class Header; + class ContentPrivate : public ClonableObjectPrivate { private: std::vector body; ContentType contentType; ContentDisposition contentDisposition; std::string contentEncoding; - std::list> headers; + std::list
headers; L_DECLARE_PUBLIC(Content); }; diff --git a/src/content/content-type.cpp b/src/content/content-type.cpp index 4f357f3ee..35391783b 100644 --- a/src/content/content-type.cpp +++ b/src/content/content-type.cpp @@ -179,8 +179,7 @@ string ContentType::asString () const { L_D(); if (isValid()) { string asString = d->type + "/" + d->subType; - for (auto it = std::begin(getParameters()); it!=std::end(getParameters()); ++it) { - HeaderParam param = *it; + for (const auto ¶m : getParameters()) { asString += param.asString(); } return asString; diff --git a/src/content/content.cpp b/src/content/content.cpp index f27e731d6..91761e268 100644 --- a/src/content/content.cpp +++ b/src/content/content.cpp @@ -25,6 +25,7 @@ #include "content-p.h" #include "content-type.h" +#include "header/header.h" // ============================================================================= @@ -197,10 +198,17 @@ bool Content::isFileTransfer () const { void Content::addHeader (const string &headerName, const string &headerValue) { L_D(); removeHeader(headerName); - d->headers.push_back(make_pair(headerName, headerValue)); + Header header = Header(headerName, headerValue); + d->headers.push_back(header); } -const list> &Content::getHeaders () const { +void Content::addHeader (const Header &header) { + L_D(); + removeHeader(header.getName()); + d->headers.push_back(header); +} + +const list
&Content::getHeaders () const { L_D(); return d->headers; } @@ -212,10 +220,10 @@ void Content::removeHeader (const string &headerName) { d->headers.remove(*it); } -list>::const_iterator Content::findHeader (const string &headerName) const { +list
::const_iterator Content::findHeader (const string &headerName) const { L_D(); - return findIf(d->headers, [&headerName](const pair &pair) { - return pair.first == headerName; + return findIf(d->headers, [&headerName](const Header &header) { + return header.getName() == headerName; }); } diff --git a/src/content/content.h b/src/content/content.h index 94d832643..50066133f 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -35,6 +35,7 @@ LINPHONE_BEGIN_NAMESPACE class ContentDisposition; class ContentType; class ContentPrivate; +class Header; class LINPHONE_PUBLIC Content : public ClonableObject, public AppDataContainer { public: @@ -76,10 +77,11 @@ public: virtual bool isFile () const; virtual bool isFileTransfer () const; - const std::list> &getHeaders () const; + const std::list
&getHeaders () const; void addHeader (const std::string &headerName, const std::string &headerValue); + void addHeader (const Header &header); void removeHeader (const std::string &headerName); - std::list>::const_iterator findHeader (const std::string &headerName) const; + std::list
::const_iterator findHeader (const std::string &headerName) const; protected: explicit Content (ContentPrivate &p); diff --git a/src/content/header/header-p.h b/src/content/header/header-p.h index 3af1e3688..fd4663efe 100644 --- a/src/content/header/header-p.h +++ b/src/content/header/header-p.h @@ -32,6 +32,8 @@ LINPHONE_BEGIN_NAMESPACE class HeaderPrivate : public ClonableObjectPrivate { private: + std::string name; + std::string value; std::list parameters; L_DECLARE_PUBLIC(Header); }; diff --git a/src/content/header/header.cpp b/src/content/header/header.cpp index 0c8017e60..561b7ce05 100644 --- a/src/content/header/header.cpp +++ b/src/content/header/header.cpp @@ -35,14 +35,64 @@ Header::Header(HeaderPrivate &p) : ClonableObject(p) { } -void Header::cleanParameters() { +Header::Header (const string &name, const string &value) : ClonableObject(*new HeaderPrivate) { + setName(name); + setValue(value); +} + +Header::Header (const string &name, const string &value, const list ¶ms) : Header(name, value) { + addParameters(params); +} + +Header::Header (const Header &other) : Header(other.getName(), other.getValue(), other.getParameters()) {} + +Header &Header::operator= (const Header &other) { + if (this != &other) { + setName(other.getName()); + setValue(other.getValue()); + cleanParameters(); + addParameters(other.getParameters()); + } + + return *this; +} + +bool Header::operator== (const Header &other) const { + return getName() == other.getName() && + getValue() == other.getValue(); +} + +bool Header::operator!= (const Header &other) const { + return !(*this == other); +} + +void Header::setName (const string &name) { + L_D(); + d->name = name; +} + +string Header::getName () const { + L_D(); + return d->name; +} + +void Header::setValue (const string &value) { + L_D(); + d->value = value; +} + +string Header::getValue () const { + L_D(); + return d->value; +} + +void Header::cleanParameters () { L_D(); d->parameters.clear(); } const std::list &Header::getParameters () const { L_D(); - return d->parameters; } @@ -90,4 +140,12 @@ const HeaderParam &Header::getParameter (const std::string ¶mName) const { return Utils::getEmptyConstRefObject(); } +string Header::asString () const { + string asString = getName() + ":" + getValue(); + for (const auto ¶m : getParameters()) { + asString += param.asString(); + } + return asString; +} + LINPHONE_END_NAMESPACE \ No newline at end of file diff --git a/src/content/header/header.h b/src/content/header/header.h index 822839516..a0b8083fe 100644 --- a/src/content/header/header.h +++ b/src/content/header/header.h @@ -33,7 +33,22 @@ class HeaderParam; class LINPHONE_PUBLIC Header : public ClonableObject { public: - void cleanParameters(); + Header (const std::string &name, const std::string &value); + Header (const std::string &name, const std::string &value, const std::list ¶ms); + Header (const Header &other); + + Header &operator= (const Header &other); + + bool operator== (const Header &other) const; + bool operator!= (const Header &other) const; + + void setName (const std::string &name); + std::string getName () const; + + void setValue (const std::string &value); + std::string getValue () const; + + void cleanParameters (); const std::list &getParameters () const; void addParameter (const std::string ¶mName, const std::string ¶mValue); void addParameter (const HeaderParam ¶m); @@ -43,6 +58,8 @@ public: std::list::const_iterator findParameter (const std::string ¶mName) const; const HeaderParam &getParameter (const std::string ¶mName) const; + std::string asString () const; + protected: explicit Header (HeaderPrivate &p); diff --git a/tester/content-manager-tester.cpp b/tester/content-manager-tester.cpp index 296231086..24c4817d5 100644 --- a/tester/content-manager-tester.cpp +++ b/tester/content-manager-tester.cpp @@ -61,6 +61,7 @@ static const char* source_multipart = \ " " \ "" \ "-----------------------------14737809831466499882746641449\r\n" \ +"Content-Encoding: b64\r\n" \ "Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ "" \ "" \ @@ -78,6 +79,7 @@ static const char* source_multipart = \ " " \ "" \ "-----------------------------14737809831466499882746641449\r\n" \ +"Content-Id: toto;param1=value1;param2;param3=value3\r\n" \ "Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ "" \ "" \ @@ -98,8 +100,8 @@ static const char* source_multipart = \ static const char* generated_multipart = \ "-----------------------------14737809831466499882746641449\r\n" \ -"Content-Type: application/rlmi+xml;charset=\"UTF-8\"\r\n\r\n" \ -"Content-Length:582" \ +"Content-Type: application/rlmi+xml;charset=\"UTF-8\"\r\n" \ +"Content-Length:582\r\n\r\n" \ "" \ "" \ " " \ @@ -113,8 +115,8 @@ static const char* generated_multipart = \ " " \ "" \ "-----------------------------14737809831466499882746641449\r\n" \ -"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ -"Content-Length:561" \ +"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n" \ +"Content-Length:561\r\n\r\n" \ "" \ "" \ " " \ @@ -131,8 +133,9 @@ static const char* generated_multipart = \ " " \ "" \ "-----------------------------14737809831466499882746641449\r\n" \ -"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ -"Content-Length:561" \ +"Content-Encoding:b64\r\n" \ +"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n" \ +"Content-Length:561\r\n\r\n" \ "" \ "" \ " " \ @@ -149,8 +152,9 @@ static const char* generated_multipart = \ " " \ "" \ "-----------------------------14737809831466499882746641449\r\n" \ -"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ -"Content-Length:546" \ +"Content-Id:toto;param1=value1;param2;param3=value3\r\n" \ +"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n" \ +"Content-Length:546\r\n\r\n" \ "" \ "" \ " " \ @@ -326,8 +330,14 @@ void list_to_multipart () { content2.setContentType(contentType); Content content3; content3.setBody(part3); + content3.addHeader("Content-Encoding", "b64"); content3.setContentType(contentType); Content content4; + Header header = Header("Content-Id", "toto"); + header.addParameter("param1", "value1"); + header.addParameter("param2", ""); + header.addParameter("param3", "value3"); + content4.addHeader(header); content4.setBody(part4); content4.setContentType(contentType); list contents = {&content1, &content2, &content3, &content4}; From 646cff8460b50d146b0f407841e9f4f233c4d98e Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 27 Mar 2018 12:16:42 +0200 Subject: [PATCH 4/7] Improved presence tester, now more reliable --- tester/presence_server_tester.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tester/presence_server_tester.c b/tester/presence_server_tester.c index 62bb8a364..4aa3b4ee8 100644 --- a/tester/presence_server_tester.c +++ b/tester/presence_server_tester.c @@ -430,8 +430,10 @@ static void test_presence_list_base(bool_t enable_compression) { lcs = bctbx_list_append(lcs, pauline->lc); wait_for_list(lcs, &laure->stat.number_of_NotifyPresenceReceived, 2, 4000); - BC_ASSERT_EQUAL(laure->stat.number_of_NotifyPresenceReceived, 2, int, "%d"); - BC_ASSERT_EQUAL(linphone_friend_list_get_expected_notification_version(linphone_core_get_default_friend_list(laure->lc)), 1, int, "%d"); + BC_ASSERT_GREATER(laure->stat.number_of_NotifyPresenceReceived, 2, int, "%d"); + BC_ASSERT_LOWER(laure->stat.number_of_NotifyPresenceReceived, 3, int, "%d"); + BC_ASSERT_GREATER(linphone_friend_list_get_expected_notification_version(linphone_core_get_default_friend_list(laure->lc)), 1, int, "%d"); + BC_ASSERT_LOWER(linphone_friend_list_get_expected_notification_version(linphone_core_get_default_friend_list(laure->lc)), 2, int, "%d"); lf = linphone_friend_list_find_friend_by_address(linphone_core_get_default_friend_list(laure->lc), get_identity_address(marie)); if (!BC_ASSERT_PTR_NOT_NULL(lf)) goto end; BC_ASSERT_EQUAL(linphone_friend_get_status(lf), LinphoneStatusBusy, int, "%d"); @@ -485,7 +487,8 @@ static void test_presence_list_base(bool_t enable_compression) { /* The number of PresenceReceived events can be 3 or 4 here. TODO: ideally it should always be 3. */ BC_ASSERT_GREATER(laure->stat.number_of_NotifyPresenceReceived, 3, int, "%d"); BC_ASSERT_LOWER(laure->stat.number_of_NotifyPresenceReceived, 4, int, "%d"); - BC_ASSERT_EQUAL(linphone_friend_list_get_expected_notification_version(linphone_core_get_default_friend_list(laure->lc)), 2, int, "%d"); + BC_ASSERT_GREATER(linphone_friend_list_get_expected_notification_version(linphone_core_get_default_friend_list(laure->lc)), 2, int, "%d"); + BC_ASSERT_LOWER(linphone_friend_list_get_expected_notification_version(linphone_core_get_default_friend_list(laure->lc)), 3, int, "%d"); lf = linphone_friend_list_find_friend_by_address(linphone_core_get_default_friend_list(laure->lc), get_identity_address(marie)); BC_ASSERT_EQUAL(linphone_friend_get_status(lf), LinphoneStatusOnThePhone, int, "%d"); From 7009b68b9a149fe5484ddc36992dbedc9be8ad62 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 27 Mar 2018 12:45:47 +0200 Subject: [PATCH 5/7] Added << operator for ContentType and Header --- src/content/content-type.cpp | 13 +++++++++++++ src/content/content-type.h | 1 + src/content/header/header.cpp | 5 +++++ src/content/header/header.h | 1 + tester/content-manager-tester.cpp | 4 ++++ 5 files changed, 24 insertions(+) diff --git a/src/content/content-type.cpp b/src/content/content-type.cpp index 35391783b..e5d58c7a7 100644 --- a/src/content/content-type.cpp +++ b/src/content/content-type.cpp @@ -55,6 +55,7 @@ const ContentType ContentType::Sdp("application/sdp"); ContentType::ContentType (const string &contentType) : Header(*new ContentTypePrivate) { L_D(); + setName("Content-Type"); size_t pos = contentType.find('/'); size_t posParam = contentType.find(";"); size_t end = contentType.length(); @@ -82,11 +83,13 @@ ContentType::ContentType (const string &contentType) : Header(*new ContentTypePr params.erase(0, posParam + 1); } while (posParam != std::string::npos); } + setValue(d->type + "/" + d->subType); } ContentType::ContentType (const string &type, const string &subType) : Header(*new ContentTypePrivate) { L_D(); + setName("Content-Type"); if (setType(type) && !setSubType(subType)) d->type.clear(); } @@ -98,6 +101,7 @@ ContentType::ContentType ( ) : Header(*new ContentTypePrivate) { L_D(); + setName("Content-Type"); if (setType(type) && !setSubType(subType)) d->type.clear(); addParameter(parameter); @@ -110,6 +114,7 @@ ContentType::ContentType ( ) : Header(*new ContentTypePrivate) { L_D(); + setName("Content-Type"); if (setType(type) && !setSubType(subType)) d->type.clear(); addParameters(parameters); @@ -119,6 +124,7 @@ ContentType::ContentType (const ContentType &other) : ContentType(other.getType( ContentType &ContentType::operator= (const ContentType &other) { if (this != &other) { + setName("Content-Type"); setType(other.getType()); setSubType(other.getSubType()); cleanParameters(); @@ -146,6 +152,7 @@ bool ContentType::setType (const string &type) { L_D(); if (type.find('/') == string::npos) { d->type = Utils::stringToLower(type); + setValue(d->type + "/" + d->subType); return true; } return false; @@ -160,6 +167,7 @@ bool ContentType::setSubType (const string &subType) { L_D(); if (subType.find('/') == string::npos) { d->subType = Utils::stringToLower(subType); + setValue(d->type + "/" + d->subType); return true; } return false; @@ -187,6 +195,11 @@ string ContentType::asString () const { return ""; } +ostream &operator<<(ostream& stream, const ContentType& contentType) { + stream << contentType.asString(); + return stream; +} + bool ContentType::isMultipart() const { return getType() == "multipart"; } diff --git a/src/content/content-type.h b/src/content/content-type.h index 2ed267065..10f895e4d 100644 --- a/src/content/content-type.h +++ b/src/content/content-type.h @@ -59,6 +59,7 @@ public: bool setSubType (const std::string &subType); std::string asString () const; + friend std::ostream &operator<<(std::ostream&, const ContentType&); bool isMultipart() const; diff --git a/src/content/header/header.cpp b/src/content/header/header.cpp index 561b7ce05..14ce99f85 100644 --- a/src/content/header/header.cpp +++ b/src/content/header/header.cpp @@ -148,4 +148,9 @@ string Header::asString () const { return asString; } +ostream &operator<<(ostream& stream, const Header& header) { + stream << header.asString(); + return stream; +} + LINPHONE_END_NAMESPACE \ No newline at end of file diff --git a/src/content/header/header.h b/src/content/header/header.h index a0b8083fe..5d111900d 100644 --- a/src/content/header/header.h +++ b/src/content/header/header.h @@ -59,6 +59,7 @@ public: const HeaderParam &getParameter (const std::string ¶mName) const; std::string asString () const; + friend std::ostream &operator<<(std::ostream&, const Header&); protected: explicit Header (HeaderPrivate &p); diff --git a/tester/content-manager-tester.cpp b/tester/content-manager-tester.cpp index 24c4817d5..52d461ef9 100644 --- a/tester/content-manager-tester.cpp +++ b/tester/content-manager-tester.cpp @@ -24,6 +24,7 @@ #include "content/header/header-param.h" #include "liblinphone_tester.h" #include "tester_utils.h" +#include "logger/logger.h" using namespace LinphonePrivate; using namespace std; @@ -372,6 +373,7 @@ static void content_type_parsing(void) { BC_ASSERT_STRING_EQUAL("\"https://www.linphone.org/img/linphone-open-source-voip-projectX2.png\"", contentType.getParameter("URL").getValue().c_str()); BC_ASSERT_STRING_EQUAL("", contentType.getParameter("boundary").getValue().c_str()); BC_ASSERT_EQUAL(2, contentType.getParameters().size(), int, "%d"); + lInfo() << "Content-Type is " << contentType; BC_ASSERT_TRUE(type == contentType.asString()); type = "multipart/mixed;boundary=-----------------------------14737809831466499882746641450"; @@ -381,6 +383,7 @@ static void content_type_parsing(void) { BC_ASSERT_STRING_EQUAL("-----------------------------14737809831466499882746641450", contentType.getParameter("boundary").getValue().c_str()); BC_ASSERT_STRING_EQUAL("", contentType.getParameter("access-type").getValue().c_str()); BC_ASSERT_EQUAL(1, contentType.getParameters().size(), int, "%d"); + lInfo() << "Content-Type is " << contentType; BC_ASSERT_TRUE(type == contentType.asString()); type = "plain/text"; @@ -389,6 +392,7 @@ static void content_type_parsing(void) { BC_ASSERT_STRING_EQUAL("text", contentType.getSubType().c_str()); BC_ASSERT_STRING_EQUAL("", contentType.getParameter("boundary").getValue().c_str()); BC_ASSERT_EQUAL(0, contentType.getParameters().size(), int, "%d"); + lInfo() << "Content-Type is " << contentType; BC_ASSERT_TRUE(type == contentType.asString()); } From af2a607014a341da246174f5c914a697880e8d47 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 27 Mar 2018 14:39:32 +0200 Subject: [PATCH 6/7] Improved ContentType's asString() method --- src/chat/chat-message/chat-message.cpp | 2 +- .../modifier/cpim-chat-message-modifier.cpp | 2 +- src/content/content-type.cpp | 22 ------------------- src/content/content-type.h | 3 --- src/content/header/header.cpp | 12 +++++++--- 5 files changed, 11 insertions(+), 30 deletions(-) diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index 26232bbeb..ddfd018a6 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -582,7 +582,7 @@ LinphoneReason ChatMessagePrivate::receive () { foundSupportContentType = true; break; } else - lError() << "Unsupported content-type: " << c->getContentType().asString(); + lError() << "Unsupported content-type: " << c->getContentType(); } if (!foundSupportContentType) { diff --git a/src/chat/modifier/cpim-chat-message-modifier.cpp b/src/chat/modifier/cpim-chat-message-modifier.cpp index 8958c0f82..03c35fed0 100644 --- a/src/chat/modifier/cpim-chat-message-modifier.cpp +++ b/src/chat/modifier/cpim-chat-message-modifier.cpp @@ -98,7 +98,7 @@ ChatMessageModifier::Result CpimChatMessageModifier::decode (const shared_ptrgetContents().front(); if (content->getContentType() != ContentType::Cpim) { - lError() << "[CPIM] Message is not CPIM but " << content->getContentType().asString(); + lError() << "[CPIM] Message is not CPIM but " << content->getContentType(); return ChatMessageModifier::Result::Skipped; } diff --git a/src/content/content-type.cpp b/src/content/content-type.cpp index e5d58c7a7..3d6b4689f 100644 --- a/src/content/content-type.cpp +++ b/src/content/content-type.cpp @@ -55,7 +55,6 @@ const ContentType ContentType::Sdp("application/sdp"); ContentType::ContentType (const string &contentType) : Header(*new ContentTypePrivate) { L_D(); - setName("Content-Type"); size_t pos = contentType.find('/'); size_t posParam = contentType.find(";"); size_t end = contentType.length(); @@ -89,7 +88,6 @@ ContentType::ContentType (const string &contentType) : Header(*new ContentTypePr ContentType::ContentType (const string &type, const string &subType) : Header(*new ContentTypePrivate) { L_D(); - setName("Content-Type"); if (setType(type) && !setSubType(subType)) d->type.clear(); } @@ -101,7 +99,6 @@ ContentType::ContentType ( ) : Header(*new ContentTypePrivate) { L_D(); - setName("Content-Type"); if (setType(type) && !setSubType(subType)) d->type.clear(); addParameter(parameter); @@ -114,7 +111,6 @@ ContentType::ContentType ( ) : Header(*new ContentTypePrivate) { L_D(); - setName("Content-Type"); if (setType(type) && !setSubType(subType)) d->type.clear(); addParameters(parameters); @@ -124,7 +120,6 @@ ContentType::ContentType (const ContentType &other) : ContentType(other.getType( ContentType &ContentType::operator= (const ContentType &other) { if (this != &other) { - setName("Content-Type"); setType(other.getType()); setSubType(other.getSubType()); cleanParameters(); @@ -183,23 +178,6 @@ bool ContentType::isValid () const { return !d->type.empty() && !d->subType.empty(); } -string ContentType::asString () const { - L_D(); - if (isValid()) { - string asString = d->type + "/" + d->subType; - for (const auto ¶m : getParameters()) { - asString += param.asString(); - } - return asString; - } - return ""; -} - -ostream &operator<<(ostream& stream, const ContentType& contentType) { - stream << contentType.asString(); - return stream; -} - bool ContentType::isMultipart() const { return getType() == "multipart"; } diff --git a/src/content/content-type.h b/src/content/content-type.h index 10f895e4d..c9aa7af77 100644 --- a/src/content/content-type.h +++ b/src/content/content-type.h @@ -58,9 +58,6 @@ public: const std::string &getSubType () const; bool setSubType (const std::string &subType); - std::string asString () const; - friend std::ostream &operator<<(std::ostream&, const ContentType&); - bool isMultipart() const; static bool isFile (const ContentType &contentType); diff --git a/src/content/header/header.cpp b/src/content/header/header.cpp index 14ce99f85..92920e024 100644 --- a/src/content/header/header.cpp +++ b/src/content/header/header.cpp @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + #include "linphone/utils/utils.h" #include "linphone/utils/algorithm.h" @@ -141,11 +143,15 @@ const HeaderParam &Header::getParameter (const std::string ¶mName) const { } string Header::asString () const { - string asString = getName() + ":" + getValue(); + stringstream asString; + if (!getName().empty()) { + asString << getName() << ":"; + } + asString << getValue(); for (const auto ¶m : getParameters()) { - asString += param.asString(); + asString << param.asString(); } - return asString; + return asString.str(); } ostream &operator<<(ostream& stream, const Header& header) { From ee88045383626abd452ba66dec64b3d6bd3e447e Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 27 Mar 2018 16:10:17 +0200 Subject: [PATCH 7/7] Renamed content_manager_tester + improved header parsing + new tests --- src/content/content-type.cpp | 1 - src/content/content.cpp | 9 ++++ src/content/content.h | 1 + src/content/header/header.cpp | 41 ++++++++++++++----- src/content/header/header.h | 1 + tester/CMakeLists.txt | 2 +- ...manager-tester.cpp => contents-tester.cpp} | 33 ++++++++++++--- tester/liblinphone_tester.h | 2 +- tester/tester.c | 2 +- 9 files changed, 73 insertions(+), 19 deletions(-) rename tester/{content-manager-tester.cpp => contents-tester.cpp} (92%) diff --git a/src/content/content-type.cpp b/src/content/content-type.cpp index 3d6b4689f..7aee05247 100644 --- a/src/content/content-type.cpp +++ b/src/content/content-type.cpp @@ -82,7 +82,6 @@ ContentType::ContentType (const string &contentType) : Header(*new ContentTypePr params.erase(0, posParam + 1); } while (posParam != std::string::npos); } - setValue(d->type + "/" + d->subType); } ContentType::ContentType (const string &type, const string &subType) : Header(*new ContentTypePrivate) { diff --git a/src/content/content.cpp b/src/content/content.cpp index 91761e268..ad74de27f 100644 --- a/src/content/content.cpp +++ b/src/content/content.cpp @@ -213,6 +213,15 @@ const list
&Content::getHeaders () const { return d->headers; } +const Header &Content::getHeader (const string &headerName) const { + L_D(); + list
::const_iterator it = findHeader(headerName); + if (it != d->headers.cend()) { + return *it; + } + return Utils::getEmptyConstRefObject
(); +} + void Content::removeHeader (const string &headerName) { L_D(); auto it = findHeader(headerName); diff --git a/src/content/content.h b/src/content/content.h index 50066133f..3c29d3a1a 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -78,6 +78,7 @@ public: virtual bool isFileTransfer () const; const std::list
&getHeaders () const; + const Header &getHeader (const std::string &headerName) const; void addHeader (const std::string &headerName, const std::string &headerValue); void addHeader (const Header &header); void removeHeader (const std::string &headerName); diff --git a/src/content/header/header.cpp b/src/content/header/header.cpp index 92920e024..5924cdf5b 100644 --- a/src/content/header/header.cpp +++ b/src/content/header/header.cpp @@ -33,13 +33,34 @@ LINPHONE_BEGIN_NAMESPACE // ----------------------------------------------------------------------------- -Header::Header(HeaderPrivate &p) : ClonableObject(p) { +Header::Header(HeaderPrivate &p) : ClonableObject(p) {} -} +Header::Header() : ClonableObject(*new HeaderPrivate) {} Header::Header (const string &name, const string &value) : ClonableObject(*new HeaderPrivate) { setName(name); - setValue(value); + + size_t posParam = value.find(";"); + if (posParam == string::npos) { + setValue(value); + return; + } + + string parsedValue = value.substr(0, posParam); + string params = value.substr(posParam + 1); + string token; + do { + posParam = params.find(";"); + if (posParam == string::npos) { + token = params; + } else { + token = params.substr(0, posParam); + } + addParameter(HeaderParam(token)); + params.erase(0, posParam + 1); + } while (posParam != std::string::npos); + + setValue(parsedValue); } Header::Header (const string &name, const string &value, const list ¶ms) : Header(name, value) { @@ -93,12 +114,12 @@ void Header::cleanParameters () { d->parameters.clear(); } -const std::list &Header::getParameters () const { +const list &Header::getParameters () const { L_D(); return d->parameters; } -void Header::addParameter (const std::string ¶mName, const std::string ¶mValue) { +void Header::addParameter (const string ¶mName, const string ¶mValue) { addParameter(HeaderParam(paramName, paramValue)); } @@ -108,14 +129,14 @@ void Header::addParameter (const HeaderParam ¶m) { d->parameters.push_back(param); } -void Header::addParameters(const std::list ¶ms) { +void Header::addParameters(const list ¶ms) { for (auto it = std::begin(params); it!=std::end(params); ++it) { HeaderParam param = *it; addParameter(param.getName(), param.getValue()); } } -void Header::removeParameter (const std::string ¶mName) { +void Header::removeParameter (const string ¶mName) { L_D(); auto it = findParameter(paramName); if (it != d->parameters.cend()) @@ -126,16 +147,16 @@ void Header::removeParameter (const HeaderParam ¶m) { removeParameter(param.getName()); } -std::list::const_iterator Header::findParameter (const std::string ¶mName) const { +list::const_iterator Header::findParameter (const string ¶mName) const { L_D(); return findIf(d->parameters, [¶mName](const HeaderParam ¶m) { return param.getName() == paramName; }); } -const HeaderParam &Header::getParameter (const std::string ¶mName) const { +const HeaderParam &Header::getParameter (const string ¶mName) const { L_D(); - std::list::const_iterator it = findParameter(paramName); + list::const_iterator it = findParameter(paramName); if (it != d->parameters.cend()) { return *it; } diff --git a/src/content/header/header.h b/src/content/header/header.h index 5d111900d..583572523 100644 --- a/src/content/header/header.h +++ b/src/content/header/header.h @@ -33,6 +33,7 @@ class HeaderParam; class LINPHONE_PUBLIC Header : public ClonableObject { public: + Header (); Header (const std::string &name, const std::string &value); Header (const std::string &name, const std::string &value, const std::list ¶ms); Header (const Header &other); diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index b14e28572..e07217934 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -199,7 +199,7 @@ set(SOURCE_FILES_C set(SOURCE_FILES_CXX clonable-object-tester.cpp conference-event-tester.cpp - content-manager-tester.cpp + contents-tester.cpp cpim-tester.cpp main-db-tester.cpp multipart-tester.cpp diff --git a/tester/content-manager-tester.cpp b/tester/contents-tester.cpp similarity index 92% rename from tester/content-manager-tester.cpp rename to tester/contents-tester.cpp index 52d461ef9..99ecee08d 100644 --- a/tester/content-manager-tester.cpp +++ b/tester/contents-tester.cpp @@ -298,6 +298,7 @@ void multipart_to_list () { ms_message("\n\n----- Original part 3 -----"); ms_message("%s", originalStr3.c_str()); BC_ASSERT_TRUE(originalStr3 == generatedStr3); + BC_ASSERT_TRUE(content3.getHeader("Content-Encoding").getValue() == "b64"); Content content4 = contents.front(); contents.pop_front(); @@ -316,6 +317,10 @@ void multipart_to_list () { ms_message("\n\n----- Original part 4 -----"); ms_message("%s", originalStr4.c_str()); BC_ASSERT_TRUE(originalStr4 == generatedStr4); + BC_ASSERT_TRUE(content4.getHeader("Content-Id").getValue() == "toto"); + BC_ASSERT_TRUE(content4.getHeader("Content-Id").getParameter("param1").getValue() == "value1"); + BC_ASSERT_TRUE(content4.getHeader("Content-Id").getParameter("param2").getValue().empty()); + BC_ASSERT_TRUE(content4.getHeader("Content-Id").getParameter("param3").getValue() == "value3"); } void list_to_multipart () { @@ -396,17 +401,35 @@ static void content_type_parsing(void) { BC_ASSERT_TRUE(type == contentType.asString()); } -test_t content_manager_tests[] = { +static void content_header_parsing(void) { + string value = "toto;param1=value1;param2;param3=value3"; + Header header = Header("Content-Id", value); + BC_ASSERT_TRUE(header.getValue() == "toto"); + BC_ASSERT_TRUE(header.getParameter("param1").getValue() == "value1"); + BC_ASSERT_TRUE(header.getParameter("param2").getValue().empty()); + BC_ASSERT_TRUE(header.getParameter("param3").getValue() == "value3"); + BC_ASSERT_EQUAL(3, header.getParameters().size(), int, "%d"); + BC_ASSERT_STRING_EQUAL("", header.getParameter("encoding").getValue().c_str()); + + value = "b64"; + header = Header("Content-Encoding", value); + BC_ASSERT_TRUE(header.getValue() == value); + BC_ASSERT_EQUAL(0, header.getParameters().size(), int, "%d"); + BC_ASSERT_STRING_EQUAL("", header.getParameter("access-type").getValue().c_str()); +} + +test_t contents_tests[] = { TEST_NO_TAG("Multipart to list", multipart_to_list), TEST_NO_TAG("List to multipart", list_to_multipart), - TEST_NO_TAG("Content type parsing", content_type_parsing) + TEST_NO_TAG("Content type parsing", content_type_parsing), + TEST_NO_TAG("Content header parsing", content_header_parsing) }; -test_suite_t content_manager_test_suite = { - "Content manager", +test_suite_t contents_test_suite = { + "Contents", nullptr, nullptr, liblinphone_tester_before_each, liblinphone_tester_after_each, - sizeof(content_manager_tests) / sizeof(content_manager_tests[0]), content_manager_tests + sizeof(contents_tests) / sizeof(contents_tests[0]), contents_tests }; diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 9824c2084..c96be9ed4 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -45,7 +45,7 @@ extern test_suite_t call_video_test_suite; extern test_suite_t clonable_object_test_suite; extern test_suite_t conference_event_test_suite; extern test_suite_t conference_test_suite; -extern test_suite_t content_manager_test_suite; +extern test_suite_t contents_test_suite; extern test_suite_t cpim_test_suite; extern test_suite_t dtmf_test_suite; extern test_suite_t event_test_suite; diff --git a/tester/tester.c b/tester/tester.c index c47cff87f..95e3a9fe5 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -604,7 +604,7 @@ void liblinphone_tester_add_suites() { bc_tester_add_suite(&stun_test_suite); bc_tester_add_suite(&event_test_suite); bc_tester_add_suite(&conference_event_test_suite); - bc_tester_add_suite(&content_manager_test_suite); + bc_tester_add_suite(&contents_test_suite); bc_tester_add_suite(&flexisip_test_suite); bc_tester_add_suite(&remote_provisioning_test_suite); bc_tester_add_suite(&quality_reporting_test_suite);