diff --git a/share/cpim_grammar b/share/cpim_grammar index 7ea914ca5..066afb558 100644 Binary files a/share/cpim_grammar and b/share/cpim_grammar differ diff --git a/src/chat/cpim/header/cpim-core-headers.cpp b/src/chat/cpim/header/cpim-core-headers.cpp index 1a85d7e81..4f35651fe 100644 --- a/src/chat/cpim/header/cpim-core-headers.cpp +++ b/src/chat/cpim/header/cpim-core-headers.cpp @@ -17,6 +17,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include +#include + +#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(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 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 &headerNames) : RequireHeader() { + L_D(); + d->headerNames = headerNames; +} + +list 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(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 diff --git a/src/chat/cpim/header/cpim-core-headers.h b/src/chat/cpim/header/cpim-core-headers.h index 1c7321ce4..2ddd7ea29 100644 --- a/src/chat/cpim/header/cpim-core-headers.h +++ b/src/chat/cpim/header/cpim-core-headers.h @@ -20,60 +20,160 @@ #ifndef _L_CPIM_CORE_HEADERS_H_ #define _L_CPIM_CORE_HEADERS_H_ +#include +#include + #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 &headerNames); + + inline std::string getName () const override { + return "Require"; + } + + std::list 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 diff --git a/src/chat/cpim/header/cpim-generic-header.cpp b/src/chat/cpim/header/cpim-generic-header.cpp index 67a7b81d4..6c5947777 100644 --- a/src/chat/cpim/header/cpim-generic-header.cpp +++ b/src/chat/cpim/header/cpim-generic-header.cpp @@ -34,38 +34,50 @@ LINPHONE_BEGIN_NAMESPACE class Cpim::GenericHeaderPrivate : public HeaderPrivate { public: - GenericHeaderPrivate () : parameters(make_shared > >()) {} + GenericHeaderPrivate () : parameters(make_shared>>()) {} string name; - shared_ptr > > parameters; + string value; + shared_ptr>> parameters; }; Cpim::GenericHeader::GenericHeader () : Header(*new GenericHeaderPrivate) {} +Cpim::GenericHeader::GenericHeader (string name, string value, string parameters) : GenericHeader() { + setName(name); + setValue(value); + + for (const auto ¶meter : 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 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 ¶meters) { - L_D(); - - // Set name/value. - d->name = name; - Header::setValue(value); - - // Parse and build parameters list. - for (const auto ¶meter : 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 diff --git a/src/chat/cpim/header/cpim-generic-header.h b/src/chat/cpim/header/cpim-generic-header.h index 2433a9cb7..6ee8a3695 100644 --- a/src/chat/cpim/header/cpim-generic-header.h +++ b/src/chat/cpim/header/cpim-generic-header.h @@ -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 > > ParameterList; + typedef std::shared_ptr>> 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 ¶meters); - private: L_DECLARE_PRIVATE(GenericHeader); L_DISABLE_COPY(GenericHeader); diff --git a/src/chat/cpim/header/cpim-header-p.h b/src/chat/cpim/header/cpim-header-p.h index 048e91309..2aaea0acb 100644 --- a/src/chat/cpim/header/cpim-header-p.h +++ b/src/chat/cpim/header/cpim-header-p.h @@ -30,8 +30,6 @@ LINPHONE_BEGIN_NAMESPACE namespace Cpim { class HeaderPrivate : public ObjectPrivate { private: - std::string value; - L_DECLARE_PUBLIC(Header); }; } diff --git a/src/chat/cpim/header/cpim-header.cpp b/src/chat/cpim/header/cpim-header.cpp index 8141d7811..6a3b83470 100644 --- a/src/chat/cpim/header/cpim-header.cpp +++ b/src/chat/cpim/header/cpim-header.cpp @@ -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 diff --git a/src/chat/cpim/header/cpim-header.h b/src/chat/cpim/header/cpim-header.h index 13dcfdce8..f71d115b0 100644 --- a/src/chat/cpim/header/cpim-header.h +++ b/src/chat/cpim/header/cpim-header.h @@ -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); diff --git a/src/chat/cpim/message/cpim-message.cpp b/src/chat/cpim/message/cpim-message.cpp index 123ef6c66..ca4aa3b4d 100644 --- a/src/chat/cpim/message/cpim-message.cpp +++ b/src/chat/cpim/message/cpim-message.cpp @@ -18,6 +18,7 @@ */ #include +#include #include "linphone/utils/utils.h" @@ -36,10 +37,10 @@ LINPHONE_BEGIN_NAMESPACE class Cpim::MessagePrivate : public ObjectPrivate { public: - typedef list > PrivHeaderList; + using PrivHeaderList = list>; + using PrivHeaderMap = map>; - shared_ptr cpimHeaders = make_shared(); // TODO: Remove this useless variable - shared_ptr messageHeaders = make_shared(); + PrivHeaderMap messageHeaders; shared_ptr contentHeaders = make_shared(); 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(); - 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 &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 &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 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 &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 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 &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"; } diff --git a/src/chat/cpim/message/cpim-message.h b/src/chat/cpim/message/cpim-message.h index c7778c929..19e91a43a 100644 --- a/src/chat/cpim/message/cpim-message.h +++ b/src/chat/cpim/message/cpim-message.h @@ -34,27 +34,21 @@ namespace Cpim { public: Message (); - typedef std::shared_ptr > > HeaderList; + typedef std::shared_ptr>> 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 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 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 createFromString (const std::string &str); diff --git a/src/chat/cpim/parser/cpim-parser.cpp b/src/chat/cpim/parser/cpim-parser.cpp index 3d34fcd71..23fa0a360 100644 --- a/src/chat/cpim/parser/cpim-parser.cpp +++ b/src/chat/cpim/parser/cpim-parser.cpp @@ -17,7 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include +#include #include #include @@ -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(&header); if (genericHeader) { for (const auto ¶meter : *genericHeader->getParameters()) mParameters += ";" + parameter.first + "=" + parameter.second; - return; - } - - // Subject header. - const SubjectHeader *subjectHeader = dynamic_cast(&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 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
createHeader (bool force) const; + virtual shared_ptr
createHeader () const; + + virtual bool isValid () const; private: - template - shared_ptr
createCoreHeader (bool force) const { - shared_ptr header = make_shared(); - 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
HeaderNode::createCoreHeader(bool force) const { - shared_ptr header = make_shared(); - 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
HeaderNode::createHeader (bool force = false) const { - static const unordered_map(HeaderNode::*)(bool)const> reservedHandlers = { - { "From", &HeaderNode::createCoreHeader }, - { "To", &HeaderNode::createCoreHeader }, - { "cc", &HeaderNode::createCoreHeader }, - { "DateTime", &HeaderNode::createCoreHeader }, - { "Subject", &HeaderNode::createCoreHeader }, - { "NS", &HeaderNode::createCoreHeader }, - { "Require", &HeaderNode::createCoreHeader } - }; + shared_ptr
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 = make_shared(); - genericHeader->force(mName, mValue, mParameters); + genericHeader->setName(mName); + + for (const auto ¶meter : 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(&header); + if (fromHeader) { + setFormalName(fromHeader->getFormalName()); + setUri(fromHeader->getUri()); + } + } + + shared_ptr
createHeader () const override; + }; + + shared_ptr
FromHeaderNode::createHeader () const { + if (!isValid()) + return nullptr; + + return make_shared(getUri(), getFormalName()); + } + + // ------------------------------------------------------------------------- + + class ToHeaderNode : public ContactHeaderNode { + public: + ToHeaderNode () = default; + + explicit ToHeaderNode (const Header &header) { + const ToHeader *toHeader = dynamic_cast(&header); + if (toHeader) { + setFormalName(toHeader->getFormalName()); + setUri(toHeader->getUri()); + } + } + + shared_ptr
createHeader () const override; + }; + + shared_ptr
ToHeaderNode::createHeader () const { + if (!isValid()) + return nullptr; + + return make_shared(getUri(), getFormalName()); + } + + // ------------------------------------------------------------------------- + + class CcHeaderNode : public ContactHeaderNode { + public: + CcHeaderNode () = default; + + explicit CcHeaderNode (const Header &header) { + const CcHeader *ccHeader = dynamic_cast(&header); + if (ccHeader) { + setFormalName(ccHeader->getFormalName()); + setUri(ccHeader->getUri()); + } + } + + shared_ptr
createHeader () const override; + }; + + shared_ptr
CcHeaderNode::createHeader () const { + if (!isValid()) + return nullptr; + + return make_shared(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(&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 &offset) { + mTimeOffset.tm_hour = offset->mHour; + mTimeOffset.tm_min = offset->mMinute; + mSignOffset = offset->mSign; + } + + bool isValid () const override; + + shared_ptr
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
DateTimeHeaderNode::createHeader () const { + if (!isValid()) + return nullptr; + + return make_shared(getTime(), getTimeOffset(), getSignOffset()); + } + + // ------------------------------------------------------------------------- + + class SubjectHeaderNode : public HeaderNode { + public: + SubjectHeaderNode () = default; + + explicit SubjectHeaderNode (const Header &header) { + const SubjectHeader *subjectHeader = dynamic_cast(&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
createHeader () const override; + + private: + string mLanguage; + string mSubject; + }; + + bool SubjectHeaderNode::isValid () const { + return !mSubject.empty(); + } + + shared_ptr
SubjectHeaderNode::createHeader () const { + if (!isValid()) + return nullptr; + + return make_shared(getSubject(), getLanguage()); + } + + // ------------------------------------------------------------------------- + + class NsHeaderNode : public HeaderNode { + public: + NsHeaderNode () = default; + + explicit NsHeaderNode (const Header &header) { + const NsHeader *nsHeader = dynamic_cast(&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
createHeader () const override; + + private: + string mPrefixName; + string mUri; + }; + + bool NsHeaderNode::isValid () const { + return !mUri.empty(); + } + + shared_ptr
NsHeaderNode::createHeader () const { + if (!isValid()) + return nullptr; + + return make_shared(getUri(), getPrefixName()); + } + + // ------------------------------------------------------------------------- + + class RequireHeaderNode : public HeaderNode { + public: + RequireHeaderNode () = default; + + explicit RequireHeaderNode (const Header &header) { + const RequireHeader *requireHeader = dynamic_cast(&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
createHeader () const override; + + private: + string mHeaderNames; + }; + + bool RequireHeaderNode::isValid () const { + return !mHeaderNames.empty(); + } + + shared_ptr
RequireHeaderNode::createHeader () const { + if (!isValid()) + return nullptr; + + return make_shared(mHeaderNames); + } + + // ------------------------------------------------------------------------- + class ListHeaderNode : public Node, public list > {}; @@ -163,67 +516,61 @@ namespace Cpim { class MessageNode : public Node { public: - void addHeaders (const shared_ptr &headers) { - mHeaders->push_back(headers); + void addMessageHeaders (const shared_ptr &headers) { + for (const auto &headerNode : *headers) { + mMessageHeaders.push_back(headerNode); + } + } + + void addContentHeaders (const shared_ptr &headers) { + for (const auto &headerNode : *headers) { + mContentHeaders.push_back(headerNode); + } } // Warning: Call this function one time! shared_ptr 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 = make_shared(); - // TODO: To remove - if (size == 3) { - const shared_ptr cpimHeaders = mHeaders->front(); - if (find_if(cpimHeaders->cbegin(), cpimHeaders->cend(), - [](const shared_ptr &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 header = headerNode->createHeader(); + if (!header) return nullptr; - } - // Add MIME headers. - for (const auto &headerNode : *cpimHeaders) { - const shared_ptr header = headerNode->createHeader(); - if (!header || !message->addCpimHeader(*header)) - return nullptr; - } - - // Add message headers. - for (const auto &headerNode : **(++mHeaders->cbegin())) { - const shared_ptr 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 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 header = headerNode->createHeader(); - if (!header || !message->addContentHeader(*header)) + if (!header) return nullptr; + + message->addContentHeader(*header); } return message; } private: - shared_ptr > > mHeaders = make_shared > >(); + list> mContentHeaders; + list> mMessageHeaders; }; } @@ -260,27 +607,64 @@ shared_ptr Cpim::Parser::parseMessage (const string &input) { typedef void (list >::*pushPtr)(const shared_ptr &value); belr::Parser > parser(d->grammar); - parser.setHandler( - "Message", belr::make_fn(make_shared ) - )->setCollector( - "Headers", belr::make_sfn(&MessageNode::addHeaders) - ); + parser.setHandler("Message", belr::make_fn(make_shared)) + ->setCollector("Message-headers", belr::make_sfn(&MessageNode::addMessageHeaders)) + ->setCollector("Content-headers", belr::make_sfn(&MessageNode::addContentHeaders)); - parser.setHandler( - "Headers", belr::make_fn(make_shared ) - )->setCollector( - "Header", belr::make_sfn(static_cast(&ListHeaderNode::push_back)) - ); + parser.setHandler("Message-headers", belr::make_fn(make_shared)) + ->setCollector("Header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))) + ->setCollector("From-header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))) + ->setCollector("To-header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))) + ->setCollector("DateTime-header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))) + ->setCollector("cc-header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))) + ->setCollector("Subject-header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))) + ->setCollector("NS-header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))) + ->setCollector("Require-header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))); - parser.setHandler( - "Header", belr::make_fn(make_shared ) - )->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)) + ->setCollector("Header", belr::make_sfn(static_cast(&ListHeaderNode::push_back))); + + parser.setHandler("Header", belr::make_fn(make_shared)) + ->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)) + ->setCollector("Formal-name", belr::make_sfn(&FromHeaderNode::setFormalName)) + ->setCollector("URI", belr::make_sfn(&FromHeaderNode::setUri)); + + parser.setHandler("To-header", belr::make_fn(make_shared)) + ->setCollector("Formal-name", belr::make_sfn(&ToHeaderNode::setFormalName)) + ->setCollector("URI", belr::make_sfn(&ToHeaderNode::setUri)); + + parser.setHandler("cc-header", belr::make_fn(make_shared)) + ->setCollector("Formal-name", belr::make_sfn(&CcHeaderNode::setFormalName)) + ->setCollector("URI", belr::make_sfn(&CcHeaderNode::setUri)); + + parser.setHandler("DateTime-header", belr::make_fn(make_shared)) + ->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)) + ->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)) + ->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)) + ->setCollector("Name-prefix", belr::make_sfn(&NsHeaderNode::setPrefixName)) + ->setCollector("URI", belr::make_sfn(&NsHeaderNode::setUri)); + + parser.setHandler("Require-header", belr::make_fn(make_shared)) + ->setCollector("Require-header-value", belr::make_sfn(&RequireHeaderNode::setHeaderNames)); size_t parsedSize; shared_ptr node = parser.parseInput("Message", input, &parsedSize); @@ -305,143 +689,28 @@ shared_ptr Cpim::Parser::parseMessage (const string &input) { // ----------------------------------------------------------------------------- shared_ptr 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 &grammar, const string &input) { - belr::Parser > parser(grammar); - parser.setHandler( - "Header", belr::make_fn(make_shared ) - ); + if (header.getName() == "DateTime") + return DateTimeHeaderNode(header).createHeader(); - size_t parsedSize; - shared_ptr 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 &grammar, - const string &headerName, - const string &headerValue, - const string &headerParams = string() -) { - const string mainRule = headerName + "-header"; - - belr::Parser > parser(grammar); - parser.setHandler( - mainRule, belr::make_fn(make_shared ) - ); - - const string input = headerName + ":" + headerParams + " " + headerValue; - - size_t parsedSize; - shared_ptr node = parser.parseInput(mainRule, input, &parsedSize); - return node && parsedSize == input.length(); -} - -template<> -bool Cpim::Parser::coreHeaderIsValid (const string &headerValue) const { - L_D(); - return LinphonePrivate::coreHeaderIsValid(d->grammar, "From", headerValue); -} - -template<> -bool Cpim::Parser::coreHeaderIsValid (const string &headerValue) const { - L_D(); - return LinphonePrivate::coreHeaderIsValid(d->grammar, "To", headerValue); -} - -template<> -bool Cpim::Parser::coreHeaderIsValid (const string &headerValue) const { - L_D(); - return LinphonePrivate::coreHeaderIsValid(d->grammar, "cc", headerValue); -} - -template<> -bool Cpim::Parser::coreHeaderIsValid (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 (const string &headerValue) const { - L_D(); - return LinphonePrivate::coreHeaderIsValid(d->grammar, "Subject", headerValue); -} - -template<> -bool Cpim::Parser::coreHeaderIsValid (const string &headerValue) const { - L_D(); - return LinphonePrivate::coreHeaderIsValid(d->grammar, "NS", headerValue); -} - -template<> -bool Cpim::Parser::coreHeaderIsValid (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 diff --git a/src/chat/cpim/parser/cpim-parser.h b/src/chat/cpim/parser/cpim-parser.h index bdb32f7e4..076400841 100644 --- a/src/chat/cpim/parser/cpim-parser.h +++ b/src/chat/cpim/parser/cpim-parser.h @@ -38,46 +38,12 @@ namespace Cpim { std::shared_ptr
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 - 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(const std::string &headerValue) const; - - template<> - bool Parser::coreHeaderIsValid(const std::string &headerValue) const; - - template<> - bool Parser::coreHeaderIsValid(const std::string &headerValue) const; - - template<> - bool Parser::coreHeaderIsValid(const std::string &headerValue) const; - - template<> - bool Parser::coreHeaderIsValid(const std::string &headerValue) const; - - template<> - bool Parser::coreHeaderIsValid(const std::string &headerValue) const; - - template<> - bool Parser::coreHeaderIsValid(const std::string &headerValue) const; } LINPHONE_END_NAMESPACE diff --git a/src/chat/cpim/parser/cpim-rules b/src/chat/cpim/parser/cpim-rules index bba8e019c..b11cccb9a 100644 --- a/src/chat/cpim/parser/cpim-rules +++ b/src/chat/cpim/parser/cpim-rules @@ -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 ] diff --git a/src/chat/modifier/cpim-chat-message-modifier.cpp b/src/chat/modifier/cpim-chat-message-modifier.cpp index 9c7d48f6b..39104a674 100644 --- a/src/chat/modifier/cpim-chat-message-modifier.cpp +++ b/src/chat/modifier/cpim-chat-message-modifier.cpp @@ -38,32 +38,30 @@ LINPHONE_BEGIN_NAMESPACE ChatMessageModifier::Result CpimChatMessageModifier::encode (const shared_ptr &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 + " "); - 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 dispositionNotificationValues; if (message->getPrivate()->getPositiveDeliveryNotificationRequired()) dispositionNotificationValues.emplace_back("positive-delivery"); @@ -71,8 +69,12 @@ ChatMessageModifier::Result CpimChatMessageModifier::encode (const shared_ptrgetPrivate()->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_ptrgetBodyAsString(); 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_ptrgetBodyAsString(); const shared_ptr 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) == "") - 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 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(header); + if (nsHeader->getUri() == "urn:ietf:params:imdn") { + imdnNamespace = nsHeader->getPrefixName(); + break; } } } + auto fromHeader = static_pointer_cast(cpimMessage->getMessageHeader("From")); + Address cpimFromAddress(fromHeader->getValue()); + auto toHeader = static_pointer_cast(cpimMessage->getMessageHeader("To")); + Address cpimToAddress(toHeader->getValue()); + auto dateTimeHeader = static_pointer_cast(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 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"; - 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 diff --git a/src/chat/modifier/cpim-chat-message-modifier.h b/src/chat/modifier/cpim-chat-message-modifier.h index ab63c74e6..04dab858a 100644 --- a/src/chat/modifier/cpim-chat-message-modifier.h +++ b/src/chat/modifier/cpim-chat-message-modifier.h @@ -34,7 +34,8 @@ public: Result decode (const std::shared_ptr &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 diff --git a/src/db/main-db-p.h b/src/db/main-db-p.h index bd2076407..35c93f42d 100644 --- a/src/db/main-db-p.h +++ b/src/db/main-db-p.h @@ -44,6 +44,8 @@ private: // Misc helpers. // --------------------------------------------------------------------------- + static time_t getTmAsTimeT (const tm &t); + std::shared_ptr findChatRoom (const ChatRoomId &chatRoomId) const; // --------------------------------------------------------------------------- diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index dcb589a60..bd5245f67 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -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 MainDbPrivate::findChatRoom (const ChatRoomId &chatRoomId) const { L_Q(); shared_ptr chatRoom = q->getCore()->findChatRoom(chatRoomId); @@ -610,7 +616,7 @@ shared_ptr MainDbPrivate::selectConferenceChatMessageEvent ( dChatMessage->forceFromAddress(IdentityAddress(row.get(3))); dChatMessage->forceToAddress(IdentityAddress(row.get(4))); - dChatMessage->setTime(Utils::getTmAsTimeT(row.get(5))); + dChatMessage->setTime(MainDbPrivate::getTmAsTimeT(row.get(5))); dChatMessage->setImdnMessageId(row.get(6)); dChatMessage->setPositiveDeliveryNotificationRequired(bool(row.get(14))); dChatMessage->setDisplayNotificationRequired(bool(row.get(15))); @@ -1976,7 +1982,7 @@ list MainDb::getChatMessageParticipantsThatHaveDisplay list result; for (const auto &row : rows) - result.emplace_back(IdentityAddress(row.get(0)), ChatMessage::State::Displayed, Utils::getTmAsTimeT(row.get(1))); + result.emplace_back(IdentityAddress(row.get(0)), ChatMessage::State::Displayed, MainDbPrivate::getTmAsTimeT(row.get(1))); return result; }; } @@ -2029,7 +2035,7 @@ list MainDb::getChatMessageParticipantsThatHaveReceive list result; for (const auto &row : rows) - result.emplace_back(IdentityAddress(row.get(0)), ChatMessage::State::DeliveredToUser, Utils::getTmAsTimeT(row.get(1))); + result.emplace_back(IdentityAddress(row.get(0)), ChatMessage::State::DeliveredToUser, MainDbPrivate::getTmAsTimeT(row.get(1))); return result; }; } @@ -2483,8 +2489,8 @@ list> 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() << ")."; diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 897f5dc8f..b05174a63 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -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(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(time)); + adjust_timezone = 0; + #else + result = mktime(&const_cast(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; } // ----------------------------------------------------------------------------- diff --git a/tester/cpim-tester.cpp b/tester/cpim-tester.cpp index 83a45430e..ec1f8338a 100644 --- a/tester/cpim-tester.cpp +++ b/tester/cpim-tester.cpp @@ -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 > 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 > entries = { - { "MyFeatures ", 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, string> > entries = { + const list, string> > entries = { { make_shared(), "From" }, { make_shared(), "To" }, { make_shared(), "cc" }, @@ -136,118 +89,13 @@ static void check_core_header_names () { } } -static void set_core_header_values () { - const list, list > > > entries = { - { make_shared(), { - { "Winnie the Pooh ", true }, - { "", true }, - { "", true }, - { "toto", false } - } }, - { make_shared(), { - { "", true }, - { "toto", false }, - { "", true }, - { "", true } - } }, - { make_shared(), { - { "", true }, - { "", true }, - { "", true }, - { "toto", false } - } }, - { make_shared(), { - { "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(), { - { "Eeyore's feeling very depressed today", true }, - { "🤣", true }, - { "hello", true } - } }, - { make_shared(), { - { "MyAlias ", true }, - { "What is this? - Barry Burton", false }, - { "", true }, - { "(), { - { "MyAlias.VitalHeader", true }, - { "MyAlias.VitalHeader,Test", true }, - { "MyAlias.VitalHeader,🤣", false } - } } - }; - - for (const auto &entry : entries) { - const shared_ptr 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 = "" "Here is the text of my message." ""; - const string str = "Content-type: Message/CPIM\r\n" - "\r\n" - "From: MR SANDERS \r\n" - "To: Depressed Donkey \r\n" + const string str = "From: \"MR SANDERS\"\r\n" + "To: \"Depressed Donkey\"\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." ""; - const string str = "Content-type: Message/CPIM\r\n" - "\r\n" - "From: MR SANDERS \r\n" + const string str = "From: \"MR SANDERS\"\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 "))) return; + Cpim::FromHeader fromHeader("im:piglet@100akerwood.com", "MR SANDERS"); - Cpim::ToHeader toHeader; - if (!BC_ASSERT_TRUE(toHeader.setValue("Depressed Donkey "))) 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 "))) 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 = "" "Here is the text of my message." ""; 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 \r\n" - "To: Depressed Donkey \r\n" - "DateTime: 2000-12-13T13:40:00-08:00\r\n" + const string expectedMessage = "From: \"MR SANDERS\"\r\n" + "To: \"Depressed Donkey\"\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 \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),