mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-22 13:48:09 +00:00
Improved CPIM parser to parse all MIME-encapsulated message content headers
This commit is contained in:
parent
3d30114ea7
commit
0c9fbfc16c
8 changed files with 144 additions and 33 deletions
|
|
@ -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<PrivHeaderList> cpimHeaders = make_shared<PrivHeaderList>();
|
||||
shared_ptr<PrivHeaderList> messageHeaders = make_shared<PrivHeaderList>();
|
||||
shared_ptr<PrivHeaderList> contentHeaders = make_shared<PrivHeaderList>();
|
||||
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<const Header> &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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ namespace Cpim {
|
|||
// Warning: Call this function one time!
|
||||
shared_ptr<Message> 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<shared_ptr<ListHeaderNode>>::iterator it = mHeaders->begin();
|
||||
std::advance(it, 1);
|
||||
shared_ptr<ListHeaderNode> messageHeaders = *it;
|
||||
for (const auto &headerNode : *messageHeaders) {
|
||||
const shared_ptr<const Header> header = headerNode->createHeader();
|
||||
if (!header || !message->addMessageHeader(*header))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Add content headers.
|
||||
for (const auto &headerNode : *mHeaders->back()) {
|
||||
const shared_ptr<const Header> header = headerNode->createHeader();
|
||||
if (!header || !message->addMessageHeader(*header))
|
||||
if (!header || !message->addContentHeader(*header))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -265,8 +277,9 @@ shared_ptr<Cpim::Message> Cpim::Parser::parseMessage (const string &input) {
|
|||
}
|
||||
|
||||
shared_ptr<Message> message = messageNode->createMessage();
|
||||
if (message)
|
||||
if (message) {
|
||||
message->setContent(input.substr(parsedSize));
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> content;
|
||||
if (msg->internalContent) {
|
||||
|
|
@ -50,7 +50,13 @@
|
|||
string contentType = content->getContentType().asString();
|
||||
const vector<char> 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> content;
|
||||
if (msg->internalContent) {
|
||||
content = msg->internalContent;
|
||||
} else {
|
||||
content = msg->contents.front();
|
||||
}
|
||||
|
||||
ContentType contentType = content->getContentType();
|
||||
if (contentType.asString() == "Message/CPIM") {
|
||||
const vector<char> body = content->getBody();
|
||||
string contentBody(body.begin(), body.end());
|
||||
shared_ptr<const Cpim::Message> message = Cpim::Message::createFromString(contentBody);
|
||||
if (message && message->isValid()) {
|
||||
shared_ptr<Content> newContent = make_shared<Content>();
|
||||
ContentType newContentType(message->getContentHeaders()->front()->getValue());
|
||||
newContent->setContentType(newContentType);
|
||||
newContent->setBody(message->getContent());
|
||||
} else {
|
||||
//TODO
|
||||
}
|
||||
} else {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
LINPHONE_END_NAMESPACE
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public:
|
|||
ContentType &operator= (const ContentType &src);
|
||||
|
||||
bool operator== (const ContentType &contentType);
|
||||
bool operator== (const std::string &contentType);
|
||||
|
||||
bool isValid () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = "<body>"
|
||||
"Here is the text of my message."
|
||||
"</body>";
|
||||
|
||||
const string str = "Content-type: Message/CPIM\r\n"
|
||||
"\r\n"
|
||||
"From: MR SANDERS <im:piglet@100akerwood.com>\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"
|
||||
"<body>"
|
||||
"Here is the text of my message."
|
||||
"</body>";
|
||||
"\r\n" + body;
|
||||
|
||||
shared_ptr<const Cpim::Message> 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 = "<body>"
|
||||
"Here is the text of my message."
|
||||
"</body>";
|
||||
|
||||
const string str = "Content-type: Message/CPIM\r\n"
|
||||
"\r\n"
|
||||
"From: MR SANDERS <im:piglet@100akerwood.com>\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"
|
||||
"<body>"
|
||||
"Here is the text of my message."
|
||||
"</body>";
|
||||
"\r\n" + body;
|
||||
|
||||
shared_ptr<const Cpim::Message> 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"
|
||||
"<body>"
|
||||
// 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 = "<body>"
|
||||
"Here is the text of my message."
|
||||
"</body>";
|
||||
|
||||
|
|
@ -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"
|
||||
"<body>"
|
||||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue