diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ca281a42..17bc2550b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,6 +77,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES conference/session/call-session.h conference/session/media-session.h conference/session/port-config.h + content/content-manager.h content/content-type.h content/content.h core/core-p.h @@ -171,6 +172,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES conference/remote-conference.cpp conference/session/call-session.cpp conference/session/media-session.cpp + content/content-manager.cpp content/content-type.cpp content/content.cpp core/core.cpp diff --git a/src/content/content-manager.cpp b/src/content/content-manager.cpp new file mode 100644 index 000000000..c3071ca19 --- /dev/null +++ b/src/content/content-manager.cpp @@ -0,0 +1,111 @@ +/* + * content-manager.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 + +#include "belle-sip/belle-sip.h" +#include "content-manager.h" +#include "content-type.h" +#include "linphone/content.h" +#include "linphone/core.h" +#include "logger/logger.h" +#include "private.h" + +// ============================================================================= + +#define MULTIPART_BOUNDARY "---------------------------14737809831466499882746641449" + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +ContentManager::ContentManager (LinphoneCore *core) { + mCore = core; +} + +list ContentManager::multipartToContentLists (Content content) const { + belle_sip_multipart_body_handler_t *mpbh = belle_sip_multipart_body_handler_new_from_buffer((void *)content.getBodyAsString().c_str(), content.getBodyAsString().length(), MULTIPART_BOUNDARY); + belle_sip_object_ref(mpbh); + + const belle_sip_list_t *parts = belle_sip_multipart_body_handler_get_parts(mpbh); + list contentsList = list(); + while (parts) { + belle_sip_body_handler_t *part = BELLE_SIP_BODY_HANDLER(parts->data); + const belle_sip_list_t *part_headers = belle_sip_body_handler_get_headers(part); + belle_sip_list_t *it; + belle_sip_header_content_type_t *part_content_type=NULL;; + for(it = (belle_sip_list_t *)part_headers;it!=NULL;it=it->next) { + belle_sip_header_t *header = BELLE_SIP_HEADER(it->data); + if(strcasecmp("Content-Type",belle_sip_header_get_name(header)) == 0) { + part_content_type=BELLE_SIP_HEADER_CONTENT_TYPE(header); + break; + } + } + belle_sip_header_content_type_get_type(part_content_type); + belle_sip_memory_body_handler_get_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(part)); + Content retContent = Content(); + ContentType type(belle_sip_header_content_type_get_type(part_content_type), belle_sip_header_content_type_get_subtype(part_content_type)); + retContent.setBody((const char *)belle_sip_memory_body_handler_get_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(part))); + retContent.setContentType(type); + contentsList.push_back(retContent); + parts = parts->next; + } + + belle_sip_object_unref(mpbh); + return contentsList; +} + +Content ContentManager::contentsListToMultipart (list contents) const { + char *desc; + string sub; + belle_sip_memory_body_handler_t *mbh; + belle_sip_multipart_body_handler_t *mpbh = belle_sip_multipart_body_handler_new(NULL, NULL, NULL, MULTIPART_BOUNDARY); + belle_sip_object_ref(mpbh); + for (const auto &content : contents) { + const ContentType &contentType = content.getContentType(); + stringstream subtype; + sub = contentType.getSubType(); + subtype << sub << "; charset=\"UTF-8\""; + belle_sip_header_t *content_type = BELLE_SIP_HEADER( + belle_sip_header_content_type_create( + contentType.getType().c_str(), + subtype.str().c_str() + ) + ); + mbh = belle_sip_memory_body_handler_new_copy_from_buffer( + (void *)content.getBodyAsString().c_str(), + content.getBodyAsString().length(), + NULL, + NULL + ); + belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(mbh), content_type); + belle_sip_multipart_body_handler_add_part(mpbh, BELLE_SIP_BODY_HANDLER(mbh)); + } + desc = belle_sip_object_to_string(mpbh); + belle_sip_object_unref(mpbh); + belle_sip_object_ref(mbh); + + Content retContent = Content(); + ContentType type("application", sub); + retContent.setBody(desc); + retContent.setContentType(type); + return retContent; +} + +LINPHONE_END_NAMESPACE diff --git a/src/content/content-manager.h b/src/content/content-manager.h new file mode 100644 index 000000000..0b020616a --- /dev/null +++ b/src/content/content-manager.h @@ -0,0 +1,45 @@ +/* + * content-manager.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_MANAGER_H_ +#define _CONTENT_MANAGER_H_ + +#include + +#include "content.h" +#include "linphone/types.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class ContentManager { +public: + ContentManager (LinphoneCore *core); + + std::list multipartToContentLists (Content content) const; + Content contentsListToMultipart (std::list contents) const; + +private: + LinphoneCore *mCore = nullptr; +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _CONTENT_MANAGER_H_ diff --git a/src/content/content-type.cpp b/src/content/content-type.cpp index c6772c207..4be8f9626 100644 --- a/src/content/content-type.cpp +++ b/src/content/content-type.cpp @@ -46,6 +46,7 @@ const ContentType ContentType::ImIsComposing("application/im-iscomposing+xml"); const ContentType ContentType::PlainText("text/plain"); const ContentType ContentType::ResourceLists("application/resource-lists+xml"); const ContentType ContentType::Sdp("application/sdp"); +const ContentType ContentType::ConferenceInfo("application/conference-info+xml"); // ----------------------------------------------------------------------------- diff --git a/src/content/content-type.h b/src/content/content-type.h index c2a1d0ba1..f3039c164 100644 --- a/src/content/content-type.h +++ b/src/content/content-type.h @@ -66,6 +66,7 @@ public: static const ContentType PlainText; static const ContentType ResourceLists; static const ContentType Sdp; + static const ContentType ConferenceInfo; private: L_DECLARE_PRIVATE(ContentType); diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index fff62034b..3e58928c1 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -198,6 +198,7 @@ set(SOURCE_FILES_CXX clonable-object-tester.cpp conference-event-tester.cpp conference-tester.cpp + content-manager-tester.cpp cpim-tester.cpp main-db-tester.cpp multipart-tester.cpp diff --git a/tester/content-manager-tester.cpp b/tester/content-manager-tester.cpp new file mode 100644 index 000000000..db5fc375a --- /dev/null +++ b/tester/content-manager-tester.cpp @@ -0,0 +1,303 @@ +/* + * conference-event-tester.cpp + * Copyright (C) 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 3 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, see . + */ +#include +#include + +#include "content/content-manager.h" +#include "content/content-type.h" +#include "liblinphone_tester.h" + +using namespace LinphonePrivate; +using namespace std; + +static const char* multipart = \ +"-----------------------------14737809831466499882746641449\r\n" \ +"Content-Type: application/rlmi+xml;charset=\"UTF-8\"\r\n\r\n" \ +"" \ +"" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +"" \ +"-----------------------------14737809831466499882746641449\r\n" \ +"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ +"" \ +"" \ +" " \ +" " \ +" open" \ +" " \ +" sip:+YYYYYYYYYY@sip.linphone.org;user=phone" \ +" 2017-10-25T13:18:26" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +"" \ +"-----------------------------14737809831466499882746641449\r\n" \ +"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ +"" \ +"" \ +" " \ +" " \ +" open" \ +" " \ +" sip:+XXXXXXXXXX@sip.linphone.org;user=phone" \ +" 2017-10-25T13:18:26" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +"" \ +"-----------------------------14737809831466499882746641449\r\n" \ +"Content-Type: application/pidf+xml;charset=\"UTF-8\"\r\n\r\n" \ +"" \ +"" \ +" " \ +" " \ +" open" \ +" " \ +" sip:someone@sip.linphone.org" \ +" 2017-10-25T13:18:26" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +"" \ +"-----------------------------14737809831466499882746641449--\r\n"; + +static const char* part1 = \ +"" \ +"" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +""; + +static const char* part2 = \ +"" \ +"" \ +" " \ +" " \ +" open" \ +" " \ +" sip:+YYYYYYYYYY@sip.linphone.org;user=phone" \ +" 2017-10-25T13:18:26" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +"" ; + +static const char* part3 = \ +"" \ +"" \ +" " \ +" " \ +" open" \ +" " \ +" sip:+XXXXXXXXXX@sip.linphone.org;user=phone" \ +" 2017-10-25T13:18:26" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +""; + +static const char* part4 = \ +"" \ +"" \ +" " \ +" " \ +" open" \ +" " \ +" sip:someone@sip.linphone.org" \ +" 2017-10-25T13:18:26" \ +" " \ +" " \ +" " \ +" " \ +" " \ +" " \ +""; + +void multipart_to_list () { + LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); + ContentManager manager(marie->lc); + + Content multipartContent = Content(); + multipartContent.setBody(multipart); + multipartContent.setContentType(ContentType("multipart", "related")); + + list contents = manager.multipartToContentLists(multipartContent); + BC_ASSERT_EQUAL(contents.size(), 4, int, "%d"); + Content content1 = contents.front(); + contents.pop_front(); + string originalStr1(part1); + originalStr1.erase(std::remove(originalStr1.begin(), originalStr1.end(), ' '), originalStr1.end()); + originalStr1.erase(std::remove(originalStr1.begin(), originalStr1.end(), '\t'), originalStr1.end()); + originalStr1.erase(std::remove(originalStr1.begin(), originalStr1.end(), '\r'), originalStr1.end()); + originalStr1.erase(std::remove(originalStr1.begin(), originalStr1.end(), '\n'), originalStr1.end()); + string generatedStr1 = content1.getBodyAsString(); + generatedStr1.erase(std::remove(generatedStr1.begin(), generatedStr1.end(), ' '), generatedStr1.end()); + generatedStr1.erase(std::remove(generatedStr1.begin(), generatedStr1.end(), '\t'), generatedStr1.end()); + generatedStr1.erase(std::remove(generatedStr1.begin(), generatedStr1.end(), '\r'), generatedStr1.end()); + generatedStr1.erase(std::remove(generatedStr1.begin(), generatedStr1.end(), '\n'), generatedStr1.end()); + ms_message("\n\n----- Generated part 1 -----"); + ms_message("%s", generatedStr1.c_str()); + ms_message("\n\n----- Original part 1 -----"); + ms_message("%s", originalStr1.c_str()); + BC_ASSERT_TRUE(originalStr1 == generatedStr1); + + Content content2 = contents.front(); + contents.pop_front(); + string originalStr2(part2); + originalStr2.erase(std::remove(originalStr2.begin(), originalStr2.end(), ' '), originalStr2.end()); + originalStr2.erase(std::remove(originalStr2.begin(), originalStr2.end(), '\t'), originalStr2.end()); + originalStr2.erase(std::remove(originalStr2.begin(), originalStr2.end(), '\r'), originalStr2.end()); + originalStr2.erase(std::remove(originalStr2.begin(), originalStr2.end(), '\n'), originalStr2.end()); + string generatedStr2 = content2.getBodyAsString(); + generatedStr2.erase(std::remove(generatedStr2.begin(), generatedStr2.end(), ' '), generatedStr2.end()); + generatedStr2.erase(std::remove(generatedStr2.begin(), generatedStr2.end(), '\t'), generatedStr2.end()); + generatedStr2.erase(std::remove(generatedStr2.begin(), generatedStr2.end(), '\r'), generatedStr2.end()); + generatedStr2.erase(std::remove(generatedStr2.begin(), generatedStr2.end(), '\n'), generatedStr2.end()); + ms_message("\n\n----- Generated part 2 -----"); + ms_message("%s", generatedStr2.c_str()); + ms_message("\n\n----- Original part 2 -----"); + ms_message("%s", originalStr2.c_str()); + BC_ASSERT_TRUE(originalStr2 == generatedStr2); + + Content content3 = contents.front(); + contents.pop_front(); + string originalStr3(part3); + originalStr3.erase(std::remove(originalStr3.begin(), originalStr3.end(), ' '), originalStr3.end()); + originalStr3.erase(std::remove(originalStr3.begin(), originalStr3.end(), '\t'), originalStr3.end()); + originalStr3.erase(std::remove(originalStr3.begin(), originalStr3.end(), '\r'), originalStr3.end()); + originalStr3.erase(std::remove(originalStr3.begin(), originalStr3.end(), '\n'), originalStr3.end()); + string generatedStr3 = content3.getBodyAsString(); + generatedStr3.erase(std::remove(generatedStr3.begin(), generatedStr3.end(), ' '), generatedStr3.end()); + generatedStr3.erase(std::remove(generatedStr3.begin(), generatedStr3.end(), '\t'), generatedStr3.end()); + generatedStr3.erase(std::remove(generatedStr3.begin(), generatedStr3.end(), '\r'), generatedStr3.end()); + generatedStr3.erase(std::remove(generatedStr3.begin(), generatedStr3.end(), '\n'), generatedStr3.end()); + ms_message("\n\n----- Generated part 3 -----"); + ms_message("%s", generatedStr3.c_str()); + ms_message("\n\n----- Original part 3 -----"); + ms_message("%s", originalStr3.c_str()); + BC_ASSERT_TRUE(originalStr3 == generatedStr3); + + Content content4 = contents.front(); + contents.pop_front(); + string originalStr4(part4); + originalStr4.erase(std::remove(originalStr4.begin(), originalStr4.end(), ' '), originalStr4.end()); + originalStr4.erase(std::remove(originalStr4.begin(), originalStr4.end(), '\t'), originalStr4.end()); + originalStr4.erase(std::remove(originalStr4.begin(), originalStr4.end(), '\r'), originalStr4.end()); + originalStr4.erase(std::remove(originalStr4.begin(), originalStr4.end(), '\n'), originalStr4.end()); + string generatedStr4 = content4.getBodyAsString(); + generatedStr4.erase(std::remove(generatedStr4.begin(), generatedStr4.end(), ' '), generatedStr4.end()); + generatedStr4.erase(std::remove(generatedStr4.begin(), generatedStr4.end(), '\t'), generatedStr4.end()); + generatedStr4.erase(std::remove(generatedStr4.begin(), generatedStr4.end(), '\r'), generatedStr4.end()); + generatedStr4.erase(std::remove(generatedStr4.begin(), generatedStr4.end(), '\n'), generatedStr4.end()); + ms_message("\n\n----- Generated part 4 -----"); + ms_message("%s", generatedStr3.c_str()); + ms_message("\n\n----- Original part 4 -----"); + ms_message("%s", originalStr4.c_str()); + BC_ASSERT_TRUE(originalStr4 == generatedStr4); + + linphone_core_manager_destroy(marie); +} + +void list_to_multipart () { + LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); + ContentManager manager(marie->lc); + + Content content1 = Content(); + content1.setBody(part1); + content1.setContentType(ContentType("application", "rlmi+xml")); + Content content2 = Content(); + content2.setBody(part2); + content2.setContentType(ContentType("application", "pidf+xml")); + Content content3 = Content(); + content3.setBody(part3); + content3.setContentType(ContentType("application", "pidf+xml")); + Content content4 = Content(); + content4.setBody(part4); + content4.setContentType(ContentType("application", "pidf+xml")); + list contents; + contents.push_back(content1); + contents.push_back(content2); + contents.push_back(content3); + contents.push_back(content4); + + Content multipartContent = manager.contentsListToMultipart(contents); + string originalStr(multipart); + originalStr.erase(std::remove(originalStr.begin(), originalStr.end(), ' '), originalStr.end()); + originalStr.erase(std::remove(originalStr.begin(), originalStr.end(), '\t'), originalStr.end()); + originalStr.erase(std::remove(originalStr.begin(), originalStr.end(), '\r'), originalStr.end()); + originalStr.erase(std::remove(originalStr.begin(), originalStr.end(), '\n'), originalStr.end()); + + string generatedStr = multipartContent.getBodyAsString(); + generatedStr.erase(std::remove(generatedStr.begin(), generatedStr.end(), ' '), generatedStr.end()); + generatedStr.erase(std::remove(generatedStr.begin(), generatedStr.end(), '\t'), generatedStr.end()); + generatedStr.erase(std::remove(generatedStr.begin(), generatedStr.end(), '\r'), generatedStr.end()); + generatedStr.erase(std::remove(generatedStr.begin(), generatedStr.end(), '\n'), generatedStr.end()); + + ms_message("\n\n----- Generated multipart -----"); + ms_message("%s", generatedStr.c_str()); + + ms_message("\n\n----- Original multipart -----"); + ms_message("%s", originalStr.c_str()); + BC_ASSERT_TRUE(originalStr == generatedStr); + + linphone_core_manager_destroy(marie); +} + +test_t content_manager_tests[] = { + TEST_NO_TAG("Multipart to list", multipart_to_list), + TEST_NO_TAG("List to multipart", list_to_multipart) +}; + +test_suite_t content_manager_test_suite = { + "Content manager", + nullptr, + nullptr, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(content_manager_tests) / sizeof(content_manager_tests[0]), content_manager_tests +}; diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 804288955..11a7a712c 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -46,6 +46,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 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 4cef7a7e6..48708461e 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -565,6 +565,7 @@ void liblinphone_tester_add_suites() { bc_tester_add_suite(&event_test_suite); bc_tester_add_suite(&conference_event_test_suite); bc_tester_add_suite(&conference_test_suite); + bc_tester_add_suite(&content_manager_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);