aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/contentDispositionField.cpp8
-rw-r--r--src/contentTypeField.cpp8
-rw-r--r--src/defaultParameter.cpp416
-rw-r--r--src/parameter.cpp22
-rw-r--r--src/parameterFactory.cpp23
-rw-r--r--src/parameterizedHeaderField.cpp107
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)