// // MessageHeader.cpp // // Library: Net // Package: Messages // Module: MessageHeader // // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #include "Poco/Net/MessageHeader.h" #include "Poco/Net/NetException.h" #include "Poco/String.h" #include "Poco/Ascii.h" #include "Poco/TextConverter.h" #include "Poco/StringTokenizer.h" #include "Poco/Base64Decoder.h" #include "Poco/UTF8Encoding.h" #include namespace Poco { namespace Net { MessageHeader::MessageHeader(): _fieldLimit(DFL_FIELD_LIMIT) { } MessageHeader::MessageHeader(const MessageHeader& messageHeader): NameValueCollection(messageHeader), _fieldLimit(DFL_FIELD_LIMIT) { } MessageHeader::~MessageHeader() { } MessageHeader& MessageHeader::operator = (const MessageHeader& messageHeader) { NameValueCollection::operator = (messageHeader); return *this; } void MessageHeader::write(std::ostream& ostr) const { NameValueCollection::ConstIterator it = begin(); while (it != end()) { ostr << it->first << ": " << it->second << "\r\n"; ++it; } } void MessageHeader::read(std::istream& istr) { static const int eof = std::char_traits::eof(); std::streambuf& buf = *istr.rdbuf(); std::string name; std::string value; name.reserve(32); value.reserve(64); int ch = buf.sbumpc(); int fields = 0; while (ch != eof && ch != '\r' && ch != '\n') { if (_fieldLimit > 0 && fields == _fieldLimit) throw MessageException("Too many header fields"); name.clear(); value.clear(); while (ch != eof && ch != ':' && ch != '\n' && name.length() < MAX_NAME_LENGTH) { name += ch; ch = buf.sbumpc(); } if (ch == '\n') { ch = buf.sbumpc(); continue; } // ignore invalid header lines if (ch != ':') throw MessageException("Field name too long/no colon found"); if (ch != eof) ch = buf.sbumpc(); // ':' while (ch != eof && Poco::Ascii::isSpace(ch) && ch != '\r' && ch != '\n') ch = buf.sbumpc(); while (ch != eof && ch != '\r' && ch != '\n' && value.length() < MAX_VALUE_LENGTH) { value += ch; ch = buf.sbumpc(); } if (ch == '\r') ch = buf.sbumpc(); if (ch == '\n') ch = buf.sbumpc(); else if (ch != eof) throw MessageException("Field value too long/no CRLF found"); while (ch == ' ' || ch == '\t') // folding { while (ch != eof && ch != '\r' && ch != '\n' && value.length() < MAX_VALUE_LENGTH) { value += ch; ch = buf.sbumpc(); } if (ch == '\r') ch = buf.sbumpc(); if (ch == '\n') ch = buf.sbumpc(); else if (ch != eof) throw MessageException("Folded field value too long/no CRLF found"); } Poco::trimRightInPlace(value); add(name, decodeWord(value)); ++fields; } istr.putback(ch); } int MessageHeader::getFieldLimit() const { return _fieldLimit; } void MessageHeader::setFieldLimit(int limit) { poco_assert (limit >= 0); _fieldLimit = limit; } bool MessageHeader::hasToken(const std::string& fieldName, const std::string& token) const { std::string field = get(fieldName, ""); std::vector tokens; splitElements(field, tokens, true); for (const auto& t: tokens) { if (Poco::icompare(t, token) == 0) return true; } return false; } void MessageHeader::splitElements(const std::string& s, std::vector& elements, bool ignoreEmpty) { elements.clear(); std::string::const_iterator it = s.begin(); std::string::const_iterator end = s.end(); std::string elem; elem.reserve(64); while (it != end) { if (*it == '"') { elem += *it++; while (it != end && *it != '"') { if (*it == '\\') { ++it; if (it != end) elem += *it++; } else elem += *it++; } if (it != end) elem += *it++; } else if (*it == '\\') { ++it; if (it != end) elem += *it++; } else if (*it == ',') { Poco::trimInPlace(elem); if (!ignoreEmpty || !elem.empty()) elements.push_back(elem); elem.clear(); ++it; } else elem += *it++; } if (!elem.empty()) { Poco::trimInPlace(elem); if (!ignoreEmpty || !elem.empty()) elements.push_back(elem); } } void MessageHeader::splitParameters(const std::string& s, std::string& value, NameValueCollection& parameters) { value.clear(); parameters.clear(); std::string::const_iterator it = s.begin(); std::string::const_iterator end = s.end(); while (it != end && Poco::Ascii::isSpace(*it)) ++it; while (it != end && *it != ';') value += *it++; Poco::trimRightInPlace(value); if (it != end) ++it; splitParameters(it, end, parameters); } void MessageHeader::splitParameters(const std::string::const_iterator& begin, const std::string::const_iterator& end, NameValueCollection& parameters) { std::string pname; std::string pvalue; pname.reserve(32); pvalue.reserve(64); std::string::const_iterator it = begin; while (it != end) { pname.clear(); pvalue.clear(); while (it != end && Poco::Ascii::isSpace(*it)) ++it; while (it != end && *it != '=' && *it != ';') pname += *it++; Poco::trimRightInPlace(pname); if (it != end && *it != ';') ++it; while (it != end && Poco::Ascii::isSpace(*it)) ++it; while (it != end && *it != ';') { if (*it == '"') { ++it; while (it != end && *it != '"') { if (*it == '\\') { ++it; if (it != end) pvalue += *it++; } else pvalue += *it++; } if (it != end) ++it; } else if (*it == '\\') { ++it; if (it != end) pvalue += *it++; } else pvalue += *it++; } Poco::trimRightInPlace(pvalue); if (!pname.empty()) parameters.add(pname, pvalue); if (it != end) ++it; } } void MessageHeader::quote(const std::string& value, std::string& result, bool allowSpace) { bool mustQuote = false; for (std::string::const_iterator it = value.begin(); !mustQuote && it != value.end(); ++it) { if (!Poco::Ascii::isAlphaNumeric(*it) && *it != '.' && *it != '_' && *it != '-' && !(Poco::Ascii::isSpace(*it) && allowSpace)) mustQuote = true; } if (mustQuote) result += '"'; result.append(value); if (mustQuote) result += '"'; } void MessageHeader::decodeRFC2047(const std::string& ins, std::string& outs, const std::string& charset_to) { std::string tempout; StringTokenizer tokens(ins, "?"); std::string charset = toUpper(tokens[0]); std::string encoding = toUpper(tokens[1]); std::string text = tokens[2]; std::istringstream istr(text); if (encoding == "B") { // Base64 encoding. Base64Decoder decoder(istr); for (char c; decoder.get(c); tempout += c) {} } else if (encoding == "Q") { // Quoted encoding. for (char c; istr.get(c);) { if (c == '_') { //RFC 2047 _ is a space. tempout += " "; continue; } // FIXME: check that we have enought chars- if (c == '=') { // The next two chars are hex representation of the complete byte. std::string hex; for (int i = 0; i < 2; i++) { istr.get(c); hex += c; } hex = toUpper(hex); tempout += (char)(int)strtol(hex.c_str(), 0, 16); continue; } tempout += c; } } else { // Wrong encoding outs = ins; return; } // convert to the right charset. if (charset != charset_to) { try { TextEncoding& enc = TextEncoding::byName(charset); TextEncoding& dec = TextEncoding::byName(charset_to); TextConverter converter(enc, dec); converter.convert(tempout, outs); } catch (...) { // FIXME: Unsuported encoding... outs = tempout; } } else { // Not conversion necesary. outs = tempout; } } std::string MessageHeader::decodeWord(const std::string& text, const std::string& charset) { std::string outs, tmp = text; do { std::string tmp2; // find the begining of the next rfc2047 chunk size_t pos = tmp.find("=?"); if (pos == std::string::npos) { // No more found, return outs += tmp; break; } // check if there are standar text before the rfc2047 chunk, and if so, copy it. if (pos > 0) { outs += tmp.substr(0, pos); } // remove text already copied. tmp = tmp.substr(pos + 2); // find the first separator size_t pos1 = tmp.find("?"); if (pos1 == std::string::npos) { // not found. outs += tmp; break; } // find the second separator size_t pos2 = tmp.find("?", pos1 + 1); if (pos2 == std::string::npos) { // not found outs += tmp; break; } // find the end of the actual rfc2047 chunk size_t pos3 = tmp.find("?=", pos2 + 1); if (pos3 == std::string::npos) { // not found. outs += tmp; break; } // At this place, there are a valid rfc2047 chunk, so decode and copy the result. decodeRFC2047(tmp.substr(0, pos3), tmp2, charset); outs += tmp2; // Jump at the rest of the string and repeat the whole process. tmp = tmp.substr(pos3 + 2); } while (true); return outs; } } } // namespace Poco::Net