/* * cpim-parser.cpp * Copyright (C) 2010-2018 Belledonne Communications SARL * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "linphone/utils/utils.h" #include "content/content-type.h" #include "logger/logger.h" #include "object/object-p.h" #include "cpim-parser.h" #define CPIM_GRAMMAR "cpim_grammar" // ============================================================================= using namespace std; LINPHONE_BEGIN_NAMESPACE namespace Cpim { class Node { public: virtual ~Node () = default; }; class HeaderNode : public Node { public: HeaderNode () = default; explicit HeaderNode (const Header &header) : mName(header.getName()), mValue(header.getValue()) { const GenericHeader *genericHeader = dynamic_cast(&header); if (genericHeader) { for (const auto ¶meter : *genericHeader->getParameters()) mParameters += ";" + parameter.first + "=" + parameter.second; } } string getName () const { return mName; } void setName (const string &name) { static const set reserved = { "From", "To", "cc", "DateTime", "Subject", "NS", "Require" }; if (reserved.find(name) == reserved.end()) mName = name; } string getParameters () const { return mParameters; } void setParameters (const string ¶meters) { mParameters = parameters; } string getValue () const { return mValue; } void setValue (const string &value) { mValue = value; } virtual shared_ptr
createHeader () const; virtual bool isValid () const; private: string mName; string mValue; string mParameters; }; bool HeaderNode::isValid () const { return !mName.empty() && !mValue.empty(); } shared_ptr
HeaderNode::createHeader () const { if (!isValid()) return nullptr; shared_ptr genericHeader = make_shared(); 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 > {}; // ------------------------------------------------------------------------- class MessageNode : public Node { public: 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 { 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(); // 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; message->addMessageHeader(*header, ns); } // Add content headers. for (const auto &headerNode : mContentHeaders) { const shared_ptr header = headerNode->createHeader(); if (!header) return nullptr; message->addContentHeader(*header); } return message; } private: list> mContentHeaders; list> mMessageHeaders; }; } // ----------------------------------------------------------------------------- class Cpim::ParserPrivate : public ObjectPrivate { public: shared_ptr grammar; }; Cpim::Parser::Parser () : Singleton(*new ParserPrivate) { L_D(); d->grammar = belr::GrammarLoader::get().load(CPIM_GRAMMAR); if (!d->grammar) lFatal() << "Unable to load CPIM grammar."; } // ----------------------------------------------------------------------------- shared_ptr Cpim::Parser::parseMessage (const string &input) { L_D(); typedef void (list >::*pushPtr)(const shared_ptr &value); belr::Parser > parser(d->grammar); 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("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("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); if (!node) { lWarning() << "Unable to parse message."; return nullptr; } shared_ptr messageNode = dynamic_pointer_cast(node); if (!messageNode) { lWarning() << "Unable to cast belr result to message node."; return nullptr; } shared_ptr message = messageNode->createMessage(); if (message) { message->setContent(input.substr(parsedSize)); } return message; } // ----------------------------------------------------------------------------- shared_ptr Cpim::Parser::cloneHeader (const Header &header) { if (header.getName() == "From") return FromHeaderNode(header).createHeader(); if (header.getName() == "To") return ToHeaderNode(header).createHeader(); if (header.getName() == "cc") return CcHeaderNode(header).createHeader(); if (header.getName() == "DateTime") return DateTimeHeaderNode(header).createHeader(); if (header.getName() == "Subject") return SubjectHeaderNode(header).createHeader(); if (header.getName() == "NS") return NsHeaderNode(header).createHeader(); if (header.getName() == "Require") return RequireHeaderNode(header).createHeader(); return HeaderNode(header).createHeader(); } LINPHONE_END_NAMESPACE