diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/contentDispositionField.cpp | 8 | ||||
-rw-r--r-- | src/contentTypeField.cpp | 8 | ||||
-rw-r--r-- | src/defaultParameter.cpp | 416 | ||||
-rw-r--r-- | src/parameter.cpp | 22 | ||||
-rw-r--r-- | src/parameterFactory.cpp | 23 | ||||
-rw-r--r-- | src/parameterizedHeaderField.cpp | 107 |
6 files changed, 569 insertions, 15 deletions
diff --git a/src/contentDispositionField.cpp b/src/contentDispositionField.cpp index bfbacc21..816c484f 100644 --- a/src/contentDispositionField.cpp +++ b/src/contentDispositionField.cpp @@ -74,13 +74,13 @@ void contentDispositionField::setReadDate(const datetime& readDate) } -const string contentDispositionField::getFilename() const +const word contentDispositionField::getFilename() const { return (dynamic_cast <const defaultParameter&>(*findParameter("filename")).getValue()); } -void contentDispositionField::setFilename(const string& filename) +void contentDispositionField::setFilename(const word& filename) { dynamic_cast <defaultParameter&>(*getParameter("filename")).setValue(filename); } @@ -88,13 +88,13 @@ void contentDispositionField::setFilename(const string& filename) const string contentDispositionField::getSize() const { - return (dynamic_cast <const defaultParameter&>(*findParameter("size")).getValue()); + return (dynamic_cast <const defaultParameter&>(*findParameter("size")).getValue().getBuffer()); } void contentDispositionField::setSize(const string& size) { - dynamic_cast <defaultParameter&>(*getParameter("size")).setValue(size); + dynamic_cast <defaultParameter&>(*getParameter("size")).setValue(word(size)); } diff --git a/src/contentTypeField.cpp b/src/contentTypeField.cpp index 61b4e5e4..28e31901 100644 --- a/src/contentTypeField.cpp +++ b/src/contentTypeField.cpp @@ -40,13 +40,13 @@ contentTypeField::contentTypeField(contentTypeField&) const string contentTypeField::getBoundary() const { - return (dynamic_cast <const defaultParameter&>(*findParameter("boundary")).getValue()); + return (dynamic_cast <const defaultParameter&>(*findParameter("boundary")).getValue().getBuffer()); } void contentTypeField::setBoundary(const string& boundary) { - dynamic_cast <defaultParameter&>(*getParameter("boundary")).setValue(boundary); + dynamic_cast <defaultParameter&>(*getParameter("boundary")).setValue(word(boundary)); } @@ -64,13 +64,13 @@ void contentTypeField::setCharset(const charset& ch) const string contentTypeField::getReportType() const { - return (dynamic_cast <const defaultParameter&>(*findParameter("report-type")).getValue()); + return (dynamic_cast <const defaultParameter&>(*findParameter("report-type")).getValue().getBuffer()); } void contentTypeField::setReportType(const string& reportType) { - dynamic_cast <defaultParameter&>(*getParameter("report-type")).setValue(reportType); + dynamic_cast <defaultParameter&>(*getParameter("report-type")).setValue(word(reportType)); } diff --git a/src/defaultParameter.cpp b/src/defaultParameter.cpp new file mode 100644 index 00000000..796b4d49 --- /dev/null +++ b/src/defaultParameter.cpp @@ -0,0 +1,416 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 Vincent Richard <[email protected]> +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "vmime/defaultParameter.hpp" +#include "vmime/parserHelpers.hpp" + + +namespace vmime +{ + + +defaultParameter::defaultParameter() +{ +} + + +defaultParameter& defaultParameter::operator=(const defaultParameter& other) +{ + copyFrom(other); + return (*this); +} + + +const word& defaultParameter::getValue() const +{ + return (m_value); +} + + +word& defaultParameter::getValue() +{ + return (m_value); +} + + +void defaultParameter::setValue(const word& value) +{ + m_value = value; +} + + +void defaultParameter::setValue(const component& value) +{ + const word& v = dynamic_cast <const word&>(value); + m_value = v; +} + + +void defaultParameter::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_value = word(string(buffer.begin() + position, buffer.begin() + end), + charset(charsets::US_ASCII)); + + if (newPosition) + *newPosition = end; +} + + +void defaultParameter::parse(const std::vector <valueChunk>& chunks) +{ + bool foundCharsetChunk = false; + + charset ch(charsets::US_ASCII); + std::ostringstream value; + + for (std::vector <valueChunk>::size_type i = 0 ; i < chunks.size() ; ++i) + { + const valueChunk& chunk = chunks[i]; + + // Decode following data + if (chunk.encoded) + { + const string::size_type len = chunk.data.length(); + string::size_type pos = 0; + + // If this is the first encoded chunk, extract charset + // and language information + if (!foundCharsetChunk) + { + // Eg. "us-ascii'en'This%20is%20even%20more%20" + string::size_type q = chunk.data.find_first_of('\''); + + if (q != string::npos) + { + const string chs = chunk.data.substr(0, q); + + if (!chs.empty()) + ch = charset(chs); + + ++q; + pos = q; + } + + q = chunk.data.find_first_of('\'', pos); + + if (q != string::npos) + { + // Ignore language + ++q; + pos = q; + } + + foundCharsetChunk = true; + } + + for (string::size_type i = pos ; i < len ; ++i) + { + const string::value_type c = chunk.data[i]; + + if (c == '%' && i + 2 < len) + { + string::value_type v = 0; + + // First char + switch (chunk.data[i + 1]) + { + case 'a': case 'A': v += 10; break; + case 'b': case 'B': v += 11; break; + case 'c': case 'C': v += 12; break; + case 'd': case 'D': v += 13; break; + case 'e': case 'E': v += 14; break; + case 'f': case 'F': v += 15; break; + default: // assume 0-9 + + v += (chunk.data[i + 1] - '0'); + break; + } + + v *= 16; + + // Second char + switch (chunk.data[i + 2]) + { + case 'a': case 'A': v += 10; break; + case 'b': case 'B': v += 11; break; + case 'c': case 'C': v += 12; break; + case 'd': case 'D': v += 13; break; + case 'e': case 'E': v += 14; break; + case 'f': case 'F': v += 15; break; + default: // assume 0-9 + + v += (chunk.data[i + 2] - '0'); + break; + } + + value << v; + + i += 2; // skip next 2 chars + } + else + { + value << c; + } + } + } + // Simply copy data, as it is not encoded + else + { + value << chunk.data; + } + } + + m_value = word(value.str(), ch); +} + + +void defaultParameter::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + const string& name = getName(); + const string& value = m_value.getBuffer(); + + // For compatibility with implementations that do not understand RFC-2231, + // also generate a normal "7bit/us-ascii" parameter + string::size_type pos = curLinePos; + + if (pos + name.length() + 10 > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + pos = NEW_LINE_SEQUENCE_LENGTH; + } + + bool needQuoting = false; + + for (string::size_type i = 0 ; (i < value.length()) && (pos < maxLineLength - 4) ; ++i) + { + switch (value[i]) + { + // Characters that need to be quoted _and_ escaped + case '"': + case '\\': + // Other characters that need quoting + case ' ': + case '\t': + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case ':': + case '/': + case '[': + case ']': + case '?': + case '=': + + needQuoting = true; + break; + } + } + + if (needQuoting) + { + os << name << "=\""; + pos += name.length() + 2; + } + else + { + os << name << "="; + pos += name.length() + 1; + } + + bool extended = false; + + for (string::size_type i = 0 ; (i < value.length()) && (pos < maxLineLength - 4) ; ++i) + { + const char_t c = value[i]; + + if (/* needQuoting && */ (c == '"' || c == '\\')) // 'needQuoting' is implicit + { + os << '\\' << value[i]; // escape 'x' with '\x' + pos += 2; + } + else if (parserHelpers::isAscii(c)) + { + os << value[i]; + ++pos; + } + else + { + extended = true; + } + } + + if (needQuoting) + { + os << '"'; + ++pos; + } + + // Also generate an extended parameter if the value contains 8-bit characters + // or is too long for a single line + if (extended || (value.length() >= (maxLineLength - name.length() + 3))) + { + os << ';'; + ++pos; + + /* RFC-2231 + * ======== + * + * Content-Type: message/external-body; access-type=URL; + * URL*0="ftp://"; + * URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" + * + * Content-Type: application/x-stuff; + * title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A + * + * Content-Type: application/x-stuff; + * title*0*=us-ascii'en'This%20is%20even%20more%20 + * title*1*=%2A%2A%2Afun%2A%2A%2A%20 + * title*2="isn't it!" + */ + + // Check whether there is enough space for the first section: + // parameter name, section identifier, charset and separators + // + at least 5 characters for the value + const string::size_type firstSectionLength = + name.length() + 4 /* *0*= */ + 2 /* '' */ + + m_value.getCharset().getName().length(); + + if (pos + firstSectionLength + 5 >= maxLineLength) + { + os << NEW_LINE_SEQUENCE; + pos = NEW_LINE_SEQUENCE_LENGTH; + } + + // Split text into multiple sections that fit on one line + int sectionCount = 0; + std::vector <string> sectionText; + + string currentSection; + string::size_type currentSectionLength = firstSectionLength; + + for (string::size_type i = 0 ; i < value.length() ; ++i) + { + // Check whether we should start a new line (taking into + // account the next character will be encoded = worst case) + if (currentSectionLength + 3 >= maxLineLength) + { + sectionText.push_back(currentSection); + sectionCount++; + + currentSection.clear(); + currentSectionLength = NEW_LINE_SEQUENCE_LENGTH + + name.length() + 6; + } + + // Output next character + const char_t c = value[i]; + bool encode = false; + + switch (c) + { + // special characters + case ' ': + case '\t': + case '\r': + case '\n': + case '"': + case ';': + case ',': + + encode = true; + break; + + default: + + encode = (!parserHelpers::isPrint(c) || + !parserHelpers::isAscii(c)); + + break; + } + + if (encode) // need encoding + { + const int h1 = static_cast <unsigned char>(c) / 16; + const int h2 = static_cast <unsigned char>(c) % 16; + + currentSection += '%'; + currentSection += "0123456789ABCDEF"[h1]; + currentSection += "0123456789ABCDEF"[h2]; + + pos += 3; + currentSectionLength += 3; + } + else + { + currentSection += value[i]; + + ++pos; + ++currentSectionLength; + } + } + + if (!currentSection.empty()) + { + sectionText.push_back(currentSection); + sectionCount++; + } + + // Output sections + for (int sectionNumber = 0 ; sectionNumber < sectionCount ; ++sectionNumber) + { + os << name; + + if (sectionCount != 1) // no section specifier when only a single one + { + os << '*'; + os << sectionNumber; + } + + os << "*="; + + if (sectionNumber == 0) + { + os << m_value.getCharset().getName(); + os << '\'' << /* No language */ '\''; + } + + os << sectionText[sectionNumber]; + + if (sectionNumber + 1 < sectionCount) + { + os << ';'; + os << NEW_LINE_SEQUENCE; + pos = NEW_LINE_SEQUENCE_LENGTH; + } + } + } + + if (newLinePos) + *newLinePos = pos; +} + + + +} // vmime diff --git a/src/parameter.cpp b/src/parameter.cpp index 1cc2e697..988755be 100644 --- a/src/parameter.cpp +++ b/src/parameter.cpp @@ -63,6 +63,23 @@ void parameter::parse(const string& buffer, const string::size_type position, getValue().parse(buffer, position, end, newPosition); setParsedBounds(position, end); + + if (newPosition) + *newPosition = end; +} + + +void parameter::parse(const std::vector <valueChunk>& chunks) +{ + string value; + + for (std::vector <valueChunk>::const_iterator it = chunks.begin() ; + it != chunks.end() ; ++it) + { + value += (*it).data; + } + + getValue().parse(value, 0, value.length(), NULL); } @@ -87,10 +104,13 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL void parameter::generateValue(utility::outputStream& os, const string::size_type /* maxLineLength */, const string::size_type curLinePos, string::size_type* newLinePos) const { + // NOTE: This default implementation does not support parameter + // values that span on several lines ('defaultParameter' can do + // that, following rules specified in RFC-2231). + std::ostringstream valueStream; utility::outputStreamAdapter valueStreamV(valueStream); - // TODO: can we imagine having values that span on multiple lines? getValue().generate(valueStreamV, lineLengthLimits::infinite, 0, NULL); const string value(valueStream.str()); diff --git a/src/parameterFactory.cpp b/src/parameterFactory.cpp index 77450409..a5a7a81c 100644 --- a/src/parameterFactory.cpp +++ b/src/parameterFactory.cpp @@ -73,4 +73,27 @@ parameter* parameterFactory::create } +parameter* parameterFactory::create(const string& name, const component& value) +{ + const string lcName = utility::stringUtils::toLower(name); + + NameMap::const_iterator pos = m_nameMap.find(lcName); + parameter* param = NULL; + + if (pos != m_nameMap.end()) + { + param = ((*pos).second)(); + } + else + { + param = registerer <defaultParameter>::creator(); + } + + param->m_name = name; + param->setValue(value); + + return (param); +} + + } // vmime diff --git a/src/parameterizedHeaderField.cpp b/src/parameterizedHeaderField.cpp index 92631d9d..d3c8f67c 100644 --- a/src/parameterizedHeaderField.cpp +++ b/src/parameterizedHeaderField.cpp @@ -55,6 +55,20 @@ parameterizedHeaderField::parameterizedHeaderField() ; to use within parameter values */ + +#ifndef VMIME_BUILDING_DOC + +struct paramInfo +{ + bool extended; + std::vector <parameter::valueChunk> value; + string::size_type start; + string::size_type end; +}; + +#endif // VMIME_BUILDING_DOC + + void parameterizedHeaderField::parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { @@ -73,6 +87,8 @@ void parameterizedHeaderField::parse(const string& buffer, const string::size_ty // If there is one or more parameters following... if (p < pend) { + std::map <string, paramInfo> params; + while (*p == ';') { // Skip ';' @@ -183,20 +199,99 @@ void parameterizedHeaderField::parse(const string& buffer, const string::size_ty // Don't allow ill-formed parameters if (attrStart != attrEnd && value.length()) { - // Append this parameter to the list - parameter* param = parameterFactory::getInstance()-> - create(string(buffer.begin() + attrStart, - buffer.begin() + attrEnd), value); + string name(buffer.begin() + attrStart, buffer.begin() + attrEnd); - param->setParsedBounds(attrStart, position + (p - pstart)); + // Check for RFC-2231 extended parameters + bool extended = false; + bool encoded = false; - appendParameter(param); + if (name[name.length() - 1] == '*') + { + name.erase(name.end() - 1, name.end()); + + extended = true; + encoded = true; + } + + // Check for RFC-2231 multi-section parameters + const string::size_type star = name.find_last_of('*'); + + if (star != string::npos) + { + bool allDigits = true; + + for (string::size_type i = star + 1 ; allDigits && (i < name.length()) ; ++i) + allDigits = parserHelpers::isDigit(name[i]); + + if (allDigits) + { + name.erase(name.begin() + star, name.end()); + extended = true; + } + + // NOTE: we ignore section number, and we suppose that + // the sequence is correct (ie. the sections appear + // in order: param*0, param*1...) + } + + // Add/replace/modify the parameter + const std::map <string, paramInfo>::iterator it = params.find(name); + + if (it != params.end()) + { + paramInfo& info = (*it).second; + + // An extended parameter replaces a normal one + if (!info.extended) + { + info.extended = extended; + info.value.clear(); + info.start = attrStart; + } + + // Append a new section for a multi-section parameter + parameter::valueChunk chunk; + chunk.encoded = encoded; + chunk.data = value; + + info.value.push_back(chunk); + info.end = position + (p - pstart); + } + else + { + parameter::valueChunk chunk; + chunk.encoded = encoded; + chunk.data = value; + + paramInfo info; + info.extended = extended; + info.value.push_back(chunk); + info.start = attrStart; + info.end = position + (p - pstart); + + // Insert a new parameter + params.insert(std::map <string, paramInfo>::value_type(name, info)); + } } // Skip white-spaces after this parameter while (p < pend && parserHelpers::isSpace(*p)) ++p; } } + + for (std::map <string, paramInfo>::const_iterator it = params.begin() ; + it != params.end() ; ++it) + { + const paramInfo& info = (*it).second; + + // Append this parameter to the list + parameter* param = parameterFactory::getInstance()->create((*it).first); + + param->parse(info.value); + param->setParsedBounds(info.start, info.end); + + appendParameter(param); + } } if (newPosition) |