Merge branch 'dev_cpim_improvements' into dev_refactor_cpp

This commit is contained in:
Ghislain MARY 2018-04-27 08:56:30 +02:00
commit 2bb1893802
19 changed files with 1184 additions and 805 deletions

Binary file not shown.

View file

@ -17,6 +17,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <iomanip>
#include <sstream>
#include "linphone/utils/utils.h"
#include "logger/logger.h"
#include "chat/cpim/parser/cpim-parser.h"
#include "cpim-header-p.h"
@ -28,50 +35,278 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
Cpim::CoreHeader::CoreHeader () : Header(*new HeaderPrivate) {}
class Cpim::ContactHeaderPrivate : public HeaderPrivate {
public:
string uri;
string formalName;
};
Cpim::CoreHeader::CoreHeader (HeaderPrivate &p) : Header(p) {}
Cpim::ContactHeader::ContactHeader () : Header(*new ContactHeaderPrivate) {}
Cpim::CoreHeader::~CoreHeader () {}
Cpim::ContactHeader::ContactHeader (const string &uri, const string &formalName) : ContactHeader() {
setUri(uri);
setFormalName(formalName);
}
bool Cpim::CoreHeader::isValid () const {
return !getValue().empty();
string Cpim::ContactHeader::getUri () const {
L_D();
return d->uri;
}
void Cpim::ContactHeader::setUri (const string &uri) {
L_D();
d->uri = uri;
}
string Cpim::ContactHeader::getFormalName () const {
L_D();
return d->formalName;
}
void Cpim::ContactHeader::setFormalName (const string &formalName) {
L_D();
if (formalName.front() == '\"' && formalName.back() == '\"')
d->formalName = formalName.substr(1, formalName.size() - 2);
else if (formalName.back() == ' ')
d->formalName = formalName.substr(0, formalName.size() - 1);
else
d->formalName = formalName;
}
string Cpim::ContactHeader::getValue () const {
L_D();
string result;
if (!d->formalName.empty())
result += "\"" + d->formalName + "\"";
result += "<" + d->uri + ">";
return result;
}
string Cpim::ContactHeader::asString () const {
return getName() + ": " + getValue() + "\r\n";
}
// -----------------------------------------------------------------------------
#define MAKE_CORE_HEADER_IMPL(CLASS_PREFIX) \
bool Cpim::CLASS_PREFIX ## Header::setValue(const string &value) { \
return Parser::getInstance()->coreHeaderIsValid<CLASS_PREFIX ## Header>(value) && Header::setValue(value); \
class Cpim::DateTimeHeaderPrivate : public HeaderPrivate {
public:
tm dateTime;
tm dateTimeOffset;
string signOffset;
};
Cpim::DateTimeHeader::DateTimeHeader () : Header(*new DateTimeHeaderPrivate) {}
Cpim::DateTimeHeader::DateTimeHeader (time_t time) : DateTimeHeader() {
setTime(time);
}
Cpim::DateTimeHeader::DateTimeHeader (const tm &time, const tm &timeOffset, const string &signOffset) : DateTimeHeader() {
setTime(time, timeOffset, signOffset);
}
time_t Cpim::DateTimeHeader::getTime () const {
L_D();
tm result = d->dateTime;
result.tm_year -= 1900;
result.tm_isdst = 0;
if (d->signOffset == "+") {
result.tm_hour += d->dateTimeOffset.tm_hour;
result.tm_min += d->dateTimeOffset.tm_min;
while (result.tm_min > 59) {
result.tm_hour++;
result.tm_min -= 60;
}
}
else if (d->signOffset == "-") {
result.tm_hour -= d->dateTimeOffset.tm_hour;
result.tm_hour -= d->dateTimeOffset.tm_min;
while (result.tm_min < 0) {
result.tm_hour--;
result.tm_min += 60;
}
}
MAKE_CORE_HEADER_IMPL(From);
MAKE_CORE_HEADER_IMPL(To);
MAKE_CORE_HEADER_IMPL(Cc);
MAKE_CORE_HEADER_IMPL(DateTime);
return Utils::getTmAsTimeT(result);
}
MAKE_CORE_HEADER_IMPL(Ns);
MAKE_CORE_HEADER_IMPL(Require);
void Cpim::DateTimeHeader::setTime (const time_t time) {
L_D();
#undef MAKE_CORE_HEADER_IMPL
d->signOffset = "Z";
d->dateTime = Utils::getTimeTAsTm(time);
d->dateTime.tm_year += 1900;
}
void Cpim::DateTimeHeader::setTime (const tm &time, const tm &timeOffset, const string &signOffset) {
L_D();
d->dateTime = time;
d->dateTimeOffset = timeOffset;
d->signOffset = signOffset;
}
string Cpim::DateTimeHeader::getValue () const {
L_D();
stringstream ss;
ss << setfill('0') << setw(4) << d->dateTime.tm_year << "-"
<< setfill('0') << setw(2) << d->dateTime.tm_mon + 1 << "-"
<< setfill('0') << setw(2) << d->dateTime.tm_mday << "T"
<< setfill('0') << setw(2) << d->dateTime.tm_hour << ":"
<< setfill('0') << setw(2) << d->dateTime.tm_min << ":"
<< setfill('0') << setw(2) << d->dateTime.tm_sec;
ss << d->signOffset;
if (d->signOffset != "Z")
ss << setfill('0') << setw(2) << d->dateTimeOffset.tm_hour << ":"
<< setfill('0') << setw(2) << d->dateTimeOffset.tm_min;
return ss.str();
}
string Cpim::DateTimeHeader::asString () const {
return getName() + ": " + getValue() + "\r\n";
}
struct tm Cpim::DateTimeHeader::getTimeStruct () const {
L_D();
return d->dateTime;
}
struct tm Cpim::DateTimeHeader::getTimeOffset () const {
L_D();
return d->dateTimeOffset;
}
string Cpim::DateTimeHeader::getSignOffset () const {
L_D();
return d->signOffset;
}
// -----------------------------------------------------------------------------
void Cpim::CoreHeader::force (const string &value) {
Header::setValue(value);
class Cpim::NsHeaderPrivate : public HeaderPrivate {
public:
string uri;
string prefixName;
};
Cpim::NsHeader::NsHeader () : Header(*new NsHeaderPrivate) {}
Cpim::NsHeader::NsHeader (const string &uri, const string &prefixName) : NsHeader() {
setUri(uri);
setPrefixName(prefixName);
}
string Cpim::NsHeader::getUri () const {
L_D();
return d->uri;
}
void Cpim::NsHeader::setUri (const string &uri) {
L_D();
d->uri = uri;
}
string Cpim::NsHeader::getPrefixName () const {
L_D();
return d->prefixName;
}
void Cpim::NsHeader::setPrefixName (const string &prefixName) {
L_D();
d->prefixName = prefixName;
}
string Cpim::NsHeader::getValue () const {
L_D();
string ns;
if (!d->prefixName.empty())
ns = d->prefixName + " ";
return ns + "<" + d->uri + ">";
}
string Cpim::NsHeader::asString () const {
return getName() + ": " + getValue() + "\r\n";
}
// -----------------------------------------------------------------------------
class Cpim::RequireHeaderPrivate : public HeaderPrivate {
public:
list<string> headerNames;
};
Cpim::RequireHeader::RequireHeader () : Header(*new RequireHeaderPrivate) {}
Cpim::RequireHeader::RequireHeader (const string &headerNames) : RequireHeader() {
for (const string &header : Utils::split(headerNames, ",")) {
addHeaderName(header);
}
}
Cpim::RequireHeader::RequireHeader (const list<string> &headerNames) : RequireHeader() {
L_D();
d->headerNames = headerNames;
}
list<string> Cpim::RequireHeader::getHeaderNames () const {
L_D();
return d->headerNames;
}
void Cpim::RequireHeader::addHeaderName (const string &headerName) {
L_D();
d->headerNames.push_back(headerName);
}
string Cpim::RequireHeader::getValue () const {
L_D();
string requires;
for (const string &header : d->headerNames) {
if (header != d->headerNames.front())
requires += ",";
requires += header;
}
return requires;
}
string Cpim::RequireHeader::asString () const {
return getName() + ": " + getValue() + "\r\n";
}
// -----------------------------------------------------------------------------
class Cpim::SubjectHeaderPrivate : public HeaderPrivate {
public:
string subject;
string language;
};
Cpim::SubjectHeader::SubjectHeader () : CoreHeader(*new SubjectHeaderPrivate) {}
Cpim::SubjectHeader::SubjectHeader () : Header(*new SubjectHeaderPrivate) {}
bool Cpim::SubjectHeader::setValue (const string &value) {
return Parser::getInstance()->coreHeaderIsValid<SubjectHeader>(value) && Header::setValue(value);
Cpim::SubjectHeader::SubjectHeader (const string &subject, const string &language) : SubjectHeader() {
setSubject(subject);
setLanguage(language);
}
string Cpim::SubjectHeader::getSubject () const {
L_D();
return d->subject;
}
void Cpim::SubjectHeader::setSubject (const string &subject) {
L_D();
d->subject = subject;
}
string Cpim::SubjectHeader::getLanguage () const {
@ -79,30 +314,23 @@ string Cpim::SubjectHeader::getLanguage () const {
return d->language;
}
bool Cpim::SubjectHeader::setLanguage (const string &language) {
if (!language.empty() && !Parser::getInstance()->subjectHeaderLanguageIsValid(language))
return false;
void Cpim::SubjectHeader::setLanguage (const string &language) {
L_D();
d->language = language;
return true;
}
string Cpim::SubjectHeader::asString () const {
string Cpim::SubjectHeader::getValue () const {
L_D();
string languageParam;
if (!d->language.empty())
languageParam = ";lang=" + d->language;
return getName() + ":" + languageParam + " " + getValue() + "\r\n";
return languageParam + " " + d->subject;
}
void Cpim::SubjectHeader::force (const string &value, const string &language) {
L_D();
CoreHeader::force(value);
d->language = language;
string Cpim::SubjectHeader::asString () const {
return getName() + ":" + getValue() + "\r\n";
}
LINPHONE_END_NAMESPACE

View file

@ -20,60 +20,160 @@
#ifndef _L_CPIM_CORE_HEADERS_H_
#define _L_CPIM_CORE_HEADERS_H_
#include <ctime>
#include <list>
#include "cpim-header.h"
// =============================================================================
LINPHONE_BEGIN_NAMESPACE
#define MAKE_CORE_HEADER(CLASS_PREFIX, NAME) \
class LINPHONE_PUBLIC CLASS_PREFIX ## Header : public CoreHeader { \
#define MAKE_CONTACT_HEADER(CLASS_PREFIX, NAME) \
class LINPHONE_PUBLIC CLASS_PREFIX ## Header : public ContactHeader { \
public: \
CLASS_PREFIX ## Header() = default; \
inline std::string getName() const override { \
CLASS_PREFIX ## Header () = default; \
CLASS_PREFIX ## Header (const std::string &uri, const std::string &formalName = "") : ContactHeader (uri, formalName) {} \
inline std::string getName () const override { \
return NAME; \
} \
bool setValue(const std::string &value) override; \
private: \
L_DISABLE_COPY(CLASS_PREFIX ## Header); \
};
namespace Cpim {
class HeaderNode;
class DateTimeHeaderNode;
// -------------------------------------------------------------------------
// Generic core header.
// Specific Contact headers declaration.
// -------------------------------------------------------------------------
class LINPHONE_PUBLIC CoreHeader : public Header {
friend class HeaderNode;
class ContactHeaderPrivate;
class LINPHONE_PUBLIC ContactHeader : public Header {
public:
CoreHeader ();
ContactHeader ();
virtual ~CoreHeader () = 0;
ContactHeader (const std::string &uri, const std::string &formalName = "");
bool isValid () const override;
std::string getUri () const;
void setUri (const std::string &uri);
protected:
explicit CoreHeader (HeaderPrivate &p);
std::string getFormalName () const;
void setFormalName (const std::string &formalName);
void force (const std::string &value);
std::string getValue () const override;
std::string asString () const override;
private:
L_DISABLE_COPY(CoreHeader);
L_DECLARE_PRIVATE(ContactHeader);
L_DISABLE_COPY(ContactHeader);
};
// -------------------------------------------------------------------------
// Core headers.
MAKE_CONTACT_HEADER(From, "From");
MAKE_CONTACT_HEADER(To, "To");
MAKE_CONTACT_HEADER(Cc, "cc");
// -------------------------------------------------------------------------
// Specific DateTime declaration.
// -------------------------------------------------------------------------
MAKE_CORE_HEADER(From, "From");
MAKE_CORE_HEADER(To, "To");
MAKE_CORE_HEADER(Cc, "cc");
MAKE_CORE_HEADER(DateTime, "DateTime");
MAKE_CORE_HEADER(Ns, "NS");
MAKE_CORE_HEADER(Require, "Require");
class DateTimeHeaderPrivate;
class LINPHONE_PUBLIC DateTimeHeader : public Header {
friend class DateTimeHeaderNode;
public:
DateTimeHeader ();
DateTimeHeader (time_t time);
DateTimeHeader (const tm &time, const tm &timeOffset, const std::string &signOffset);
inline std::string getName () const override {
return "DateTime";
}
time_t getTime () const;
void setTime (const time_t time);
void setTime (const tm &time, const tm &timeOffset, const std::string &signOffset);
std::string getValue () const override;
std::string asString () const override;
private:
tm getTimeStruct () const;
tm getTimeOffset () const;
std::string getSignOffset () const;
L_DECLARE_PRIVATE(DateTimeHeader);
L_DISABLE_COPY(DateTimeHeader);
};
// -------------------------------------------------------------------------
// Specific Ns declaration.
// -------------------------------------------------------------------------
class NsHeaderPrivate;
class LINPHONE_PUBLIC NsHeader : public Header {
public:
NsHeader ();
NsHeader (const std::string &uri, const std::string &prefixName = "");
inline std::string getName () const override {
return "NS";
}
std::string getPrefixName () const;
void setPrefixName (const std::string &prefixName);
std::string getUri () const;
void setUri (const std::string &uri);
std::string getValue () const override;
std::string asString () const override;
private:
L_DECLARE_PRIVATE(NsHeader);
L_DISABLE_COPY(NsHeader);
};
// -------------------------------------------------------------------------
// Specific Require declaration.
// -------------------------------------------------------------------------
class RequireHeaderPrivate;
class LINPHONE_PUBLIC RequireHeader : public Header {
public:
RequireHeader ();
RequireHeader (const std::string &headerNames);
RequireHeader (const std::list<std::string> &headerNames);
inline std::string getName () const override {
return "Require";
}
std::list<std::string> getHeaderNames () const;
void addHeaderName (const std::string &headerName);
std::string getValue () const override;
std::string asString () const override;
private:
L_DECLARE_PRIVATE(RequireHeader);
L_DISABLE_COPY(RequireHeader);
};
// -------------------------------------------------------------------------
// Specific Subject declaration.
@ -81,33 +181,33 @@ namespace Cpim {
class SubjectHeaderPrivate;
class LINPHONE_PUBLIC SubjectHeader : public CoreHeader {
friend class HeaderNode;
class LINPHONE_PUBLIC SubjectHeader : public Header {
public:
SubjectHeader ();
SubjectHeader (const std::string &subject, const std::string &language = "");
inline std::string getName () const override {
return "Subject";
}
bool setValue (const std::string &value) override;
std::string getSubject () const;
void setSubject (const std::string &subject);
std::string getLanguage () const;
bool setLanguage (const std::string &language);
void setLanguage (const std::string &language);
std::string getValue () const override;
std::string asString () const override;
protected:
void force (const std::string &value, const std::string &language);
private:
L_DECLARE_PRIVATE(SubjectHeader);
L_DISABLE_COPY(SubjectHeader);
};
}
#undef MAKE_CORE_HEADER
#undef MAKE_CONTACT_HEADER
LINPHONE_END_NAMESPACE

View file

@ -34,38 +34,50 @@ LINPHONE_BEGIN_NAMESPACE
class Cpim::GenericHeaderPrivate : public HeaderPrivate {
public:
GenericHeaderPrivate () : parameters(make_shared<list<pair<string, string> > >()) {}
GenericHeaderPrivate () : parameters(make_shared<list<pair<string, string>>>()) {}
string name;
shared_ptr<list<pair<string, string> > > parameters;
string value;
shared_ptr<list<pair<string, string>>> parameters;
};
Cpim::GenericHeader::GenericHeader () : Header(*new GenericHeaderPrivate) {}
Cpim::GenericHeader::GenericHeader (string name, string value, string parameters) : GenericHeader() {
setName(name);
setValue(value);
for (const auto &parameter : Utils::split(parameters, ';')) {
size_t equalIndex = parameter.find('=');
if (equalIndex != string::npos)
addParameter(parameter.substr(0, equalIndex), parameter.substr(equalIndex + 1));
}
}
string Cpim::GenericHeader::getName () const {
L_D();
return d->name;
}
bool Cpim::GenericHeader::setName (const string &name) {
void Cpim::GenericHeader::setName (const string &name) {
L_D();
static const set<string> reserved = {
"From", "To", "cc", "DateTime", "Subject", "NS", "Require"
};
if (
reserved.find(name) != reserved.end() ||
!Parser::getInstance()->headerNameIsValid(name)
)
return false;
d->name = name;
return true;
if (reserved.find(name) == reserved.end())
d->name = name;
}
bool Cpim::GenericHeader::setValue (const string &value) {
return Parser::getInstance()->headerValueIsValid(value) && Header::setValue(value);
string Cpim::GenericHeader::getValue () const {
L_D();
return d->value;
}
void Cpim::GenericHeader::setValue (const string &value) {
L_D();
d->value = value;
}
Cpim::GenericHeader::ParameterList Cpim::GenericHeader::getParameters () const {
@ -73,14 +85,9 @@ Cpim::GenericHeader::ParameterList Cpim::GenericHeader::getParameters () const {
return d->parameters;
}
bool Cpim::GenericHeader::addParameter (const string &key, const string &value) {
void Cpim::GenericHeader::addParameter (const string &key, const string &value) {
L_D();
if (!Parser::getInstance()->headerParameterIsValid(key + "=" + value))
return false;
d->parameters->push_back(make_pair(key, value));
return true;
}
void Cpim::GenericHeader::removeParameter (const string &key, const string &value) {
@ -88,11 +95,6 @@ void Cpim::GenericHeader::removeParameter (const string &key, const string &valu
d->parameters->remove(make_pair(key, value));
}
bool Cpim::GenericHeader::isValid () const {
L_D();
return !d->name.empty() && !getValue().empty();
}
string Cpim::GenericHeader::asString () const {
L_D();
@ -103,21 +105,4 @@ string Cpim::GenericHeader::asString () const {
return d->name + ":" + parameters + " " + getValue() + "\r\n";
}
// -----------------------------------------------------------------------------
void Cpim::GenericHeader::force (const string &name, const string &value, const string &parameters) {
L_D();
// Set name/value.
d->name = name;
Header::setValue(value);
// Parse and build parameters list.
for (const auto &parameter : Utils::split(parameters, ';')) {
size_t equalIndex = parameter.find('=');
if (equalIndex != string::npos)
d->parameters->push_back(make_pair(parameter.substr(0, equalIndex), parameter.substr(equalIndex + 1)));
}
}
LINPHONE_END_NAMESPACE

View file

@ -38,24 +38,22 @@ namespace Cpim {
public:
GenericHeader ();
GenericHeader (std::string name, std::string value, std::string parameters = "");
std::string getName () const override;
bool setName (const std::string &name);
void setName (const std::string &name);
bool setValue (const std::string &value) override;
std::string getValue () const override;
void setValue (const std::string &value);
typedef std::shared_ptr<const std::list<std::pair<std::string, std::string> > > ParameterList;
typedef std::shared_ptr<const std::list<std::pair<std::string, std::string>>> ParameterList;
ParameterList getParameters () const;
bool addParameter (const std::string &key, const std::string &value);
void addParameter (const std::string &key, const std::string &value);
void removeParameter (const std::string &key, const std::string &value);
bool isValid () const override;
std::string asString () const override;
protected:
void force (const std::string &name, const std::string &value, const std::string &parameters);
private:
L_DECLARE_PRIVATE(GenericHeader);
L_DISABLE_COPY(GenericHeader);

View file

@ -30,8 +30,6 @@ LINPHONE_BEGIN_NAMESPACE
namespace Cpim {
class HeaderPrivate : public ObjectPrivate {
private:
std::string value;
L_DECLARE_PUBLIC(Header);
};
}

View file

@ -29,20 +29,4 @@ LINPHONE_BEGIN_NAMESPACE
Cpim::Header::Header (HeaderPrivate &p) : Object(p) {}
string Cpim::Header::getValue () const {
L_D();
return d->value;
}
bool Cpim::Header::setValue (const string &value) {
L_D();
d->value = value;
return true;
}
string Cpim::Header::asString () const {
L_D();
return getName() + ": " + d->value + "\r\n";
}
LINPHONE_END_NAMESPACE

View file

@ -35,12 +35,9 @@ namespace Cpim {
virtual std::string getName () const = 0;
std::string getValue () const;
virtual bool setValue (const std::string &value);
virtual std::string getValue () const = 0;
virtual bool isValid () const = 0;
virtual std::string asString () const;
virtual std::string asString () const = 0;
protected:
explicit Header (HeaderPrivate &p);

View file

@ -18,6 +18,7 @@
*/
#include <algorithm>
#include <map>
#include "linphone/utils/utils.h"
@ -36,10 +37,10 @@ LINPHONE_BEGIN_NAMESPACE
class Cpim::MessagePrivate : public ObjectPrivate {
public:
typedef list<shared_ptr<const Header> > PrivHeaderList;
using PrivHeaderList = list<shared_ptr<const Header>>;
using PrivHeaderMap = map<string, shared_ptr<PrivHeaderList>>;
shared_ptr<PrivHeaderList> cpimHeaders = make_shared<PrivHeaderList>(); // TODO: Remove this useless variable
shared_ptr<PrivHeaderList> messageHeaders = make_shared<PrivHeaderList>();
PrivHeaderMap messageHeaders;
shared_ptr<PrivHeaderList> contentHeaders = make_shared<PrivHeaderList>();
string content;
};
@ -48,50 +49,47 @@ Cpim::Message::Message () : Object(*new MessagePrivate) {}
// -----------------------------------------------------------------------------
Cpim::Message::HeaderList Cpim::Message::getCpimHeaders () const {
Cpim::Message::HeaderList Cpim::Message::getMessageHeaders (const string &ns) const {
L_D();
return d->cpimHeaders;
if (d->messageHeaders.find(ns) == d->messageHeaders.end())
return nullptr;
return d->messageHeaders.at(ns);
}
bool Cpim::Message::addCpimHeader (const Header &cpimHeader) {
void Cpim::Message::addMessageHeader (const Header &messageHeader, const string &ns) {
L_D();
if (!cpimHeader.isValid())
return false;
if (d->messageHeaders.find(ns) == d->messageHeaders.end())
d->messageHeaders[ns] = make_shared<Cpim::MessagePrivate::PrivHeaderList>();
d->cpimHeaders->push_back(Parser::getInstance()->cloneHeader(cpimHeader));
return true;
auto list = d->messageHeaders.at(ns);
list->push_back(Parser::getInstance()->cloneHeader(messageHeader));
}
void Cpim::Message::removeCpimHeader (const Header &cpimHeader) {
void Cpim::Message::removeMessageHeader (const Header &messageHeader, const string &ns) {
L_D();
d->cpimHeaders->remove_if([&cpimHeader](const shared_ptr<const Header> &header) {
return cpimHeader.getName() == header->getName() && cpimHeader.getValue() == header->getValue();
});
if (d->messageHeaders.find(ns) != d->messageHeaders.end())
d->messageHeaders.at(ns)->remove_if([&messageHeader](const shared_ptr<const Header> &header) {
return messageHeader.getName() == header->getName() && messageHeader.getValue() == header->getValue();
});
}
// -----------------------------------------------------------------------------
Cpim::Message::HeaderList Cpim::Message::getMessageHeaders () const {
L_D();
return d->messageHeaders;
}
bool Cpim::Message::addMessageHeader (const Header &messageHeader) {
shared_ptr<const Cpim::Header> Cpim::Message::getMessageHeader (const string &name, const string &ns) const {
L_D();
if (!messageHeader.isValid())
return false;
if (d->messageHeaders.find(ns) == d->messageHeaders.end())
return nullptr;
d->messageHeaders->push_back(Parser::getInstance()->cloneHeader(messageHeader));
return true;
}
auto list = d->messageHeaders.at(ns);
for (const auto &messageHeader : *list) {
if (messageHeader->getName() == name)
return messageHeader;
}
void Cpim::Message::removeMessageHeader (const Header &messageHeader) {
L_D();
d->messageHeaders->remove_if([&messageHeader](const shared_ptr<const Header> &header) {
return messageHeader.getName() == header->getName() && messageHeader.getValue() == header->getValue();
});
return nullptr;
}
// -----------------------------------------------------------------------------
@ -101,14 +99,9 @@ Cpim::Message::HeaderList Cpim::Message::getContentHeaders () const {
return d->contentHeaders;
}
bool Cpim::Message::addContentHeader (const Header &contentHeader) {
void Cpim::Message::addContentHeader (const Header &contentHeader) {
L_D();
if (!contentHeader.isValid())
return false;
d->contentHeaders->push_back(Parser::getInstance()->cloneHeader(contentHeader));
return true;
}
void Cpim::Message::removeContentHeader (const Header &contentHeader) {
@ -118,6 +111,17 @@ void Cpim::Message::removeContentHeader (const Header &contentHeader) {
});
}
shared_ptr<const Cpim::Header> Cpim::Message::getContentHeader(const string &name) const {
L_D();
for (const auto &contentHeader : *d->contentHeaders) {
if (contentHeader->getName() == name)
return contentHeader;
}
return nullptr;
}
// -----------------------------------------------------------------------------
string Cpim::Message::getContent () const {
@ -133,32 +137,19 @@ bool Cpim::Message::setContent (const string &content) {
// -----------------------------------------------------------------------------
bool Cpim::Message::isValid () const {
L_D();
return find_if(d->cpimHeaders->cbegin(), d->cpimHeaders->cend(),
[](const shared_ptr<const Header> &header) {
return Utils::iequals(header->getName(), "content-type") && (ContentType(header->getValue()) == ContentType::Cpim);
}) != d->cpimHeaders->cend();
}
// -----------------------------------------------------------------------------
string Cpim::Message::asString () const {
L_D();
string output;
// TODO: Remove cpimHeaders
if (d->cpimHeaders->size() > 0) {
for (const auto &cpimHeader : *d->cpimHeaders)
output += cpimHeader->asString();
output += "\r\n";
}
// TODO Remove cpimHeaders
if (d->messageHeaders->size() > 0) {
for (const auto &messageHeader : *d->messageHeaders)
output += messageHeader->asString();
if (d->messageHeaders.size() > 0) {
for (const auto &entry : d->messageHeaders) {
auto list = entry.second;
for (const auto &messageHeader : *list) {
if (entry.first != "")
output += entry.first + ".";
output += messageHeader->asString();
}
}
output += "\r\n";
}

View file

@ -34,27 +34,21 @@ namespace Cpim {
public:
Message ();
typedef std::shared_ptr<std::list<std::shared_ptr<const Cpim::Header> > > HeaderList;
typedef std::shared_ptr<std::list<std::shared_ptr<const Cpim::Header>>> HeaderList;
// TODO: Remove these useless methods
HeaderList getCpimHeaders () const;
bool addCpimHeader (const Header &cpimHeader);
void removeCpimHeader (const Header &cpimHeader);
// TODO: Remove these useless methods
HeaderList getMessageHeaders (const std::string &ns = "") const;
void addMessageHeader (const Header &messageHeader, const std::string &ns = "");
void removeMessageHeader (const Header &messageHeader, const std::string &ns = "");
std::shared_ptr<const Cpim::Header> getMessageHeader (const std::string &name, const std::string &ns = "") const;
HeaderList getMessageHeaders () const;
bool addMessageHeader (const Header &messageHeader);
void removeMessageHeader (const Header &messageHeader);
HeaderList getContentHeaders () const;
bool addContentHeader (const Header &contentHeader);
void addContentHeader (const Header &contentHeader);
void removeContentHeader (const Header &contentHeader);
std::shared_ptr<const Cpim::Header> getContentHeader (const std::string &name) const;
std::string getContent () const;
bool setContent (const std::string &content);
bool isValid () const; // TODO: Remove this useless method
std::string asString () const;
static std::shared_ptr<const Message> createFromString (const std::string &str);

View file

@ -17,7 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <unordered_map>
#include <set>
#include <belr/abnf.h>
#include <belr/grammarbuilder.h>
@ -53,20 +53,10 @@ namespace Cpim {
HeaderNode () = default;
explicit HeaderNode (const Header &header) : mName(header.getName()), mValue(header.getValue()) {
// Generic header.
const GenericHeader *genericHeader = dynamic_cast<const GenericHeader *>(&header);
if (genericHeader) {
for (const auto &parameter : *genericHeader->getParameters())
mParameters += ";" + parameter.first + "=" + parameter.second;
return;
}
// Subject header.
const SubjectHeader *subjectHeader = dynamic_cast<const SubjectHeader *>(&header);
if (subjectHeader) {
const string language = subjectHeader->getLanguage();
if (!language.empty())
mParameters = ";lang=" + language;
}
}
@ -75,7 +65,12 @@ namespace Cpim {
}
void setName (const string &name) {
mName = name;
static const set<string> reserved = {
"From", "To", "cc", "DateTime", "Subject", "NS", "Require"
};
if (reserved.find(name) == reserved.end())
mName = name;
}
string getParameters () const {
@ -94,67 +89,425 @@ namespace Cpim {
mValue = value;
}
shared_ptr<Header> createHeader (bool force) const;
virtual shared_ptr<Header> createHeader () const;
virtual bool isValid () const;
private:
template<typename T>
shared_ptr<Header> createCoreHeader (bool force) const {
shared_ptr<T> header = make_shared<T>();
if (force)
header->force(mValue);
else if (!header->setValue(mValue)) {
lWarning() << "Unable to set value on core header: `" << mName << "` => `" << mValue << "`.";
return nullptr;
}
return header;
}
string mName;
string mValue;
string mParameters;
};
template<>
shared_ptr<Header> HeaderNode::createCoreHeader<SubjectHeader>(bool force) const {
shared_ptr<SubjectHeader> header = make_shared<SubjectHeader>();
const string language = mParameters.length() >= 6 ? mParameters.substr(6) : "";
if (force)
header->force(mValue, language);
else if (!header->setValue(mValue) || (!language.empty() && !header->setLanguage(language))) {
lWarning() << "Unable to set value on subject header: `" <<
mName << "` => `" << mValue << "`, `" << language << "`.";
return nullptr;
}
return header;
bool HeaderNode::isValid () const {
return !mName.empty() && !mValue.empty();
}
shared_ptr<Header> HeaderNode::createHeader (bool force = false) const {
static const unordered_map<string, shared_ptr<Header>(HeaderNode::*)(bool)const> reservedHandlers = {
{ "From", &HeaderNode::createCoreHeader<FromHeader> },
{ "To", &HeaderNode::createCoreHeader<ToHeader> },
{ "cc", &HeaderNode::createCoreHeader<CcHeader> },
{ "DateTime", &HeaderNode::createCoreHeader<DateTimeHeader> },
{ "Subject", &HeaderNode::createCoreHeader<SubjectHeader> },
{ "NS", &HeaderNode::createCoreHeader<NsHeader> },
{ "Require", &HeaderNode::createCoreHeader<RequireHeader> }
};
shared_ptr<Header> HeaderNode::createHeader () const {
if (!isValid())
return nullptr;
// Core Header.
const auto it = reservedHandlers.find(mName);
if (it != reservedHandlers.cend())
return (this->*it->second)(force);
// Generic Header
shared_ptr<GenericHeader> genericHeader = make_shared<GenericHeader>();
genericHeader->force(mName, mValue, mParameters);
genericHeader->setName(mName);
for (const auto &parameter : Utils::split(mParameters, ';')) {
size_t equalIndex = parameter.find('=');
if (equalIndex != string::npos)
genericHeader->addParameter(parameter.substr(0, equalIndex), parameter.substr(equalIndex + 1));
}
genericHeader->setValue(mValue);
return genericHeader;
}
// -------------------------------------------------------------------------
class ContactHeaderNode : public HeaderNode {
public:
ContactHeaderNode () = default;
string getFormalName () const {
return mFormalName;
}
void setFormalName (const string &formalName) {
mFormalName = formalName;
}
string getUri () const {
return mUri;
}
void setUri (const string &uri) {
mUri = uri;
}
bool isValid () const override;
private:
string mFormalName;
string mUri;
};
bool ContactHeaderNode::isValid () const {
return !mUri.empty();
}
// -------------------------------------------------------------------------
class FromHeaderNode : public ContactHeaderNode {
public:
FromHeaderNode () = default;
explicit FromHeaderNode (const Header &header) {
const FromHeader *fromHeader = dynamic_cast<const FromHeader *>(&header);
if (fromHeader) {
setFormalName(fromHeader->getFormalName());
setUri(fromHeader->getUri());
}
}
shared_ptr<Header> createHeader () const override;
};
shared_ptr<Header> FromHeaderNode::createHeader () const {
if (!isValid())
return nullptr;
return make_shared<FromHeader>(getUri(), getFormalName());
}
// -------------------------------------------------------------------------
class ToHeaderNode : public ContactHeaderNode {
public:
ToHeaderNode () = default;
explicit ToHeaderNode (const Header &header) {
const ToHeader *toHeader = dynamic_cast<const ToHeader *>(&header);
if (toHeader) {
setFormalName(toHeader->getFormalName());
setUri(toHeader->getUri());
}
}
shared_ptr<Header> createHeader () const override;
};
shared_ptr<Header> ToHeaderNode::createHeader () const {
if (!isValid())
return nullptr;
return make_shared<ToHeader>(getUri(), getFormalName());
}
// -------------------------------------------------------------------------
class CcHeaderNode : public ContactHeaderNode {
public:
CcHeaderNode () = default;
explicit CcHeaderNode (const Header &header) {
const CcHeader *ccHeader = dynamic_cast<const CcHeader *>(&header);
if (ccHeader) {
setFormalName(ccHeader->getFormalName());
setUri(ccHeader->getUri());
}
}
shared_ptr<Header> createHeader () const override;
};
shared_ptr<Header> CcHeaderNode::createHeader () const {
if (!isValid())
return nullptr;
return make_shared<CcHeader>(getUri(), getFormalName());
}
// -------------------------------------------------------------------------
class DateTimeOffsetNode : public Node {
friend class DateTimeHeaderNode;
public:
DateTimeOffsetNode () {
mSign = "Z";
}
void setHour (const string &value) {
mHour = Utils::stoi(value);
}
void setMinute (const string &value) {
mMinute = Utils::stoi(value);
}
void setSign (const string &value) {
mSign = value;
}
private:
string mSign;
int mHour;
int mMinute;
};
class DateTimeHeaderNode : public HeaderNode {
public:
DateTimeHeaderNode () = default;
explicit DateTimeHeaderNode (const Header &header) {
const DateTimeHeader *dateTimeHeader = dynamic_cast<const DateTimeHeader *>(&header);
if (dateTimeHeader) {
setTime(dateTimeHeader->getTimeStruct());
setTimeOffset(dateTimeHeader->getTimeOffset());
setSignOffset(dateTimeHeader->getSignOffset());
}
}
struct tm getTime () const {
return mTime;
}
void setTime (const struct tm &time) {
mTime = time;
}
struct tm getTimeOffset () const {
return mTimeOffset;
}
void setTimeOffset (const struct tm &timeOffset) {
mTimeOffset = timeOffset;
}
string getSignOffset () const {
return mSignOffset;
}
void setSignOffset (const string &signOffset) {
mSignOffset = signOffset;
}
void setYear (const string &value) {
mTime.tm_year = Utils::stoi(value);
}
void setMonth (const string &value) {
mTime.tm_mon = Utils::stoi(value) - 1;
}
void setMonthDay (const string &value) {
mTime.tm_mday = Utils::stoi(value);
}
void setHour (const string &value) {
mTime.tm_hour = Utils::stoi(value);
}
void setMinute (const string &value) {
mTime.tm_min = Utils::stoi(value);
}
void setSecond (const string &value) {
mTime.tm_sec = Utils::stoi(value);
}
void setOffset (const shared_ptr<DateTimeOffsetNode> &offset) {
mTimeOffset.tm_hour = offset->mHour;
mTimeOffset.tm_min = offset->mMinute;
mSignOffset = offset->mSign;
}
bool isValid () const override;
shared_ptr<Header> createHeader() const override;
private:
tm mTime;
tm mTimeOffset;
string mSignOffset;
};
bool DateTimeHeaderNode::isValid () const {
static const int daysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// Check date.
const bool isLeapYear = (mTime.tm_year % 4 == 0 && mTime.tm_year % 100 != 0) || mTime.tm_year % 400 == 0;
if (mTime.tm_mon < 1 || mTime.tm_mon > 12)
return false;
if (mTime.tm_mday < 1 || (mTime.tm_mon == 2 && isLeapYear ? mTime.tm_mday > 29 : mTime.tm_mday > daysInMonth[mTime.tm_mon - 1]))
return false;
// Check time.
if (mTime.tm_hour > 24 || mTime.tm_min > 59 || mTime.tm_sec > 60)
return false;
// Check num offset.
if (mSignOffset != "Z") {
if (mTimeOffset.tm_hour > 24 || mTime.tm_min > 59)
return false;
}
return true;
}
shared_ptr<Header> DateTimeHeaderNode::createHeader () const {
if (!isValid())
return nullptr;
return make_shared<DateTimeHeader>(getTime(), getTimeOffset(), getSignOffset());
}
// -------------------------------------------------------------------------
class SubjectHeaderNode : public HeaderNode {
public:
SubjectHeaderNode () = default;
explicit SubjectHeaderNode (const Header &header) {
const SubjectHeader *subjectHeader = dynamic_cast<const SubjectHeader *>(&header);
if (subjectHeader) {
setLanguage(subjectHeader->getLanguage());
setSubject(subjectHeader->getSubject());
}
}
string getLanguage () const {
return mLanguage;
}
void setLanguage (const string &language) {
mLanguage = language;
}
string getSubject () const {
return mSubject;
}
void setSubject (const string &subject) {
mSubject = subject;
}
bool isValid () const override;
shared_ptr<Header> createHeader () const override;
private:
string mLanguage;
string mSubject;
};
bool SubjectHeaderNode::isValid () const {
return !mSubject.empty();
}
shared_ptr<Header> SubjectHeaderNode::createHeader () const {
if (!isValid())
return nullptr;
return make_shared<SubjectHeader>(getSubject(), getLanguage());
}
// -------------------------------------------------------------------------
class NsHeaderNode : public HeaderNode {
public:
NsHeaderNode () = default;
explicit NsHeaderNode (const Header &header) {
const NsHeader *nsHeader = dynamic_cast<const NsHeader *>(&header);
if (nsHeader) {
setPrefixName(nsHeader->getPrefixName());
setUri(nsHeader->getUri());
}
}
string getPrefixName () const {
return mPrefixName;
}
void setPrefixName (const string &prefixName) {
mPrefixName = prefixName;
}
string getUri () const {
return mUri;
}
void setUri (const string &uri) {
mUri = uri;
}
bool isValid () const override;
shared_ptr<Header> createHeader () const override;
private:
string mPrefixName;
string mUri;
};
bool NsHeaderNode::isValid () const {
return !mUri.empty();
}
shared_ptr<Header> NsHeaderNode::createHeader () const {
if (!isValid())
return nullptr;
return make_shared<NsHeader>(getUri(), getPrefixName());
}
// -------------------------------------------------------------------------
class RequireHeaderNode : public HeaderNode {
public:
RequireHeaderNode () = default;
explicit RequireHeaderNode (const Header &header) {
const RequireHeader *requireHeader = dynamic_cast<const RequireHeader *>(&header);
if (requireHeader) {
for (const auto &header : requireHeader->getHeaderNames()) {
if (header != requireHeader->getHeaderNames().front())
mHeaderNames += ",";
mHeaderNames += header;
}
}
}
string getHeaderNames () const {
return mHeaderNames;
}
void setHeaderNames (const string &headerNames) {
mHeaderNames = headerNames;
}
bool isValid () const override;
shared_ptr<Header> createHeader () const override;
private:
string mHeaderNames;
};
bool RequireHeaderNode::isValid () const {
return !mHeaderNames.empty();
}
shared_ptr<Header> RequireHeaderNode::createHeader () const {
if (!isValid())
return nullptr;
return make_shared<RequireHeader>(mHeaderNames);
}
// -------------------------------------------------------------------------
class ListHeaderNode :
public Node,
public list<shared_ptr<HeaderNode> > {};
@ -163,67 +516,61 @@ namespace Cpim {
class MessageNode : public Node {
public:
void addHeaders (const shared_ptr<ListHeaderNode> &headers) {
mHeaders->push_back(headers);
void addMessageHeaders (const shared_ptr<ListHeaderNode> &headers) {
for (const auto &headerNode : *headers) {
mMessageHeaders.push_back(headerNode);
}
}
void addContentHeaders (const shared_ptr<ListHeaderNode> &headers) {
for (const auto &headerNode : *headers) {
mContentHeaders.push_back(headerNode);
}
}
// Warning: Call this function one time!
shared_ptr<Message> createMessage () const {
size_t size = mHeaders->size();
if (size < 2 || size > 3) { // TODO: Check that size is == 2
if (mContentHeaders.empty() || mMessageHeaders.empty()) {
lWarning() << "Bad headers lists size.";
return nullptr;
}
//TODO: Verify all headers from other namespaces
const shared_ptr<Message> message = make_shared<Message>();
// TODO: To remove
if (size == 3) {
const shared_ptr<ListHeaderNode> cpimHeaders = mHeaders->front();
if (find_if(cpimHeaders->cbegin(), cpimHeaders->cend(),
[](const shared_ptr<const HeaderNode> &headerNode) {
return Utils::iequals(headerNode->getName(), "content-type") && (ContentType(headerNode->getValue()) == ContentType::Cpim);
}) == cpimHeaders->cend()) {
lWarning() << "No MIME `Content-Type` found!";
// Add message headers.
for (const auto &headerNode : mMessageHeaders) {
string ns = "";
string::size_type n = headerNode->getName().find(".");
if (n != string::npos) {
ns = headerNode->getName().substr(0, n);
headerNode->setName(headerNode->getName().substr(n + 1));
}
const shared_ptr<const Header> header = headerNode->createHeader();
if (!header)
return nullptr;
}
// Add MIME headers.
for (const auto &headerNode : *cpimHeaders) {
const shared_ptr<const Header> header = headerNode->createHeader();
if (!header || !message->addCpimHeader(*header))
return nullptr;
}
// Add message headers.
for (const auto &headerNode : **(++mHeaders->cbegin())) {
const shared_ptr<const Header> header = headerNode->createHeader();
if (!header || !message->addMessageHeader(*header))
return nullptr;
}
}
// TODO: To remove
else {
// Add message headers.
for (const auto &headerNode : *mHeaders->front()) {
const shared_ptr<const Header> header = headerNode->createHeader();
if (!header || !message->addMessageHeader(*header))
return nullptr;
}
message->addMessageHeader(*header, ns);
}
// Add content headers.
for (const auto &headerNode : *mHeaders->back()) {
for (const auto &headerNode : mContentHeaders) {
const shared_ptr<const Header> header = headerNode->createHeader();
if (!header || !message->addContentHeader(*header))
if (!header)
return nullptr;
message->addContentHeader(*header);
}
return message;
}
private:
shared_ptr<list<shared_ptr<ListHeaderNode> > > mHeaders = make_shared<list<shared_ptr<ListHeaderNode> > >();
list<shared_ptr<HeaderNode>> mContentHeaders;
list<shared_ptr<HeaderNode>> mMessageHeaders;
};
}
@ -260,27 +607,64 @@ shared_ptr<Cpim::Message> Cpim::Parser::parseMessage (const string &input) {
typedef void (list<shared_ptr<HeaderNode> >::*pushPtr)(const shared_ptr<HeaderNode> &value);
belr::Parser<shared_ptr<Node> > parser(d->grammar);
parser.setHandler(
"Message", belr::make_fn(make_shared<MessageNode> )
)->setCollector(
"Headers", belr::make_sfn(&MessageNode::addHeaders)
);
parser.setHandler("Message", belr::make_fn(make_shared<MessageNode>))
->setCollector("Message-headers", belr::make_sfn(&MessageNode::addMessageHeaders))
->setCollector("Content-headers", belr::make_sfn(&MessageNode::addContentHeaders));
parser.setHandler(
"Headers", belr::make_fn(make_shared<ListHeaderNode> )
)->setCollector(
"Header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back))
);
parser.setHandler("Message-headers", belr::make_fn(make_shared<ListHeaderNode>))
->setCollector("Header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)))
->setCollector("From-header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)))
->setCollector("To-header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)))
->setCollector("DateTime-header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)))
->setCollector("cc-header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)))
->setCollector("Subject-header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)))
->setCollector("NS-header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)))
->setCollector("Require-header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)));
parser.setHandler(
"Header", belr::make_fn(make_shared<HeaderNode> )
)->setCollector(
"Header-name", belr::make_sfn(&HeaderNode::setName)
)->setCollector(
"Header-value", belr::make_sfn(&HeaderNode::setValue)
)->setCollector(
"Header-parameters", belr::make_sfn(&HeaderNode::setParameters)
);
parser.setHandler("Content-headers", belr::make_fn(make_shared<ListHeaderNode>))
->setCollector("Header", belr::make_sfn(static_cast<pushPtr>(&ListHeaderNode::push_back)));
parser.setHandler("Header", belr::make_fn(make_shared<HeaderNode>))
->setCollector("Header-name", belr::make_sfn(&HeaderNode::setName))
->setCollector("Header-value", belr::make_sfn(&HeaderNode::setValue))
->setCollector("Header-parameters", belr::make_sfn(&HeaderNode::setParameters));
parser.setHandler("From-header", belr::make_fn(make_shared<FromHeaderNode>))
->setCollector("Formal-name", belr::make_sfn(&FromHeaderNode::setFormalName))
->setCollector("URI", belr::make_sfn(&FromHeaderNode::setUri));
parser.setHandler("To-header", belr::make_fn(make_shared<ToHeaderNode>))
->setCollector("Formal-name", belr::make_sfn(&ToHeaderNode::setFormalName))
->setCollector("URI", belr::make_sfn(&ToHeaderNode::setUri));
parser.setHandler("cc-header", belr::make_fn(make_shared<CcHeaderNode>))
->setCollector("Formal-name", belr::make_sfn(&CcHeaderNode::setFormalName))
->setCollector("URI", belr::make_sfn(&CcHeaderNode::setUri));
parser.setHandler("DateTime-header", belr::make_fn(make_shared<DateTimeHeaderNode>))
->setCollector("date-fullyear", belr::make_sfn(&DateTimeHeaderNode::setYear))
->setCollector("date-month", belr::make_sfn(&DateTimeHeaderNode::setMonth))
->setCollector("date-mday", belr::make_sfn(&DateTimeHeaderNode::setMonthDay))
->setCollector("time-hour", belr::make_sfn(&DateTimeHeaderNode::setHour))
->setCollector("time-minute", belr::make_sfn(&DateTimeHeaderNode::setMinute))
->setCollector("time-second", belr::make_sfn(&DateTimeHeaderNode::setSecond))
->setCollector("time-offset", belr::make_sfn(&DateTimeHeaderNode::setOffset));
parser.setHandler("time-offset", belr::make_fn(make_shared<DateTimeOffsetNode>))
->setCollector("time-sign", belr::make_sfn(&DateTimeOffsetNode::setSign))
->setCollector("time-hour", belr::make_sfn(&DateTimeOffsetNode::setHour))
->setCollector("time-minute", belr::make_sfn(&DateTimeOffsetNode::setMinute));
parser.setHandler("Subject-header", belr::make_fn(make_shared<SubjectHeaderNode>))
->setCollector("Language-tag", belr::make_sfn(&SubjectHeaderNode::setLanguage))
->setCollector("Header-value", belr::make_sfn(&SubjectHeaderNode::setSubject));
parser.setHandler("Ns-header", belr::make_fn(make_shared<NsHeaderNode>))
->setCollector("Name-prefix", belr::make_sfn(&NsHeaderNode::setPrefixName))
->setCollector("URI", belr::make_sfn(&NsHeaderNode::setUri));
parser.setHandler("Require-header", belr::make_fn(make_shared<RequireHeaderNode>))
->setCollector("Require-header-value", belr::make_sfn(&RequireHeaderNode::setHeaderNames));
size_t parsedSize;
shared_ptr<Node> node = parser.parseInput("Message", input, &parsedSize);
@ -305,143 +689,28 @@ shared_ptr<Cpim::Message> Cpim::Parser::parseMessage (const string &input) {
// -----------------------------------------------------------------------------
shared_ptr<Cpim::Header> Cpim::Parser::cloneHeader (const Header &header) {
return HeaderNode(header).createHeader(true);
}
if (header.getName() == "From")
return FromHeaderNode(header).createHeader();
// -----------------------------------------------------------------------------
if (header.getName() == "To")
return ToHeaderNode(header).createHeader();
class EmptyObject {};
if (header.getName() == "cc")
return CcHeaderNode(header).createHeader();
static bool headerIsValid (const shared_ptr<belr::Grammar> &grammar, const string &input) {
belr::Parser<shared_ptr<EmptyObject> > parser(grammar);
parser.setHandler(
"Header", belr::make_fn(make_shared<EmptyObject> )
);
if (header.getName() == "DateTime")
return DateTimeHeaderNode(header).createHeader();
size_t parsedSize;
shared_ptr<EmptyObject> node = parser.parseInput("Header", input, &parsedSize);
return node && parsedSize == input.length();
}
if (header.getName() == "Subject")
return SubjectHeaderNode(header).createHeader();
bool Cpim::Parser::headerNameIsValid (const string &headerName) const {
L_D();
return headerIsValid(d->grammar, headerName + ": value\r\n");
}
if (header.getName() == "NS")
return NsHeaderNode(header).createHeader();
bool Cpim::Parser::headerValueIsValid (const string &headerValue) const {
L_D();
return headerIsValid(d->grammar, "key: " + headerValue + "\r\n");
}
if (header.getName() == "Require")
return RequireHeaderNode(header).createHeader();
bool Cpim::Parser::headerParameterIsValid (const string &headerParameter) const {
L_D();
return headerIsValid(d->grammar, "key:;" + headerParameter + " value\r\n");
}
// -----------------------------------------------------------------------------
static bool coreHeaderIsValid (
const shared_ptr<belr::Grammar> &grammar,
const string &headerName,
const string &headerValue,
const string &headerParams = string()
) {
const string mainRule = headerName + "-header";
belr::Parser<shared_ptr<EmptyObject> > parser(grammar);
parser.setHandler(
mainRule, belr::make_fn(make_shared<EmptyObject> )
);
const string input = headerName + ":" + headerParams + " " + headerValue;
size_t parsedSize;
shared_ptr<EmptyObject> node = parser.parseInput(mainRule, input, &parsedSize);
return node && parsedSize == input.length();
}
template<>
bool Cpim::Parser::coreHeaderIsValid<Cpim::FromHeader> (const string &headerValue) const {
L_D();
return LinphonePrivate::coreHeaderIsValid(d->grammar, "From", headerValue);
}
template<>
bool Cpim::Parser::coreHeaderIsValid<Cpim::ToHeader> (const string &headerValue) const {
L_D();
return LinphonePrivate::coreHeaderIsValid(d->grammar, "To", headerValue);
}
template<>
bool Cpim::Parser::coreHeaderIsValid<Cpim::CcHeader> (const string &headerValue) const {
L_D();
return LinphonePrivate::coreHeaderIsValid(d->grammar, "cc", headerValue);
}
template<>
bool Cpim::Parser::coreHeaderIsValid<Cpim::DateTimeHeader> (const string &headerValue) const {
static const int daysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
L_D();
if (!LinphonePrivate::coreHeaderIsValid(d->grammar, "DateTime", headerValue))
return false;
// Check date.
const int year = Utils::stoi(headerValue.substr(0, 4));
const bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
const int month = Utils::stoi(headerValue.substr(5, 2));
if (month < 1 || month > 12)
return false;
const int day = Utils::stoi(headerValue.substr(8, 2));
if (day < 1 || (month == 2 && isLeapYear ? day > 29 : day > daysInMonth[month - 1]))
return false;
// Check time.
if (
Utils::stoi(headerValue.substr(11, 2)) > 24 ||
Utils::stoi(headerValue.substr(14, 2)) > 59 ||
Utils::stoi(headerValue.substr(17, 2)) > 60
)
return false;
// Check num offset.
if (headerValue.back() != 'Z') {
size_t length = headerValue.length();
if (
Utils::stoi(headerValue.substr(length - 5, 2)) > 24 ||
Utils::stoi(headerValue.substr(length - 2, 2)) > 59
)
return false;
}
return true;
}
template<>
bool Cpim::Parser::coreHeaderIsValid<Cpim::SubjectHeader> (const string &headerValue) const {
L_D();
return LinphonePrivate::coreHeaderIsValid(d->grammar, "Subject", headerValue);
}
template<>
bool Cpim::Parser::coreHeaderIsValid<Cpim::NsHeader> (const string &headerValue) const {
L_D();
return LinphonePrivate::coreHeaderIsValid(d->grammar, "NS", headerValue);
}
template<>
bool Cpim::Parser::coreHeaderIsValid<Cpim::RequireHeader> (const string &headerValue) const {
L_D();
return LinphonePrivate::coreHeaderIsValid(d->grammar, "Require", headerValue);
}
// -----------------------------------------------------------------------------
bool Cpim::Parser::subjectHeaderLanguageIsValid (const string &language) const {
L_D();
return LinphonePrivate::coreHeaderIsValid(d->grammar, "Subject", "SubjectValue", ";lang=" + language);
return HeaderNode(header).createHeader();
}
LINPHONE_END_NAMESPACE

View file

@ -38,46 +38,12 @@ namespace Cpim {
std::shared_ptr<Header> cloneHeader (const Header &header);
bool headerNameIsValid (const std::string &headerName) const;
bool headerValueIsValid (const std::string &headerValue) const;
bool headerParameterIsValid (const std::string &headerParameter) const;
template<typename>
bool coreHeaderIsValid (const std::string &headerValue) const {
return false;
}
bool subjectHeaderLanguageIsValid (const std::string &language) const;
private:
Parser ();
L_DECLARE_PRIVATE(Parser);
L_DISABLE_COPY(Parser);
};
// ---------------------------------------------------------------------------
template<>
bool Parser::coreHeaderIsValid<FromHeader>(const std::string &headerValue) const;
template<>
bool Parser::coreHeaderIsValid<ToHeader>(const std::string &headerValue) const;
template<>
bool Parser::coreHeaderIsValid<CcHeader>(const std::string &headerValue) const;
template<>
bool Parser::coreHeaderIsValid<DateTimeHeader>(const std::string &headerValue) const;
template<>
bool Parser::coreHeaderIsValid<SubjectHeader>(const std::string &headerValue) const;
template<>
bool Parser::coreHeaderIsValid<NsHeader>(const std::string &headerValue) const;
template<>
bool Parser::coreHeaderIsValid<RequireHeader>(const std::string &headerValue) const;
}
LINPHONE_END_NAMESPACE

View file

@ -1,7 +1,11 @@
Message = Headers CRLF Headers CRLF [Headers CRLF]
Message = Message-headers CRLF Content-headers CRLF
Headers = *Header
Header = Header-name ":" Header-parameters SP Header-value CRLF
Message-headers = 1*( Message-header CRLF )
Message-header = From-header / To-header / DateTime-header / cc-header / Subject-header / NS-header / Require-header / Header
Content-headers = 1*( Header CRLF )
Header = Header-name ":" Header-parameters SP Header-value
Header-name = [ Name-prefix "." ] Name
Name-prefix = Name
@ -29,7 +33,7 @@ cc-header = %d99.99 ": " cc-header-value
cc-header-value = [ Formal-name ] "<" URI ">"
Subject-header = %d83.117.98.106.101.99.116 ":" Subject-header-value
Subject-header-value = [ ";" Lang-param ] SP *HEADERCHAR
Subject-header-value = [ ";" Lang-param ] SP Header-value
NS-header = %d78.83 ": " NS-header-value
NS-header-value = [ Name-prefix SP ] "<" URI ">"
@ -134,7 +138,8 @@ time-minute = 2DIGIT
time-second = 2DIGIT
time-secfrac = "." 1*DIGIT
time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
time-sign = "+" / "-"
time-numoffset = time-sign time-hour ":" time-minute
time-offset = "Z" / time-numoffset
partial-time = time-hour ":" time-minute ":" time-second [ time-secfrac ]

View file

@ -38,32 +38,30 @@ LINPHONE_BEGIN_NAMESPACE
ChatMessageModifier::Result CpimChatMessageModifier::encode (const shared_ptr<ChatMessage> &message, int &errorCode) {
Cpim::Message cpimMessage;
Cpim::FromHeader cpimFromHeader;
cpimFromHeader.setValue(cpimAddressAsString(message->getFromAddress()));
cpimMessage.addMessageHeader(cpimFromHeader);
Cpim::ToHeader cpimToHeader;
cpimToHeader.setValue(cpimAddressAsString(message->getToAddress()));
cpimMessage.addMessageHeader(cpimToHeader);
cpimMessage.addMessageHeader(
Cpim::FromHeader(cpimAddressUri(message->getFromAddress()), cpimAddressDisplayName(message->getFromAddress()))
);
cpimMessage.addMessageHeader(
Cpim::ToHeader(cpimAddressUri(message->getToAddress()), cpimAddressDisplayName(message->getToAddress()))
);
cpimMessage.addMessageHeader(
Cpim::DateTimeHeader(message->getTime())
);
if (message->getPrivate()->getPositiveDeliveryNotificationRequired()
|| message->getPrivate()->getNegativeDeliveryNotificationRequired()
|| message->getPrivate()->getDisplayNotificationRequired()
) {
const string imdnNamespace = "imdn";
Cpim::NsHeader cpimNsHeader;
cpimNsHeader.setValue(imdnNamespace + " <urn:ietf:params:imdn>");
cpimMessage.addMessageHeader(cpimNsHeader);
cpimMessage.addMessageHeader(Cpim::NsHeader("urn:ietf:params:imdn", imdnNamespace));
char token[13];
belle_sip_random_token(token, sizeof(token));
Cpim::GenericHeader cpimMessageIdHeader;
cpimMessageIdHeader.setName("Message-ID"); // TODO: Replace by imdnNamespace + ".Message-ID");
cpimMessageIdHeader.setValue(token);
cpimMessage.addMessageHeader(cpimMessageIdHeader);
cpimMessage.addMessageHeader(
Cpim::GenericHeader("Message-ID", token) // TODO: Replace by imdnNamespace + ".Message-ID");
);
message->getPrivate()->setImdnMessageId(token);
Cpim::GenericHeader dispositionNotificationHeader;
dispositionNotificationHeader.setName(imdnNamespace + ".Disposition-Notification");
vector<string> dispositionNotificationValues;
if (message->getPrivate()->getPositiveDeliveryNotificationRequired())
dispositionNotificationValues.emplace_back("positive-delivery");
@ -71,8 +69,12 @@ ChatMessageModifier::Result CpimChatMessageModifier::encode (const shared_ptr<Ch
dispositionNotificationValues.emplace_back("negative-delivery");
if (message->getPrivate()->getDisplayNotificationRequired())
dispositionNotificationValues.emplace_back("display");
dispositionNotificationHeader.setValue(Utils::join(dispositionNotificationValues, ", "));
cpimMessage.addMessageHeader(dispositionNotificationHeader);
cpimMessage.addMessageHeader(
Cpim::GenericHeader(
imdnNamespace + ".Disposition-Notification",
Utils::join(dispositionNotificationValues, ", ")
)
);
}
const Content *content;
@ -88,19 +90,16 @@ ChatMessageModifier::Result CpimChatMessageModifier::encode (const shared_ptr<Ch
const string contentBody = content->getBodyAsString();
if (content->getContentDisposition().isValid()) {
Cpim::GenericHeader contentDispositionHeader;
contentDispositionHeader.setName("Content-Disposition");
contentDispositionHeader.setValue(content->getContentDisposition().asString());
cpimMessage.addContentHeader(contentDispositionHeader);
cpimMessage.addContentHeader(
Cpim::GenericHeader("Content-Disposition", content->getContentDisposition().asString())
);
}
Cpim::GenericHeader contentTypeHeader;
contentTypeHeader.setName("Content-Type");
contentTypeHeader.setValue(content->getContentType().asString());
cpimMessage.addContentHeader(contentTypeHeader);
Cpim::GenericHeader contentLengthHeader;
contentLengthHeader.setName("Content-Length");
contentLengthHeader.setValue(to_string(contentBody.size()));
cpimMessage.addContentHeader(contentLengthHeader);
cpimMessage.addContentHeader(
Cpim::GenericHeader("Content-Type", content->getContentType().asString())
);
cpimMessage.addContentHeader(
Cpim::GenericHeader("Content-Length", to_string(contentBody.size()))
);
cpimMessage.setContent(contentBody);
Content newContent;
@ -125,76 +124,70 @@ ChatMessageModifier::Result CpimChatMessageModifier::decode (const shared_ptr<Ch
const string contentBody = content->getBodyAsString();
const shared_ptr<const Cpim::Message> cpimMessage = Cpim::Message::createFromString(contentBody);
if (!cpimMessage) {
if (!cpimMessage || !cpimMessage->getMessageHeader("From") || !cpimMessage->getMessageHeader("To")) {
lError() << "[CPIM] Message is invalid: " << contentBody;
errorCode = 500;
return ChatMessageModifier::Result::Error;
}
Content newContent;
bool contentTypeFound = false;
Cpim::Message::HeaderList l = cpimMessage->getContentHeaders();
if (l) {
for (const auto &header : *l.get()) {
if (header->getName() == "Content-Disposition") {
newContent.setContentDisposition(ContentDisposition(header->getValue()));
} else if (header->getName() == "Content-Type") {
contentTypeFound = true;
newContent.setContentType(ContentType(header->getValue()));
}
}
}
if (!contentTypeFound) {
auto contentTypeHeader = cpimMessage->getContentHeader("Content-Type");
if (!contentTypeHeader) {
lError() << "[CPIM] No Content-type for the content of the message";
errorCode = 500;
return ChatMessageModifier::Result::Error;
}
newContent.setContentType(ContentType(contentTypeHeader->getValue()));
auto contentDispositionHeader = cpimMessage->getContentHeader("Content-Disposition");
if (contentDispositionHeader)
newContent.setContentDisposition(ContentDisposition(contentDispositionHeader->getValue()));
newContent.setBody(cpimMessage->getContent());
message->getPrivate()->setPositiveDeliveryNotificationRequired(false);
message->getPrivate()->setNegativeDeliveryNotificationRequired(false);
message->getPrivate()->setDisplayNotificationRequired(false);
Address cpimFromAddress;
Address cpimToAddress;
l = cpimMessage->getMessageHeaders();
if (l) {
string imdnNamespace = "";
for (const auto &header : *l.get()) {
if (header->getName() == "NS") {
string val = header->getValue();
size_t startPos = 0;
startPos = val.find("<", startPos);
if (startPos == string::npos)
break;
size_t endPos = 0;
endPos = val.find(">", startPos);
if (endPos == string::npos)
break;
if (val.substr(startPos, endPos) == "<urn:ietf:params:imdn>")
imdnNamespace = Utils::trim(val.substr(0, startPos));
}
}
for (const auto &header : *l.get()) {
if (header->getName() == "From")
cpimFromAddress = Address(header->getValue());
else if (header->getName() == "To")
cpimToAddress = Address(header->getValue());
else if ((header->getName() == "Message-ID") || (header->getName() == (imdnNamespace + ".Message-ID")))
message->getPrivate()->setImdnMessageId(header->getValue());
else if ((header->getName() == (imdnNamespace + ".Disposition-Notification"))) {
vector<string> values = Utils::split(header->getValue(), ", ");
for (const auto &value : values)
if (value == "positive-delivery")
message->getPrivate()->setPositiveDeliveryNotificationRequired(true);
else if (value == "negative-delivery")
message->getPrivate()->setNegativeDeliveryNotificationRequired(true);
else if (value == "display")
message->getPrivate()->setDisplayNotificationRequired(true);
string imdnNamespace = "";
auto messageHeaders = cpimMessage->getMessageHeaders();
if (messageHeaders) {
for (const auto &header : *messageHeaders.get()) {
if (header->getName() != "NS")
continue;
auto nsHeader = static_pointer_cast<const Cpim::NsHeader>(header);
if (nsHeader->getUri() == "urn:ietf:params:imdn") {
imdnNamespace = nsHeader->getPrefixName();
break;
}
}
}
auto fromHeader = static_pointer_cast<const Cpim::FromHeader>(cpimMessage->getMessageHeader("From"));
Address cpimFromAddress(fromHeader->getValue());
auto toHeader = static_pointer_cast<const Cpim::ToHeader>(cpimMessage->getMessageHeader("To"));
Address cpimToAddress(toHeader->getValue());
auto dateTimeHeader = static_pointer_cast<const Cpim::DateTimeHeader>(cpimMessage->getMessageHeader("DateTime"));
if (dateTimeHeader)
message->getPrivate()->setTime(dateTimeHeader->getTime());
auto messageIdHeader = cpimMessage->getMessageHeader("Message-ID"); // TODO: For compatibility, to remove
if (!imdnNamespace.empty()) {
if (!messageIdHeader)
messageIdHeader = cpimMessage->getMessageHeader("Message-ID", imdnNamespace);
auto dispositionNotificationHeader = cpimMessage->getMessageHeader("Disposition-Notification", imdnNamespace);
if (dispositionNotificationHeader) {
vector<string> values = Utils::split(dispositionNotificationHeader->getValue(), ", ");
for (const auto &value : values)
if (value == "positive-delivery")
message->getPrivate()->setPositiveDeliveryNotificationRequired(true);
else if (value == "negative-delivery")
message->getPrivate()->setNegativeDeliveryNotificationRequired(true);
else if (value == "display")
message->getPrivate()->setDisplayNotificationRequired(true);
}
}
if (messageIdHeader)
message->getPrivate()->setImdnMessageId(messageIdHeader->getValue());
// Modify the initial message since there was no error
message->setInternalContent(newContent);
if (cpimFromAddress.isValid())
@ -203,12 +196,12 @@ ChatMessageModifier::Result CpimChatMessageModifier::decode (const shared_ptr<Ch
return ChatMessageModifier::Result::Done;
}
string CpimChatMessageModifier::cpimAddressAsString (const Address &addr) const {
ostringstream os;
if (!addr.getDisplayName().empty())
os << addr.getDisplayName() << " ";
os << "<" << addr.asStringUriOnly() << ">";
return os.str();
string CpimChatMessageModifier::cpimAddressDisplayName (const Address &addr) const {
return addr.getDisplayName();
}
string CpimChatMessageModifier::cpimAddressUri (const Address &addr) const {
return addr.asStringUriOnly();
}
LINPHONE_END_NAMESPACE

View file

@ -34,7 +34,8 @@ public:
Result decode (const std::shared_ptr<ChatMessage> &message, int &errorCode) override;
private:
std::string cpimAddressAsString (const Address &addr) const;
std::string cpimAddressDisplayName (const Address &addr) const;
std::string cpimAddressUri (const Address &addr) const;
};
LINPHONE_END_NAMESPACE

View file

@ -44,6 +44,8 @@ private:
// Misc helpers.
// ---------------------------------------------------------------------------
static time_t getTmAsTimeT (const tm &t);
std::shared_ptr<AbstractChatRoom> findChatRoom (const ChatRoomId &chatRoomId) const;
// ---------------------------------------------------------------------------

View file

@ -226,6 +226,12 @@ static string buildSqlEventFilter (
// Misc helpers.
// -----------------------------------------------------------------------------
time_t MainDbPrivate::getTmAsTimeT (const tm &t) {
tm t2 = t;
t2.tm_isdst = 0;
return Utils::getTmAsTimeT(t2);
}
shared_ptr<AbstractChatRoom> MainDbPrivate::findChatRoom (const ChatRoomId &chatRoomId) const {
L_Q();
shared_ptr<AbstractChatRoom> chatRoom = q->getCore()->findChatRoom(chatRoomId);
@ -610,7 +616,7 @@ shared_ptr<EventLog> MainDbPrivate::selectConferenceChatMessageEvent (
dChatMessage->forceFromAddress(IdentityAddress(row.get<string>(3)));
dChatMessage->forceToAddress(IdentityAddress(row.get<string>(4)));
dChatMessage->setTime(Utils::getTmAsTimeT(row.get<tm>(5)));
dChatMessage->setTime(MainDbPrivate::getTmAsTimeT(row.get<tm>(5)));
dChatMessage->setImdnMessageId(row.get<string>(6));
dChatMessage->setPositiveDeliveryNotificationRequired(bool(row.get<int>(14)));
dChatMessage->setDisplayNotificationRequired(bool(row.get<int>(15)));
@ -1976,7 +1982,7 @@ list<MainDb::ParticipantState> MainDb::getChatMessageParticipantsThatHaveDisplay
list<MainDb::ParticipantState> result;
for (const auto &row : rows)
result.emplace_back(IdentityAddress(row.get<string>(0)), ChatMessage::State::Displayed, Utils::getTmAsTimeT(row.get<tm>(1)));
result.emplace_back(IdentityAddress(row.get<string>(0)), ChatMessage::State::Displayed, MainDbPrivate::getTmAsTimeT(row.get<tm>(1)));
return result;
};
}
@ -2029,7 +2035,7 @@ list<MainDb::ParticipantState> MainDb::getChatMessageParticipantsThatHaveReceive
list<MainDb::ParticipantState> result;
for (const auto &row : rows)
result.emplace_back(IdentityAddress(row.get<string>(0)), ChatMessage::State::DeliveredToUser, Utils::getTmAsTimeT(row.get<tm>(1)));
result.emplace_back(IdentityAddress(row.get<string>(0)), ChatMessage::State::DeliveredToUser, MainDbPrivate::getTmAsTimeT(row.get<tm>(1)));
return result;
};
}
@ -2483,8 +2489,8 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms () const {
continue; // Not fetched.
AbstractChatRoomPrivate *dChatRoom = chatRoom->getPrivate();
dChatRoom->setCreationTime(Utils::getTmAsTimeT(creationTime));
dChatRoom->setLastUpdateTime(Utils::getTmAsTimeT(lastUpdateTime));
dChatRoom->setCreationTime(MainDbPrivate::getTmAsTimeT(creationTime));
dChatRoom->setLastUpdateTime(MainDbPrivate::getTmAsTimeT(lastUpdateTime));
lInfo() << "Found chat room in DB: (peer=" <<
chatRoomId.getPeerAddress().asString() << ", local=" << chatRoomId.getLocalAddress().asString() << ").";

View file

@ -27,6 +27,10 @@
#include "linphone/utils/utils.h"
#include "logger/logger.h"
#include "private.h"
// =============================================================================
using namespace std;
@ -184,12 +188,42 @@ string Utils::trim (const string &str) {
// -----------------------------------------------------------------------------
tm Utils::getTimeTAsTm (time_t time) {
tm result;
return *gmtime_r(&time, &result);
#ifdef _WIN32
return *gmtime(&time);
#else
tm result;
return *gmtime_r(&time, &result);
#endif
}
time_t Utils::getTmAsTimeT (const tm &time) {
return timegm(&const_cast<tm &>(time));
time_t result;
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
long adjust_timezone;
#else
time_t adjust_timezone;
#endif
#if TARGET_IPHONE_SIMULATOR
result = timegm(&const_cast<tm &>(time));
adjust_timezone = 0;
#else
result = mktime(&const_cast<tm &>(time));
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
_get_timezone(&adjust_timezone);
#else
adjust_timezone = timezone;
#endif
#endif
if (result == (time_t)-1) {
lError() << "mktime failed: " << strerror(errno);
return (time_t)-1;
}
return result - (time_t)adjust_timezone;
}
// -----------------------------------------------------------------------------

View file

@ -37,7 +37,7 @@ using namespace std;
using namespace LinphonePrivate;
static void parse_minimal_message () {
const string str = "Content-type: Message/CPIM\r\n"
const string str = "Subject: the weather will be fine today\r\n"
"\r\n"
"Content-Type: text/plain; charset=utf-8\r\n"
"\r\n";
@ -54,13 +54,6 @@ static void parse_minimal_message () {
static void set_generic_header_name () {
const list<pair<string, bool> > entries = {
{ "toto", true },
{ "george.abitbol", true },
{ "tata/titi", false },
{ "hey ho", false },
{ " fail", false },
{ "fail2 ", false },
// Reserved.
{ "From", false },
{ "To", false },
@ -68,59 +61,19 @@ static void set_generic_header_name () {
{ "DateTime", false },
{ "Subject", false },
{ "NS", false },
{ "Require", false },
// Case sensitivity.
{ "FROM", true },
{ "to", true },
{ "cC", true },
{ "Datetime", true },
{ "SuBject", true },
{ "nS", true },
{ "requirE", true }
{ "Require", false }
};
for (const auto &entry : entries) {
Cpim::GenericHeader genericHeader;
const bool result = genericHeader.setName(entry.first);
BC_ASSERT_EQUAL(result, entry.second, bool, "%d");
Cpim::GenericHeader genericHeader(entry.first, "");
const string name = genericHeader.getName();
if (result)
BC_ASSERT_STRING_EQUAL(name.c_str(), entry.first.c_str());
else
BC_ASSERT_STRING_EQUAL(name.c_str(), "");
}
}
static void set_generic_header_value () {
const list<pair<string, bool> > entries = {
{ "MyFeatures <mid:MessageFeatures@id.foo.com>", true },
{ "2000-12-13T13:40:00-08:00", true },
{ "2000-12-13T13:40:00-08:00", true },
{ "text/xml; charset=utf-8", true },
{ "text/xml; charset=ut\r\nf-8", false }
};
for (const auto &entry : entries) {
Cpim::GenericHeader genericHeader;
const bool result = genericHeader.setValue(entry.first);
BC_ASSERT_EQUAL(result, entry.second, bool, "%d");
const string value = genericHeader.getValue();
if (result)
BC_ASSERT_STRING_EQUAL(value.c_str(), entry.first.c_str());
else
BC_ASSERT_STRING_EQUAL(value.c_str(), "");
BC_ASSERT_STRING_EQUAL(name.c_str(), "");
}
}
static void check_core_header_names () {
const list<pair<shared_ptr<Cpim::CoreHeader>, string> > entries = {
const list<pair<shared_ptr<Cpim::Header>, string> > entries = {
{ make_shared<Cpim::FromHeader>(), "From" },
{ make_shared<Cpim::ToHeader>(), "To" },
{ make_shared<Cpim::CcHeader>(), "cc" },
@ -136,118 +89,13 @@ static void check_core_header_names () {
}
}
static void set_core_header_values () {
const list<pair<shared_ptr<Cpim::Header>, list<pair<string, bool> > > > entries = {
{ make_shared<Cpim::FromHeader>(), {
{ "Winnie the Pooh <im:pooh@100akerwood.com>", true },
{ "<im:tigger@100akerwood.com>", true },
{ "<im:tigger@100akerwood.com", false },
{ "<im:tigger>", true },
{ "toto", false }
} },
{ make_shared<Cpim::ToHeader>(), {
{ "<im:tigger@100akerwood.com", false },
{ "Winnie the Pooh <im:pooh@100akerwood.com>", true },
{ "toto", false },
{ "<im:tigger>", true },
{ "<im:tigger@100akerwood.com>", true }
} },
{ make_shared<Cpim::CcHeader>(), {
{ "<im:tigger@100akerwood.com>", true },
{ "<im:tigger@100akerwood.com", false },
{ "Winnie the Pooh <im:pooh@100akerwood.com>", true },
{ "<im:tigger>", true },
{ "toto", false }
} },
{ make_shared<Cpim::DateTimeHeader>(), {
{ "abcd", false },
{ "1985-04-12T23:20:50.52Z", true },
{ "1996-12-19T16:39:57-08:00", true },
{ "1990-12-31T23:59:60Z", true },
{ "1990-12-31T15:59:60-08:00", true },
{ "2001-02-29T10:10:10Z", false },
{ "2000-02-29T10:10:10Z", true },
{ "1937-01-01T12:00:27.87+00:20", true },
{ "1937-01-01T12:00:27.87Z", true },
{ "1956", false }
} },
{ make_shared<Cpim::SubjectHeader>(), {
{ "Eeyore's feeling very depressed today", true },
{ "🤣", true },
{ "hello", true }
} },
{ make_shared<Cpim::NsHeader>(), {
{ "MyAlias <mid:MessageFeatures@id.foo.com>", true },
{ "What is this? - Barry Burton", false },
{ "<mid:MessageFeatures@id.foo.com>", true },
{ "<mid:MessageFeatures@id.foo.com", false }
} },
{ make_shared<Cpim::RequireHeader>(), {
{ "MyAlias.VitalHeader", true },
{ "MyAlias.VitalHeader,Test", true },
{ "MyAlias.VitalHeader,🤣", false }
} }
};
for (const auto &entry : entries) {
const shared_ptr<Cpim::Header> header = entry.first;
string previousValue;
for (const auto &test : entry.second) {
const bool result = header->setValue(test.first);
BC_ASSERT_EQUAL(result, test.second, bool, "%d");
const string value = header->getValue();
if (result)
BC_ASSERT_STRING_EQUAL(value.c_str(), test.first.c_str());
else
BC_ASSERT_STRING_EQUAL(value.c_str(), previousValue.c_str());
previousValue = value;
}
}
}
static void check_subject_header_language () {
Cpim::SubjectHeader subjectHeader;
// Check for not defined language.
{
const string language = subjectHeader.getLanguage();
BC_ASSERT_STRING_EQUAL(language.c_str(), "");
}
// Set valid language.
{
const string languageToSet = "fr";
BC_ASSERT_TRUE(subjectHeader.setLanguage(languageToSet));
BC_ASSERT_TRUE(languageToSet == subjectHeader.getLanguage());
const string str = subjectHeader.asString();
const string expected = "Subject:;lang=" + languageToSet + " \r\n";
BC_ASSERT_STRING_EQUAL(str.c_str(), expected.c_str());
}
// Set invalid language.
{
const string languageToSet = "fr--";
BC_ASSERT_FALSE(subjectHeader.setLanguage(languageToSet));
BC_ASSERT_FALSE(languageToSet == subjectHeader.getLanguage());
BC_ASSERT_FALSE(subjectHeader.isValid());
}
}
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"
"To: Depressed Donkey <im:eeyore@100akerwood.com>\r\n"
const string str = "From: \"MR SANDERS\"<im:piglet@100akerwood.com>\r\n"
"To: \"Depressed Donkey\"<im:eeyore@100akerwood.com>\r\n"
"DateTime: 2000-12-13T13:40:00-08:00\r\n"
"Subject: the weather will be fine today\r\n"
"Subject:;lang=fr beau temps prevu pour aujourd'hui\r\n"
@ -268,6 +116,14 @@ static void parse_rfc_example () {
string content = message->getContent();
BC_ASSERT_STRING_EQUAL(content.c_str(), body.c_str());
Cpim::Message::HeaderList list = message->getMessageHeaders();
if (!BC_ASSERT_PTR_NOT_NULL(list)) return;
BC_ASSERT_EQUAL(list->size(), 7, int, "%d");
list = message->getMessageHeaders("MyFeatures");
if (!BC_ASSERT_PTR_NOT_NULL(list)) return;
BC_ASSERT_EQUAL(list->size(), 2, int, "%d");
}
static void parse_message_with_generic_header_parameters () {
@ -275,9 +131,7 @@ static void parse_message_with_generic_header_parameters () {
"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"
const string str = "From: \"MR SANDERS\"<im:piglet@100akerwood.com>\r\n"
"Test:;aaa=bbb;yes=no CheckMe\r\n"
"yaya: coucou\r\n"
"yepee:;good=bad ugly\r\n"
@ -298,81 +152,55 @@ static void parse_message_with_generic_header_parameters () {
static void build_message () {
Cpim::Message message;
if (!BC_ASSERT_FALSE(message.isValid()))
return;
// Set CPIM headers.
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(cpimContentTypeHeader))) return;
// Set message headers.
Cpim::FromHeader fromHeader;
if (!BC_ASSERT_TRUE(fromHeader.setValue("MR SANDERS <im:piglet@100akerwood.com>"))) return;
Cpim::FromHeader fromHeader("im:piglet@100akerwood.com", "MR SANDERS");
Cpim::ToHeader toHeader;
if (!BC_ASSERT_TRUE(toHeader.setValue("Depressed Donkey <im:eeyore@100akerwood.com>"))) return;
Cpim::ToHeader toHeader("im:eeyore@100akerwood.com", "Depressed Donkey");
Cpim::DateTimeHeader dateTimeHeader;
if (!BC_ASSERT_TRUE(dateTimeHeader.setValue("2000-12-13T13:40:00-08:00"))) return;
// 976686000 is 2000-12-13T13:40:00-08:00
Cpim::DateTimeHeader dateTimeHeader(976686000);
BC_ASSERT_EQUAL(dateTimeHeader.getTime(), 976686000, int, "%d");
Cpim::SubjectHeader subjectHeader;
if (!BC_ASSERT_TRUE(subjectHeader.setValue("the weather will be fine today"))) return;
Cpim::SubjectHeader subjectHeader("the weather will be fine today");
Cpim::SubjectHeader subjectWithLanguageHeader;
if (!BC_ASSERT_TRUE(subjectWithLanguageHeader.setValue("beau temps prevu pour aujourd'hui"))) return;
if (!BC_ASSERT_TRUE(subjectWithLanguageHeader.setLanguage("fr"))) return;
Cpim::SubjectHeader subjectWithLanguageHeader("beau temps prevu pour aujourd'hui", "fr");
Cpim::NsHeader nsHeader;
if (!BC_ASSERT_TRUE(nsHeader.setValue("MyFeatures <mid:MessageFeatures@id.foo.com>"))) return;
Cpim::NsHeader nsHeader("mid:MessageFeatures@id.foo.com", "MyFeatures");
Cpim::RequireHeader requireHeader;
if (!BC_ASSERT_TRUE(requireHeader.setValue("MyFeatures.VitalMessageOption"))) return;
Cpim::RequireHeader requireHeader("MyFeatures.VitalMessageOption");
Cpim::GenericHeader vitalMessageHeader;
if (!BC_ASSERT_TRUE(vitalMessageHeader.setName("MyFeatures.VitalMessageOption"))) return;
if (!BC_ASSERT_TRUE(vitalMessageHeader.setValue("Confirmation-requested"))) return;
Cpim::GenericHeader vitalMessageHeader("MyFeatures.VitalMessageOption", "Confirmation-requested");
Cpim::GenericHeader wackyMessageHeader;
if (!BC_ASSERT_TRUE(wackyMessageHeader.setName("MyFeatures.WackyMessageOption"))) return;
if (!BC_ASSERT_TRUE(wackyMessageHeader.setValue("Use-silly-font"))) return;
Cpim::GenericHeader wackyMessageHeader("MyFeatures.WackyMessageOption", "Use-silly-font");
if (!BC_ASSERT_TRUE(message.addMessageHeader(fromHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(toHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(dateTimeHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(subjectHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(subjectWithLanguageHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(nsHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(requireHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(vitalMessageHeader))) return;
if (!BC_ASSERT_TRUE(message.addMessageHeader(wackyMessageHeader))) return;
message.addMessageHeader(fromHeader);
message.addMessageHeader(toHeader);
message.addMessageHeader(dateTimeHeader);
message.addMessageHeader(subjectHeader);
message.addMessageHeader(subjectWithLanguageHeader);
message.addMessageHeader(nsHeader);
message.addMessageHeader(requireHeader);
message.addMessageHeader(vitalMessageHeader);
message.addMessageHeader(wackyMessageHeader);
// 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 contentTypeHeader("Content-Type", "text/xml; charset=utf-8");
message.addContentHeader(contentTypeHeader);
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;
Cpim::GenericHeader contentIdHeader("Content-ID", "<1234567890@foo.com>");
message.addContentHeader(contentIdHeader);
const string content = "<body>"
"Here is the text of my message."
"</body>";
if (!BC_ASSERT_TRUE(message.setContent(content))) return;
if (!BC_ASSERT_TRUE(message.isValid())) return;
const string strMessage = message.asString();
const string expectedMessage = "Content-Type: Message/CPIM\r\n"
"\r\n"
"From: MR SANDERS <im:piglet@100akerwood.com>\r\n"
"To: Depressed Donkey <im:eeyore@100akerwood.com>\r\n"
"DateTime: 2000-12-13T13:40:00-08:00\r\n"
const string expectedMessage = "From: \"MR SANDERS\"<im:piglet@100akerwood.com>\r\n"
"To: \"Depressed Donkey\"<im:eeyore@100akerwood.com>\r\n"
"DateTime: 2000-12-13T05:40:00Z\r\n"
"Subject: the weather will be fine today\r\n"
"Subject:;lang=fr beau temps prevu pour aujourd'hui\r\n"
"NS: MyFeatures <mid:MessageFeatures@id.foo.com>\r\n"
@ -453,6 +281,9 @@ static void cpim_chat_message_modifier_base (bool useMultipart) {
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(pauline->stat.last_received_chat_message), expected.c_str());
}
marieMessage.reset();
marieRoom.reset();
linphone_im_encryption_engine_unref(marie_imee);
linphone_im_encryption_engine_unref(pauline_imee);
@ -471,10 +302,7 @@ static void cpim_chat_message_modifier_with_multipart_body () {
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),
TEST_NO_TAG("Set generic header value", set_generic_header_value),
TEST_NO_TAG("Check core header names", check_core_header_names),
TEST_NO_TAG("Set core header values", set_core_header_values),
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),