aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2014-06-17 19:08:22 +0000
committerVincent Richard <[email protected]>2014-06-17 19:08:22 +0000
commit0863f50c261be0a10e98b498d4007cce27184021 (patch)
tree4adc34d0fe19861c515b0fce31d06f1256aa05fa
parentCheck for NULL pointer in 'ai_canonname'. (diff)
downloadvmime-0863f50c261be0a10e98b498d4007cce27184021.tar.gz
vmime-0863f50c261be0a10e98b498d4007cce27184021.zip
Allow choosing between encoding modes for parameter values.
-rw-r--r--src/vmime/generationContext.cpp17
-rw-r--r--src/vmime/generationContext.hpp47
-rw-r--r--src/vmime/parameter.cpp53
-rw-r--r--tests/parser/parameterTest.cpp108
4 files changed, 182 insertions, 43 deletions
diff --git a/src/vmime/generationContext.cpp b/src/vmime/generationContext.cpp
index e9662883..0a76f4d0 100644
--- a/src/vmime/generationContext.cpp
+++ b/src/vmime/generationContext.cpp
@@ -32,7 +32,8 @@ generationContext::generationContext()
: m_maxLineLength(lineLengthLimits::convenient),
m_prologText("This is a multi-part message in MIME format. Your mail reader " \
"does not understand MIME message format."),
- m_epilogText("")
+ m_epilogText(""),
+ m_paramValueMode(PARAMETER_VALUE_RFC2231_ONLY)
{
}
@@ -89,6 +90,19 @@ void generationContext::setEpilogText(const string& epilogText)
}
+void generationContext::setEncodedParameterValueMode(const EncodedParameterValueModes mode)
+{
+ m_paramValueMode = mode;
+}
+
+
+generationContext::EncodedParameterValueModes
+ generationContext::getEncodedParameterValueMode() const
+{
+ return m_paramValueMode;
+}
+
+
generationContext& generationContext::operator=(const generationContext& ctx)
{
copyFrom(ctx);
@@ -103,6 +117,7 @@ void generationContext::copyFrom(const generationContext& ctx)
m_maxLineLength = ctx.m_maxLineLength;
m_prologText = ctx.m_prologText;
m_epilogText = ctx.m_epilogText;
+ m_paramValueMode = ctx.m_paramValueMode;
}
diff --git a/src/vmime/generationContext.hpp b/src/vmime/generationContext.hpp
index 949f06ac..22700c90 100644
--- a/src/vmime/generationContext.hpp
+++ b/src/vmime/generationContext.hpp
@@ -82,6 +82,51 @@ public:
*/
void setEpilogText(const string& epilogText);
+ /** Modes available for generating values in parameterized header fields.
+ */
+ enum EncodedParameterValueModes
+ {
+ PARAMETER_VALUE_NO_ENCODING, /**< Only generate 7-bit (ASCII-only) values,
+ even if the value contains non-ASCII chars or
+ if folding is needed. */
+ PARAMETER_VALUE_RFC2047_ONLY, /**< Only generate RFC-2047 values (do not use
+ RFC-2231). This is non-standard but most
+ mail clients support it. */
+ PARAMETER_VALUE_RFC2231_ONLY, /**< Only generate RFC-2231 values (do not use
+ RFC-2047). Some mail clients may not support
+ it. This is the default. */
+ PARAMETER_VALUE_RFC2231_AND_RFC2047 /**< Generate both RFC-2047- and RFC-2231-encoded
+ values. */
+ };
+
+ /** Sets the mode used for generating parameter values in a parameterized
+ * header field (see parameterizedHeaderField class).
+ *
+ * PARAMETER_VALUE_NO_ENCODING or PARAMETER_VALUE_RFC2047_ONLY
+ * can be used for compatibility with implementations that do not
+ * understand RFC-2231, to generate a normal parameter value.
+ * PARAMETER_VALUE_RFC2047_ONLY is non-standard (and expressly
+ * prohibited by the RFC) but most mail clients support it.
+ *
+ * Notice: if both the normal value and the extended value are present,
+ * the latter can be ignored by mail processing systems. This may lead
+ * to annoying problems, for example, with strange names of attachments
+ * with all but 7-bit ascii characters removed, etc. Either
+ * PARAMETER_VALUE_RFC2231_ONLY or PARAMETER_VALUE_RFC2047_ONLY should
+ * be preferred over PARAMETER_VALUE_RFC2231_AND_RFC2047, not to create
+ * a normal value if the extended value is to be generated.
+ *
+ * @param mode parameter value generation mode
+ */
+ void setEncodedParameterValueMode(const EncodedParameterValueModes mode);
+
+ /** Returns the mode used for generating parameter values in a parameterized
+ * header field (see parameterizedHeaderField class).
+ *
+ * @return parameter value generation mode
+ */
+ EncodedParameterValueModes getEncodedParameterValueMode() const;
+
/** Returns the default context used for generating messages.
*
* @return a reference to the default generation context
@@ -97,6 +142,8 @@ protected:
string m_prologText;
string m_epilogText;
+
+ EncodedParameterValueModes m_paramValueMode;
};
diff --git a/src/vmime/parameter.cpp b/src/vmime/parameter.cpp
index b8d5b36e..45e68ad6 100644
--- a/src/vmime/parameter.cpp
+++ b/src/vmime/parameter.cpp
@@ -279,7 +279,13 @@ void parameter::generateImpl
const string& value = m_value->getBuffer();
// For compatibility with implementations that do not understand RFC-2231,
- // also generate a normal "7bit/us-ascii" parameter
+ // we may also generate a normal "7bit/us-ascii" parameter
+ generationContext::EncodedParameterValueModes
+ genMode = ctx.getEncodedParameterValueMode();
+
+#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
+ genMode = generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047;
+#endif
// [By Eugene A. Shatokhin]
// Note that if both the normal "7bit/us-ascii" value and the extended
@@ -366,7 +372,8 @@ void parameter::generateImpl
const bool alwaysEncode = m_value->getCharset().getRecommendedEncoding(recommendedEnc);
bool extended = alwaysEncode;
- if (needQuotedPrintable)
+ if ((needQuotedPrintable || cutValue) &&
+ genMode != generationContext::PARAMETER_VALUE_NO_ENCODING)
{
// Send the name in quoted-printable, so outlook express et.al.
// will understand the real filename
@@ -378,7 +385,8 @@ void parameter::generateImpl
else
{
// Do not chop off this value, but just add the complete name as one header line.
- for (size_t i = 0 ; i < value.length() ; ++i)
+ for (size_t i = 0, n = value.length(), curValueLength = 0 ;
+ i < n && curValueLength < valueLength ; ++i)
{
const char_t c = value[i];
@@ -391,6 +399,7 @@ void parameter::generateImpl
{
sevenBitStream << value[i];
++pos;
+ ++curValueLength;
}
else
{
@@ -406,26 +415,29 @@ void parameter::generateImpl
++pos;
}
-#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
- os << sevenBitBuffer;
-#endif // !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
+ if (genMode == generationContext::PARAMETER_VALUE_RFC2047_ONLY ||
+ genMode == generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047)
+ {
+ os << sevenBitBuffer;
+ }
// Also generate an extended parameter if the value contains 8-bit characters
// or is too long for a single line
- if (extended || cutValue)
+ if ((extended || cutValue) &&
+ genMode != generationContext::PARAMETER_VALUE_NO_ENCODING &&
+ genMode != generationContext::PARAMETER_VALUE_RFC2047_ONLY)
{
-#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
-
- os << ';';
- ++pos;
-
-#else // !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
-
- // The data output to 'sevenBitBuffer' will be discarded in this case
- pos = curLinePos;
-
-#endif // VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
+ if (genMode == generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047)
+ {
+ os << ';';
+ ++pos;
+ }
+ else
+ {
+ // The data output to 'sevenBitBuffer' will be discarded in this case
+ pos = curLinePos;
+ }
/* RFC-2231
* ========
@@ -572,8 +584,8 @@ void parameter::generateImpl
}
}
}
-#if !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
- else
+ else if (!(genMode == generationContext::PARAMETER_VALUE_RFC2047_ONLY ||
+ genMode == generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047))
{
// The value does not contain 8-bit characters and
// is short enough for a single line.
@@ -582,7 +594,6 @@ void parameter::generateImpl
// Output what has been stored in temporary buffer so far
os << sevenBitBuffer;
}
-#endif // !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
if (newLinePos)
*newLinePos = pos;
diff --git a/tests/parser/parameterTest.cpp b/tests/parser/parameterTest.cpp
index 2edfabbe..12c17444 100644
--- a/tests/parser/parameterTest.cpp
+++ b/tests/parser/parameterTest.cpp
@@ -53,6 +53,26 @@ VMIME_TEST_SUITE_BEGIN(parameterTest)
setValue(vmime::headerFieldFactory::getInstance()->createValue(getName()));
setValue(vmime::word("X"));
}
+
+ using vmime::parameterizedHeaderField::generate;
+
+ const vmime::string generate
+ (const vmime::generationContext::EncodedParameterValueModes genMode,
+ const vmime::size_t maxLineLength = 0) const
+ {
+ vmime::generationContext ctx(vmime::generationContext::getDefaultContext());
+ ctx.setEncodedParameterValueMode(genMode);
+
+ if (maxLineLength != 0)
+ ctx.setMaxLineLength(maxLineLength);
+
+ std::ostringstream oss;
+ vmime::utility::outputStreamAdapter adapter(oss);
+
+ vmime::parameterizedHeaderField::generate(ctx, adapter);
+
+ return oss.str();
+ }
};
@@ -245,11 +265,17 @@ VMIME_TEST_SUITE_BEGIN(parameterTest)
p1.appendParameter(vmime::make_shared <vmime::parameter>("param1",
vmime::word("value 1\xe9", vmime::charset("charset"))));
-#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
- VASSERT_EQ("1", "F: X; param1=\"value 1\";param1*=charset''value%201%E9", p1.generate());
-#else
- VASSERT_EQ("1", "F: X; param1*=charset''value%201%E9", p1.generate());
-#endif
+ VASSERT_EQ("1.no-encoding", "F: X; param1=\"value 1\"",
+ p1.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING));
+
+ VASSERT_EQ("1.rfc2047", "F: X; param1=\"=?charset?Q?value_1=E9?=\"",
+ p1.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY));
+
+ VASSERT_EQ("1.rfc2231", "F: X; param1*=charset''value%201%E9",
+ p1.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY));
+
+ VASSERT_EQ("1.both", "F: X; param1=\"=?charset?Q?value_1=E9?=\";param1*=charset''value%201%E9",
+ p1.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047));
// Value that spans on multiple lines
parameterizedHeaderField p2;
@@ -257,26 +283,34 @@ VMIME_TEST_SUITE_BEGIN(parameterTest)
vmime::word("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
vmime::charset("charset"))));
-#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
- VASSERT_EQ("2", "F: X; \r\n "
- "param1=abcdefghijklm;\r\n "
+ VASSERT_EQ("2.no-encoding", "F: X; \r\n "
+ "param1=abcdefghijkl",
+ p2.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING, 25)); // max line length = 25
+
+ VASSERT_EQ("2.rfc2047", "F: X; \r\n "
+ "param1=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ p2.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY, 25)); // max line length = 25
+
+ VASSERT_EQ("2.rfc2231", "F: X; \r\n "
"param1*0*=charset''abc;\r\n "
"param1*1*=defghijkl;\r\n "
"param1*2*=mnopqrstu;\r\n "
"param1*3*=vwxyzABCD;\r\n "
"param1*4*=EFGHIJKLM;\r\n "
"param1*5*=NOPQRSTUV;\r\n "
- "param1*6*=WXYZ", p2.generate(25)); // max line length = 25
-#else
- VASSERT_EQ("2", "F: X; \r\n "
+ "param1*6*=WXYZ",
+ p2.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY, 25)); // max line length = 25
+
+ VASSERT_EQ("2.both", "F: X; \r\n "
+ "param1=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ;\r\n "
"param1*0*=charset''abc;\r\n "
"param1*1*=defghijkl;\r\n "
"param1*2*=mnopqrstu;\r\n "
"param1*3*=vwxyzABCD;\r\n "
"param1*4*=EFGHIJKLM;\r\n "
"param1*5*=NOPQRSTUV;\r\n "
- "param1*6*=WXYZ", p2.generate(25)); // max line length = 25
-#endif
+ "param1*6*=WXYZ",
+ p2.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047, 25)); // max line length = 25
// Non-ASCII parameter value
parameterizedHeaderField p3;
@@ -284,26 +318,58 @@ VMIME_TEST_SUITE_BEGIN(parameterTest)
vmime::word("δσσσσσσσσσσσσσσσσσσσσδσδα δσαδσδσαδσαδασδασ δσαδασδσα δσαδασδσα δασδασδασ δασαχφδδσα 2008.doc",
vmime::charset("utf-8"))));
-#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
- VASSERT_EQ("3", "F: X; \r\n "
- "param1=\" 2008.doc\";param1*0*=utf-8''%CE%B4%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n "
+ VASSERT_EQ("3.no-encoding", "F: X; \r\n "
+ "param1=\" 2008.doc\"",
+ p3.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING, 80)); // max line length = 80
+
+ VASSERT_EQ("3.7bit-only", "F: X; \r\n "
+ "param1=\"=?utf-8?B?zrTPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+DzrTPg860?=\r\n "
+ "=?utf-8?B?zrEgzrTPg86xzrTPg860z4POsc60z4POsc60zrHPg860zrHPgyDOtM+DzrHOtM6x?=\r\n "
+ "=?utf-8?B?z4POtM+DzrEgzrTPg86xzrTOsc+DzrTPg86xIM60zrHPg860zrHPg860zrHPgyDOtA==?=\r\n "
+ "=?utf-8?B?zrHPg86xz4fPhs60zrTPg86xIDIwMDguZG9j?=\"",
+ p3.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY, 80)); // max line length = 80
+
+ VASSERT_EQ("3.both", "F: X; \r\n "
+ "param1=\"=?utf-8?B?zrTPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+DzrTPg860?=\r\n "
+ "=?utf-8?B?zrEgzrTPg86xzrTPg860z4POsc60z4POsc60zrHPg860zrHPgyDOtM+DzrHOtM6x?=\r\n "
+ "=?utf-8?B?z4POtM+DzrEgzrTPg86xzrTOsc+DzrTPg86xIM60zrHPg860zrHPg860zrHPgyDOtA==?=\r\n "
+ "=?utf-8?B?zrHPg86xz4fPhs60zrTPg86xIDIwMDguZG9j?=\";\r\n "
+ "param1*0*=utf-8''%CE%B4%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n "
"param1*1*=%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n "
"param1*2*=%CE%B4%CF%83%CE%B4%CE%B1%20%CE%B4%CF%83%CE%B1%CE%B4%CF%83%CE%B4%CF;\r\n "
"param1*3*=%83%CE%B1%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20;\r\n "
"param1*4*=%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CF;\r\n "
"param1*5*=%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CE%B1%CF%83;\r\n "
"param1*6*=%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20%CE%B4%CE%B1%CF%83%CE%B1%CF;\r\n "
- "param1*7*=%87%CF%86%CE%B4%CE%B4%CF%83%CE%B1%202008.doc", p3.generate(80));
-#else
- VASSERT_EQ("3", "F: X; param1*0*=utf-8''%CE%B4%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n "
+ "param1*7*=%87%CF%86%CE%B4%CE%B4%CF%83%CE%B1%202008.doc",
+ p3.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047, 80)); // max line length = 80
+
+ VASSERT_EQ("3.either", "F: X; param1*0*=utf-8''%CE%B4%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n "
"param1*1*=%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n "
"param1*2*=%CE%B4%CF%83%CE%B4%CE%B1%20%CE%B4%CF%83%CE%B1%CE%B4%CF%83%CE%B4%CF;\r\n "
"param1*3*=%83%CE%B1%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20;\r\n "
"param1*4*=%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CF;\r\n "
"param1*5*=%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CE%B1%CF%83;\r\n "
"param1*6*=%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20%CE%B4%CE%B1%CF%83%CE%B1%CF;\r\n "
- "param1*7*=%87%CF%86%CE%B4%CE%B4%CF%83%CE%B1%202008.doc", p3.generate(80));
-#endif
+ "param1*7*=%87%CF%86%CE%B4%CE%B4%CF%83%CE%B1%202008.doc",
+ p3.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY, 80)); // max line length = 80
+
+ // No encoding needed
+ parameterizedHeaderField p4;
+ p4.appendParameter(vmime::make_shared <vmime::parameter>("param1",
+ vmime::word("va lue", vmime::charset("charset"))));
+
+ VASSERT_EQ("4.no-encoding", "F: X; param1=\"va lue\"",
+ p4.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING));
+
+ VASSERT_EQ("4.rfc2047", "F: X; param1=\"va lue\"",
+ p4.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY));
+
+ VASSERT_EQ("4.rfc2231", "F: X; param1=\"va lue\"",
+ p4.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY));
+
+ VASSERT_EQ("4.both", "F: X; param1=\"va lue\"",
+ p4.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047));
}
void testNonStandardEncodedParam()