diff --git a/src/chat/cpim/message/cpim-message.cpp b/src/chat/cpim/message/cpim-message.cpp index 29252e934..2dc33f4c3 100644 --- a/src/chat/cpim/message/cpim-message.cpp +++ b/src/chat/cpim/message/cpim-message.cpp @@ -20,6 +20,7 @@ #include "linphone/utils/utils.h" +#include "logger/logger.h" #include "chat/cpim/parser/cpim-parser.h" #include "object/object-p.h" @@ -37,6 +38,7 @@ public: shared_ptr cpimHeaders = make_shared(); shared_ptr messageHeaders = make_shared(); + shared_ptr contentHeaders = make_shared(); string content; }; @@ -92,6 +94,30 @@ void Cpim::Message::removeMessageHeader (const Header &messageHeader) { // ----------------------------------------------------------------------------- +Cpim::Message::HeaderList Cpim::Message::getContentHeaders () const { + L_D(const Message); + return d->contentHeaders; +} + +bool Cpim::Message::addContentHeader (const Header &contentHeader) { + L_D(Message); + + if (!contentHeader.isValid()) + return false; + + d->contentHeaders->push_back(Parser::getInstance()->cloneHeader(contentHeader)); + return true; +} + +void Cpim::Message::removeContentHeader (const Header &contentHeader) { + L_D(Message); + d->contentHeaders->remove_if([&contentHeader](const shared_ptr &header) { + return contentHeader.getName() == header->getName() && contentHeader.getValue() == header->getValue(); + }); +} + +// ----------------------------------------------------------------------------- + string Cpim::Message::getContent () const { L_D(const Message); return d->content; @@ -125,11 +151,18 @@ string Cpim::Message::asString () const { output += "\r\n"; - for (const auto &messageHeader : *d->messageHeaders) - output += messageHeader->asString(); + if (d->messageHeaders->size() > 0) { + for (const auto &messageHeader : *d->messageHeaders) + output += messageHeader->asString(); + output += "\r\n"; + } + + for (const auto &contentHeaders : *d->contentHeaders) + output += contentHeaders->asString(); + output += "\r\n"; - output += ""; // TODO: Headers MIME. + output += getContent(); return output; diff --git a/src/chat/cpim/message/cpim-message.h b/src/chat/cpim/message/cpim-message.h index 38c1cd5a0..0ddf36d69 100644 --- a/src/chat/cpim/message/cpim-message.h +++ b/src/chat/cpim/message/cpim-message.h @@ -42,6 +42,10 @@ namespace Cpim { HeaderList getMessageHeaders () const; bool addMessageHeader (const Header &messageHeader); void removeMessageHeader (const Header &messageHeader); + + HeaderList getContentHeaders () const; + bool addContentHeader (const Header &contentHeader); + void removeContentHeader (const Header &contentHeader); std::string getContent () const; bool setContent (const std::string &content); diff --git a/src/chat/cpim/parser/cpim-grammar.cpp b/src/chat/cpim/parser/cpim-grammar.cpp index 94bd86d16..b9b863f12 100644 --- a/src/chat/cpim/parser/cpim-grammar.cpp +++ b/src/chat/cpim/parser/cpim-grammar.cpp @@ -25,7 +25,7 @@ LINPHONE_BEGIN_NAMESPACE static const char *grammar = // See: https://tools.ietf.org/html/rfc3862 R"==GRAMMAR==( -Message = Headers CRLF Headers CRLF +Message = Headers CRLF Headers CRLF [Headers CRLF] Headers = *Header Header = Header-name ":" Header-parameters SP Header-value CRLF diff --git a/src/chat/cpim/parser/cpim-parser.cpp b/src/chat/cpim/parser/cpim-parser.cpp index 1cf503a66..0a6ee995e 100644 --- a/src/chat/cpim/parser/cpim-parser.cpp +++ b/src/chat/cpim/parser/cpim-parser.cpp @@ -166,7 +166,7 @@ namespace Cpim { // Warning: Call this function one time! shared_ptr createMessage () const { size_t size = mHeaders->size(); - if (size != 2) { + if (size < 2 || size > 3) { lWarning() << "Bad headers lists size."; return nullptr; } @@ -190,9 +190,21 @@ namespace Cpim { } // Add message headers. + if (mHeaders->size() > 2) { + list>::iterator it = mHeaders->begin(); + std::advance(it, 1); + shared_ptr messageHeaders = *it; + for (const auto &headerNode : *messageHeaders) { + const shared_ptr header = headerNode->createHeader(); + if (!header || !message->addMessageHeader(*header)) + return nullptr; + } + } + + // Add content headers. for (const auto &headerNode : *mHeaders->back()) { const shared_ptr header = headerNode->createHeader(); - if (!header || !message->addMessageHeader(*header)) + if (!header || !message->addContentHeader(*header)) return nullptr; } @@ -265,8 +277,9 @@ shared_ptr Cpim::Parser::parseMessage (const string &input) { } shared_ptr message = messageNode->createMessage(); - if (message) + if (message) { message->setContent(input.substr(parsedSize)); + } return message; } diff --git a/src/chat/modifier/cpim-chat-message-modifier.cpp b/src/chat/modifier/cpim-chat-message-modifier.cpp index b2deb558b..d03344abe 100644 --- a/src/chat/modifier/cpim-chat-message-modifier.cpp +++ b/src/chat/modifier/cpim-chat-message-modifier.cpp @@ -31,10 +31,10 @@ void CpimChatMessageModifier::encode(LinphonePrivate::ChatMessagePrivate* msg) { Cpim::Message message; - Cpim::GenericHeader contentTypeHeader; - contentTypeHeader.setName("Content-Type"); - contentTypeHeader.setValue("Message/CPIM"); - message.addCpimHeader(contentTypeHeader); + Cpim::GenericHeader cpimContentTypeHeader; + cpimContentTypeHeader.setName("Content-Type"); + cpimContentTypeHeader.setValue("Message/CPIM"); + message.addCpimHeader(cpimContentTypeHeader); shared_ptr content; if (msg->internalContent) { @@ -50,7 +50,13 @@ string contentType = content->getContentType().asString(); const vector body = content->getBody(); string contentBody(body.begin(), body.end()); - message.setContent("ContentType: " + contentType + "\r\n" + contentBody); + + Cpim::GenericHeader contentTypeHeader; + contentTypeHeader.setName("Content-Type"); + contentTypeHeader.setValue(contentType); + message.addContentHeader(contentTypeHeader); + + message.setContent(contentBody); if (!message.isValid()) { //TODO @@ -64,7 +70,29 @@ } void CpimChatMessageModifier::decode(LinphonePrivate::ChatMessagePrivate* msg) { - //TODO + shared_ptr content; + if (msg->internalContent) { + content = msg->internalContent; + } else { + content = msg->contents.front(); + } + + ContentType contentType = content->getContentType(); + if (contentType.asString() == "Message/CPIM") { + const vector body = content->getBody(); + string contentBody(body.begin(), body.end()); + shared_ptr message = Cpim::Message::createFromString(contentBody); + if (message && message->isValid()) { + shared_ptr newContent = make_shared(); + ContentType newContentType(message->getContentHeaders()->front()->getValue()); + newContent->setContentType(newContentType); + newContent->setBody(message->getContent()); + } else { + //TODO + } + } else { + //TODO + } } LINPHONE_END_NAMESPACE \ No newline at end of file diff --git a/src/content/content-type.cpp b/src/content/content-type.cpp index 7b55d4322..4fc11faf0 100644 --- a/src/content/content-type.cpp +++ b/src/content/content-type.cpp @@ -70,6 +70,10 @@ bool ContentType::operator== (const ContentType &contentType) { return getType() == contentType.getType() && getSubType() == contentType.getSubType(); } +bool ContentType::operator== (const string &contentType) { + return *this == ContentType(contentType); +} + const string &ContentType::getType () const { L_D(const ContentType); return d->type; diff --git a/src/content/content-type.h b/src/content/content-type.h index 15095207c..9df428f9f 100644 --- a/src/content/content-type.h +++ b/src/content/content-type.h @@ -38,6 +38,7 @@ public: ContentType &operator= (const ContentType &src); bool operator== (const ContentType &contentType); + bool operator== (const std::string &contentType); bool isValid () const; diff --git a/tester/cpim-tester.cpp b/tester/cpim-tester.cpp index 8c7522905..ea83c2f65 100644 --- a/tester/cpim-tester.cpp +++ b/tester/cpim-tester.cpp @@ -37,6 +37,8 @@ static void parse_minimal_message () { const string str2 = message->asString(); BC_ASSERT_STRING_EQUAL(str2.c_str(), str.c_str()); + + BC_ASSERT_STRING_EQUAL(message->getContent().c_str(), ""); } static void set_generic_header_name () { @@ -227,6 +229,10 @@ static void check_subject_header_language () { } static void parse_rfc_example () { + const string body = "" + "Here is the text of my message." + ""; + const string str = "Content-type: Message/CPIM\r\n" "\r\n" "From: MR SANDERS \r\n" @@ -239,21 +245,25 @@ static void parse_rfc_example () { "MyFeatures.VitalMessageOption: Confirmation-requested\r\n" "MyFeatures.WackyMessageOption: Use-silly-font\r\n" "\r\n" - "Content-type text/xml; charset=utf-8\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" "Content-ID: <1234567890@foo.com>\r\n" - "\r\n" - "" - "Here is the text of my message." - ""; + "\r\n" + body; shared_ptr message = Cpim::Message::createFromString(str); if (!BC_ASSERT_PTR_NOT_NULL(message)) return; const string str2 = message->asString(); BC_ASSERT_STRING_EQUAL(str2.c_str(), str.c_str()); + + string content = message->getContent(); + BC_ASSERT_STRING_EQUAL(content.c_str(), body.c_str()); } static void parse_message_with_generic_header_parameters () { + const string body = "" + "Here is the text of my message." + ""; + const string str = "Content-type: Message/CPIM\r\n" "\r\n" "From: MR SANDERS \r\n" @@ -261,18 +271,18 @@ static void parse_message_with_generic_header_parameters () { "yaya: coucou\r\n" "yepee:;good=bad ugly\r\n" "\r\n" - "Content-type text/xml; charset=utf-8\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" "Content-ID: <1234567890@foo.com>\r\n" - "\r\n" - "" - "Here is the text of my message." - ""; + "\r\n" + body; shared_ptr message = Cpim::Message::createFromString(str); if (!BC_ASSERT_PTR_NOT_NULL(message)) return; const string str2 = message->asString(); BC_ASSERT_STRING_EQUAL(str2.c_str(), str.c_str()); + + string content = message->getContent(); + BC_ASSERT_STRING_EQUAL(content.c_str(), body.c_str()); } static void build_message () { @@ -281,11 +291,11 @@ static void build_message () { return; // Set CPIM headers. - Cpim::GenericHeader contentTypeHeader; - if (!BC_ASSERT_TRUE(contentTypeHeader.setName("Content-Type"))) return; - if (!BC_ASSERT_TRUE(contentTypeHeader.setValue("Message/CPIM"))) return; + Cpim::GenericHeader cpimContentTypeHeader; + if (!BC_ASSERT_TRUE(cpimContentTypeHeader.setName("Content-Type"))) return; + if (!BC_ASSERT_TRUE(cpimContentTypeHeader.setValue("Message/CPIM"))) return; - if (!BC_ASSERT_TRUE(message.addCpimHeader(contentTypeHeader))) return; + if (!BC_ASSERT_TRUE(message.addCpimHeader(cpimContentTypeHeader))) return; // Set message headers. Cpim::FromHeader fromHeader; @@ -328,10 +338,18 @@ static void build_message () { if (!BC_ASSERT_TRUE(message.addMessageHeader(vitalMessageHeader))) return; if (!BC_ASSERT_TRUE(message.addMessageHeader(wackyMessageHeader))) return; - const string content = "Content-type text/xml; charset=utf-8\r\n" - "Content-ID: <1234567890@foo.com>\r\n" - "\r\n" - "" + // Set Content headers. + Cpim::GenericHeader contentTypeHeader; + if (!BC_ASSERT_TRUE(contentTypeHeader.setName("Content-Type"))) return; + if (!BC_ASSERT_TRUE( contentTypeHeader.setValue("text/xml; charset=utf-8"))) return; + if (!BC_ASSERT_TRUE(message.addContentHeader(contentTypeHeader))) return; + + Cpim::GenericHeader contentIdHeader; + if (!BC_ASSERT_TRUE(contentIdHeader.setName("Content-ID"))) return; + if (!BC_ASSERT_TRUE( contentIdHeader.setValue("<1234567890@foo.com>"))) return; + if (!BC_ASSERT_TRUE(message.addContentHeader(contentIdHeader))) return; + + const string content = "" "Here is the text of my message." ""; @@ -351,7 +369,7 @@ static void build_message () { "MyFeatures.VitalMessageOption: Confirmation-requested\r\n" "MyFeatures.WackyMessageOption: Use-silly-font\r\n" "\r\n" - "Content-type text/xml; charset=utf-8\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" "Content-ID: <1234567890@foo.com>\r\n" "\r\n" "" @@ -361,6 +379,14 @@ static void build_message () { BC_ASSERT_STRING_EQUAL(strMessage.c_str(), expectedMessage.c_str()); } +static void cpim_chat_message_modifier_encoder(void) { + +} + +static void cpim_chat_message_modifier_decoder(void) { + +} + test_t cpim_tests[] = { TEST_NO_TAG("Parse minimal CPIM message", parse_minimal_message), TEST_NO_TAG("Set generic header name", set_generic_header_name), @@ -370,7 +396,9 @@ test_t cpim_tests[] = { TEST_NO_TAG("Check Subject header language", check_subject_header_language), TEST_NO_TAG("Parse RFC example", parse_rfc_example), TEST_NO_TAG("Parse Message with generic header parameters", parse_message_with_generic_header_parameters), - TEST_NO_TAG("Build Message", build_message) + TEST_NO_TAG("Build Message", build_message), + TEST_NO_TAG("CPIM chat message modifier encoder", cpim_chat_message_modifier_encoder), + TEST_NO_TAG("CPIM chat message modifier decoder", cpim_chat_message_modifier_decoder) }; test_suite_t cpim_test_suite = {