aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/address.cpp7
-rw-r--r--src/addressList.cpp17
-rw-r--r--src/base.cpp6
-rw-r--r--src/body.cpp22
-rw-r--r--src/bodyPart.cpp16
-rw-r--r--src/charset.cpp30
-rw-r--r--src/charsetConverter.cpp393
-rw-r--r--src/charsetConverterOptions.cpp37
-rw-r--r--src/charsetConverter_iconv.cpp435
-rw-r--r--src/charsetConverter_idna.cpp168
-rw-r--r--src/component.cpp65
-rw-r--r--src/constants.cpp2
-rw-r--r--src/contentDisposition.cpp10
-rw-r--r--src/context.cpp87
-rw-r--r--src/dateTime.cpp10
-rw-r--r--src/disposition.cpp12
-rw-r--r--src/emailAddress.cpp513
-rw-r--r--src/encoding.cpp10
-rw-r--r--src/generationContext.cpp109
-rw-r--r--src/header.cpp14
-rw-r--r--src/headerField.cpp21
-rw-r--r--src/mailbox.cpp77
-rw-r--r--src/mailboxField.cpp7
-rw-r--r--src/mailboxGroup.cpp21
-rw-r--r--src/mailboxList.cpp11
-rw-r--r--src/mdn/MDNHelper.cpp2
-rw-r--r--src/mediaType.cpp12
-rw-r--r--src/message.cpp30
-rw-r--r--src/messageId.cpp19
-rw-r--r--src/messageIdSequence.cpp17
-rw-r--r--src/net/sendmail/sendmailTransport.cpp4
-rw-r--r--src/net/smtp/SMTPCommand.cpp15
-rw-r--r--src/parameter.cpp28
-rw-r--r--src/parameterizedHeaderField.cpp18
-rw-r--r--src/parsingContext.cpp (renamed from src/options.cpp)35
-rw-r--r--src/path.cpp10
-rw-r--r--src/relay.cpp20
-rw-r--r--src/text.cpp45
-rw-r--r--src/utility/stringUtils.cpp33
-rw-r--r--src/word.cpp98
-rw-r--r--src/wordEncoder.cpp28
41 files changed, 1817 insertions, 697 deletions
diff --git a/src/address.cpp b/src/address.cpp
index eccf4e21..ab207cf6 100644
--- a/src/address.cpp
+++ b/src/address.cpp
@@ -66,8 +66,9 @@ address-list = (address *("," address)) / obs-addr-list
*/
-ref <address> address::parseNext(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+ref <address> address::parseNext
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
bool escaped = false;
bool quoted = false;
@@ -179,7 +180,7 @@ ref <address> address::parseNext(const string& buffer, const string::size_type p
? create <mailboxGroup>().dynamicCast <address>()
: create <mailbox>().dynamicCast <address>();
- parsedAddress->parse(buffer, start, pos, NULL);
+ parsedAddress->parse(ctx, buffer, start, pos, NULL);
parsedAddress->setParsedBounds(start, pos);
return (parsedAddress);
diff --git a/src/addressList.cpp b/src/addressList.cpp
index 467a283a..5e033f38 100644
--- a/src/addressList.cpp
+++ b/src/addressList.cpp
@@ -50,8 +50,9 @@ addressList::~addressList()
}
-void addressList::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void addressList::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
removeAllAddresses();
@@ -59,7 +60,7 @@ void addressList::parseImpl(const string& buffer, const string::size_type positi
while (pos < end)
{
- ref <address> parsedAddress = address::parseNext(buffer, pos, end, &pos);
+ ref <address> parsedAddress = address::parseNext(ctx, buffer, pos, end, &pos);
if (parsedAddress != NULL)
m_list.push_back(parsedAddress);
@@ -72,16 +73,20 @@ void addressList::parseImpl(const string& buffer, const string::size_type positi
}
-void addressList::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void addressList::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
string::size_type pos = curLinePos;
+ generationContext tmpCtx(ctx);
+ tmpCtx.setMaxLineLength(tmpCtx.getMaxLineLength() - 2);
+
if (!m_list.empty())
{
for (std::vector <ref <address> >::const_iterator i = m_list.begin() ; ; )
{
- (*i)->generate(os, maxLineLength - 2, pos, &pos);
+ (*i)->generate(ctx, os, pos, &pos);
if (++i == m_list.end())
break;
diff --git a/src/base.cpp b/src/base.cpp
index 47262faf..d5f3e787 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -40,7 +40,8 @@
#include "vmime/utility/encoder/encoderFactory.hpp"
#include "vmime/headerFieldFactory.hpp"
#include "vmime/textPartFactory.hpp"
-#include "vmime/options.hpp"
+#include "vmime/generationContext.hpp"
+#include "vmime/parsingContext.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES
#include "vmime/net/serviceFactory.hpp"
@@ -132,7 +133,8 @@ public:
initializer()
{
- options::getInstance();
+ parsingContext::getDefaultContext();
+ generationContext::getDefaultContext();
utility::encoder::encoderFactory::getInstance();
headerFieldFactory::getInstance();
diff --git a/src/body.cpp b/src/body.cpp
index 20781012..8c599b99 100644
--- a/src/body.cpp
+++ b/src/body.cpp
@@ -24,8 +24,6 @@
#include "vmime/bodyPart.hpp"
#include "vmime/body.hpp"
-#include "vmime/options.hpp"
-
#include "vmime/contentTypeField.hpp"
#include "vmime/text.hpp"
@@ -56,7 +54,8 @@ body::~body()
void body::parseImpl
- (ref <utility::parserInputStreamAdapter> parser,
+ (const parsingContext& /* ctx */,
+ ref <utility::parserInputStreamAdapter> parser,
const utility::stream::size_type position,
const utility::stream::size_type end,
utility::stream::size_type* newPosition)
@@ -381,8 +380,9 @@ void body::parseImpl
}
-void body::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type /* curLinePos */, string::size_type* newLinePos) const
+void body::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type /* curLinePos */, string::size_type* newLinePos) const
{
// MIME-Multipart
if (getPartCount() != 0)
@@ -418,7 +418,7 @@ void body::generateImpl(utility::outputStream& os, const string::size_type maxLi
const string& prologText =
m_prologText.empty()
? (isRootPart()
- ? options::getInstance()->multipart.getPrologText()
+ ? ctx.getPrologText()
: NULL_STRING
)
: m_prologText;
@@ -426,7 +426,7 @@ void body::generateImpl(utility::outputStream& os, const string::size_type maxLi
const string& epilogText =
m_epilogText.empty()
? (isRootPart()
- ? options::getInstance()->multipart.getEpilogText()
+ ? ctx.getEpilogText()
: NULL_STRING
)
: m_epilogText;
@@ -435,7 +435,7 @@ void body::generateImpl(utility::outputStream& os, const string::size_type maxLi
{
text prolog(prologText, vmime::charset("us-ascii"));
- prolog.encodeAndFold(os, maxLineLength, 0,
+ prolog.encodeAndFold(ctx, os, 0,
NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);
os << CRLF;
@@ -447,7 +447,7 @@ void body::generateImpl(utility::outputStream& os, const string::size_type maxLi
{
os << CRLF;
- getPartAt(p)->generate(os, maxLineLength, 0);
+ getPartAt(p)->generate(ctx, os, 0);
os << CRLF << "--" << boundary;
}
@@ -458,7 +458,7 @@ void body::generateImpl(utility::outputStream& os, const string::size_type maxLi
{
text epilog(epilogText, vmime::charset("us-ascii"));
- epilog.encodeAndFold(os, maxLineLength, 0,
+ epilog.encodeAndFold(ctx, os, 0,
NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);
os << CRLF;
@@ -471,7 +471,7 @@ void body::generateImpl(utility::outputStream& os, const string::size_type maxLi
else
{
// Generate the contents
- m_contents->generate(os, getEncoding(), maxLineLength);
+ m_contents->generate(os, getEncoding(), ctx.getMaxLineLength());
}
}
diff --git a/src/bodyPart.cpp b/src/bodyPart.cpp
index fbe9f1ed..32544ba8 100644
--- a/src/bodyPart.cpp
+++ b/src/bodyPart.cpp
@@ -47,17 +47,18 @@ bodyPart::bodyPart(weak_ref <vmime::bodyPart> parentPart)
void bodyPart::parseImpl
- (ref <utility::parserInputStreamAdapter> parser,
+ (const parsingContext& ctx,
+ ref <utility::parserInputStreamAdapter> parser,
const utility::stream::size_type position,
const utility::stream::size_type end,
utility::stream::size_type* newPosition)
{
// Parse the headers
string::size_type pos = position;
- m_header->parse(parser, pos, end, &pos);
+ m_header->parse(ctx, parser, pos, end, &pos);
// Parse the body contents
- m_body->parse(parser, pos, end, NULL);
+ m_body->parse(ctx, parser, pos, end, NULL);
setParsedBounds(position, end);
@@ -66,14 +67,15 @@ void bodyPart::parseImpl
}
-void bodyPart::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type /* curLinePos */, string::size_type* newLinePos) const
+void bodyPart::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type /* curLinePos */, string::size_type* newLinePos) const
{
- m_header->generate(os, maxLineLength);
+ m_header->generate(ctx, os);
os << CRLF;
- m_body->generate(os, maxLineLength);
+ m_body->generate(ctx, os);
if (newLinePos)
*newLinePos = 0;
diff --git a/src/charset.cpp b/src/charset.cpp
index 84368e85..092676b2 100644
--- a/src/charset.cpp
+++ b/src/charset.cpp
@@ -57,8 +57,9 @@ charset::charset(const char* name)
}
-void charset::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void charset::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
m_name = utility::stringUtils::trim
(string(buffer.begin() + position, buffer.begin() + end));
@@ -74,8 +75,9 @@ void charset::parseImpl(const string& buffer, const string::size_type position,
}
-void charset::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void charset::generateImpl
+ (const generationContext& /* ctx */, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
os << m_name;
@@ -85,17 +87,25 @@ void charset::generateImpl(utility::outputStream& os, const string::size_type /*
void charset::convert(utility::inputStream& in, utility::outputStream& out,
- const charset& source, const charset& dest)
+ const charset& source, const charset& dest,
+ const charsetConverterOptions& opts)
{
- charsetConverter conv(source, dest);
- conv.convert(in, out);
+ ref <charsetConverter> conv = charsetConverter::create(source, dest, opts);
+ conv->convert(in, out);
}
-void charset::convert(const string& in, string& out, const charset& source, const charset& dest)
+void charset::convert(const string& in, string& out, const charset& source, const charset& dest,
+ const charsetConverterOptions& opts)
{
- charsetConverter conv(source, dest);
- conv.convert(in, out);
+ if (source == dest)
+ {
+ out = in;
+ return;
+ }
+
+ ref <charsetConverter> conv = charsetConverter::create(source, dest, opts);
+ conv->convert(in, out);
}
diff --git a/src/charsetConverter.cpp b/src/charsetConverter.cpp
index a33f4f84..c2041476 100644
--- a/src/charsetConverter.cpp
+++ b/src/charsetConverter.cpp
@@ -22,398 +22,25 @@
//
#include "vmime/charsetConverter.hpp"
-#include "vmime/exception.hpp"
-#include "vmime/utility/inputStreamStringAdapter.hpp"
-#include "vmime/utility/outputStreamStringAdapter.hpp"
-
-
-extern "C"
-{
-#ifndef VMIME_BUILDING_DOC
-
- #include <iconv.h>
- #include <errno.h>
-
- // HACK: prototypes may differ depending on the compiler and/or system (the
- // second parameter may or may not be 'const'). This relies on the compiler
- // for choosing the right type.
- class ICONV_HACK
- {
- public:
-
- ICONV_HACK(const char** ptr) : m_ptr(ptr) { }
-
- operator const char**() { return m_ptr; }
- operator char**() { return const_cast <char**>(m_ptr); }
-
- private:
-
- const char** m_ptr;
- };
-
-#endif // VMIME_BUILDING_DOC
-}
-
-
-
-// Output replacement char when an invalid sequence is encountered
-template <typename OUTPUT_CLASS, typename ICONV_DESC>
-void outputInvalidChar(OUTPUT_CLASS& out, ICONV_DESC cd)
-{
- const char* invalidCharIn = "?";
- size_t invalidCharInLen = 1;
-
- char invalidCharOutBuffer[16];
- char* invalidCharOutPtr = invalidCharOutBuffer;
- size_t invalidCharOutLen = 16;
-
- if (iconv(cd, ICONV_HACK(&invalidCharIn), &invalidCharInLen,
- &invalidCharOutPtr, &invalidCharOutLen) != static_cast <size_t>(-1))
- {
- out.write(invalidCharOutBuffer, 16 - invalidCharOutLen);
- }
-}
+#include "vmime/charsetConverter_iconv.hpp"
+#include "vmime/charsetConverter_idna.hpp"
namespace vmime
{
-charsetConverter::charsetConverter(const charset& source, const charset& dest)
- : m_desc(NULL), m_source(source), m_dest(dest)
-{
- // Get an iconv descriptor
- const iconv_t cd = iconv_open(dest.getName().c_str(), source.getName().c_str());
-
- if (cd != reinterpret_cast <iconv_t>(-1))
- {
- iconv_t* p = new iconv_t;
- *p= cd;
-
- m_desc = p;
- }
-}
-
-
-charsetConverter::~charsetConverter()
-{
- if (m_desc != NULL)
- {
- // Close iconv handle
- iconv_close(*static_cast <iconv_t*>(m_desc));
-
- delete static_cast <iconv_t*>(m_desc);
- m_desc = NULL;
- }
-}
-
-
-void charsetConverter::convert(utility::inputStream& in, utility::outputStream& out)
-{
- if (m_desc == NULL)
- throw exceptions::charset_conv_error("Cannot initialize converter.");
-
- const iconv_t cd = *static_cast <iconv_t*>(m_desc);
-
- char inBuffer[32768];
- char outBuffer[32768];
- size_t inPos = 0;
-
- bool prevIsInvalid = false;
- bool breakAfterNext = false;
-
- while (true)
- {
- // Fullfill the buffer
- size_t inLength = static_cast <size_t>(in.read(inBuffer + inPos, sizeof(inBuffer) - inPos) + inPos);
- size_t outLength = sizeof(outBuffer);
-
- const char* inPtr = breakAfterNext ? NULL : inBuffer;
- size_t *ptrLength = breakAfterNext ? NULL : &inLength;
- char* outPtr = outBuffer;
-
- // Convert input bytes
- if (iconv(cd, ICONV_HACK(&inPtr), ptrLength,
- &outPtr, &outLength) == static_cast <size_t>(-1))
- {
- // Illegal input sequence or input sequence has no equivalent
- // sequence in the destination charset.
- if (prevIsInvalid)
- {
- // Write successfully converted bytes
- out.write(outBuffer, sizeof(outBuffer) - outLength);
-
- // Output a special character to indicate we don't known how to
- // convert the sequence at this position
- outputInvalidChar(out, cd);
-
- // Skip a byte and leave unconverted bytes in the input buffer
- std::copy(const_cast <char*>(inPtr + 1), inBuffer + sizeof(inBuffer), inBuffer);
- inPos = inLength - 1;
- }
- else
- {
- // Write successfully converted bytes
- out.write(outBuffer, sizeof(outBuffer) - outLength);
-
- // Leave unconverted bytes in the input buffer
- std::copy(const_cast <char*>(inPtr), inBuffer + sizeof(inBuffer), inBuffer);
- inPos = inLength;
-
- if (errno != E2BIG)
- prevIsInvalid = true;
- }
- }
- else
- {
- // Write successfully converted bytes
- out.write(outBuffer, sizeof(outBuffer) - outLength);
-
- inPos = 0;
- prevIsInvalid = false;
- }
-
- if (breakAfterNext)
- break;
-
- // Check for end of data, loop again to flush stateful data from iconv
- if (in.eof() && inPos == 0)
- breakAfterNext = true;
- }
-}
-
-
-void charsetConverter::convert(const string& in, string& out)
+// static
+ref <charsetConverter> charsetConverter::create
+ (const charset& source, const charset& dest,
+ const charsetConverterOptions& opts)
{
- out.clear();
-
- utility::inputStreamStringAdapter is(in);
- utility::outputStreamStringAdapter os(out);
-
- convert(is, os);
-
- os.flush();
+ if (source == "idna" || dest == "idna")
+ return vmime::create <charsetConverter_idna>(source, dest, opts);
+ else
+ return vmime::create <charsetConverter_iconv>(source, dest, opts);
}
-
-// charsetFilteredOutputStream
-
-namespace utility {
-
-
-charsetFilteredOutputStream::charsetFilteredOutputStream
- (const charset& source, const charset& dest, outputStream& os)
- : m_desc(NULL), m_sourceCharset(source), m_destCharset(dest),
- m_stream(os), m_unconvCount(0)
-{
- // Get an iconv descriptor
- const iconv_t cd = iconv_open(dest.getName().c_str(), source.getName().c_str());
-
- if (cd != reinterpret_cast <iconv_t>(-1))
- {
- iconv_t* p = new iconv_t;
- *p= cd;
-
- m_desc = p;
- }
-}
-
-
-charsetFilteredOutputStream::~charsetFilteredOutputStream()
-{
- if (m_desc != NULL)
- {
- // Close iconv handle
- iconv_close(*static_cast <iconv_t*>(m_desc));
-
- delete static_cast <iconv_t*>(m_desc);
- m_desc = NULL;
- }
-}
-
-
-outputStream& charsetFilteredOutputStream::getNextOutputStream()
-{
- return m_stream;
-}
-
-
-void charsetFilteredOutputStream::write
- (const value_type* const data, const size_type count)
-{
- if (m_desc == NULL)
- throw exceptions::charset_conv_error("Cannot initialize converter.");
-
- const iconv_t cd = *static_cast <iconv_t*>(m_desc);
-
- const value_type* curData = data;
- size_type curDataLen = count;
-
- // If there is some unconverted bytes left, add more data from this
- // chunk to see if it can now be converted.
- while (m_unconvCount != 0 || curDataLen != 0)
- {
- if (m_unconvCount != 0)
- {
- // Check if an incomplete input sequence is larger than the
- // input buffer size: should not happen except if something
- // in the input sequence is invalid. If so, output a special
- // character and skip one byte in the invalid sequence.
- if (m_unconvCount >= sizeof(m_unconvBuffer))
- {
- outputInvalidChar(m_stream, cd);
-
- std::copy(m_unconvBuffer + 1,
- m_unconvBuffer + m_unconvCount, m_unconvBuffer);
-
- m_unconvCount--;
- }
-
- // Get more data
- const size_type remaining =
- std::min(curDataLen, sizeof(m_unconvBuffer) - m_unconvCount);
-
- std::copy(curData, curData + remaining, m_unconvBuffer + m_unconvCount);
-
- m_unconvCount += remaining;
- curDataLen -= remaining;
- curData += remaining;
-
- if (remaining == 0)
- return; // no more data
-
- // Try a conversion
- const char* inPtr = m_unconvBuffer;
- size_t inLength = m_unconvCount;
- char* outPtr = m_outputBuffer;
- size_t outLength = sizeof(m_outputBuffer);
-
- const size_t inLength0 = inLength;
-
- if (iconv(cd, ICONV_HACK(&inPtr), &inLength, &outPtr, &outLength) == static_cast <size_t>(-1))
- {
- const size_t inputConverted = inLength0 - inLength;
-
- // Write successfully converted bytes
- m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
-
- // Shift unconverted bytes
- std::copy(m_unconvBuffer + inputConverted,
- m_unconvBuffer + m_unconvCount, m_unconvBuffer);
-
- m_unconvCount -= inputConverted;
-
- continue;
- }
-
- // Write successfully converted bytes
- m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
-
- // Empty the unconverted buffer
- m_unconvCount = 0;
- }
-
- if (curDataLen == 0)
- return; // no more data
-
- // Now, convert the current data buffer
- const char* inPtr = curData;
- size_t inLength = std::min(curDataLen, sizeof(m_outputBuffer) / MAX_CHARACTER_WIDTH);
- char* outPtr = m_outputBuffer;
- size_t outLength = sizeof(m_outputBuffer);
-
- const size_t inLength0 = inLength;
-
- if (iconv(cd, ICONV_HACK(&inPtr), &inLength, &outPtr, &outLength) == static_cast <size_t>(-1))
- {
- // Write successfully converted bytes
- m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
-
- const size_t inputConverted = inLength0 - inLength;
-
- curData += inputConverted;
- curDataLen -= inputConverted;
-
- // Put one byte byte into the unconverted buffer so
- // that the next iteration fill it
- if (curDataLen != 0)
- {
- m_unconvCount = 1;
- m_unconvBuffer[0] = *curData;
-
- curData++;
- curDataLen--;
- }
- }
- else
- {
- // Write successfully converted bytes
- m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
-
- curData += inLength0;
- curDataLen -= inLength0;
- }
- }
-}
-
-
-void charsetFilteredOutputStream::flush()
-{
- if (m_desc == NULL)
- throw exceptions::charset_conv_error("Cannot initialize converter.");
-
- const iconv_t cd = *static_cast <iconv_t*>(m_desc);
-
- size_t offset = 0;
-
- // Process unconverted bytes
- while (m_unconvCount != 0)
- {
- // Try a conversion
- const char* inPtr = m_unconvBuffer + offset;
- size_t inLength = m_unconvCount;
- char* outPtr = m_outputBuffer;
- size_t outLength = sizeof(m_outputBuffer);
-
- const size_t inLength0 = inLength;
-
- if (iconv(cd, ICONV_HACK(&inPtr), &inLength, &outPtr, &outLength) == static_cast <size_t>(-1))
- {
- const size_t inputConverted = inLength0 - inLength;
-
- // Skip a "blocking" character
- if (inputConverted == 0)
- {
- outputInvalidChar(m_stream, cd);
-
- offset++;
- m_unconvCount--;
- }
- else
- {
- // Write successfully converted bytes
- m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
-
- offset += inputConverted;
- m_unconvCount -= inputConverted;
- }
- }
- else
- {
- // Write successfully converted bytes
- m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
-
- m_unconvCount = 0;
- }
- }
-
- m_stream.flush();
-}
-
-
-} // utility
-
-
} // vmime
diff --git a/src/charsetConverterOptions.cpp b/src/charsetConverterOptions.cpp
new file mode 100644
index 00000000..caeacd01
--- /dev/null
+++ b/src/charsetConverterOptions.cpp
@@ -0,0 +1,37 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 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 3 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.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/charsetConverterOptions.hpp"
+
+
+namespace vmime
+{
+
+
+charsetConverterOptions::charsetConverterOptions()
+ : invalidSequence("?")
+{
+}
+
+
+} // vmime
diff --git a/src/charsetConverter_iconv.cpp b/src/charsetConverter_iconv.cpp
new file mode 100644
index 00000000..c5d3557e
--- /dev/null
+++ b/src/charsetConverter_iconv.cpp
@@ -0,0 +1,435 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 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 3 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.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/charsetConverter_iconv.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/utility/inputStreamStringAdapter.hpp"
+#include "vmime/utility/outputStreamStringAdapter.hpp"
+
+
+extern "C"
+{
+#ifndef VMIME_BUILDING_DOC
+
+ #include <iconv.h>
+ #include <errno.h>
+
+ // HACK: prototypes may differ depending on the compiler and/or system (the
+ // second parameter may or may not be 'const'). This relies on the compiler
+ // for choosing the right type.
+ class ICONV_HACK
+ {
+ public:
+
+ ICONV_HACK(const char** ptr) : m_ptr(ptr) { }
+
+ operator const char**() { return m_ptr; }
+ operator char**() { return const_cast <char**>(m_ptr); }
+
+ private:
+
+ const char** m_ptr;
+ };
+
+#endif // VMIME_BUILDING_DOC
+}
+
+
+
+// Output replacement char when an invalid sequence is encountered
+template <typename OUTPUT_CLASS, typename ICONV_DESC>
+void outputInvalidChar(OUTPUT_CLASS& out, ICONV_DESC cd,
+ const vmime::charsetConverterOptions& opts = vmime::charsetConverterOptions())
+{
+ const char* invalidCharIn = opts.invalidSequence.c_str();
+ size_t invalidCharInLen = opts.invalidSequence.length();
+
+ char invalidCharOutBuffer[16];
+ char* invalidCharOutPtr = invalidCharOutBuffer;
+ size_t invalidCharOutLen = 16;
+
+ if (iconv(cd, ICONV_HACK(&invalidCharIn), &invalidCharInLen,
+ &invalidCharOutPtr, &invalidCharOutLen) != static_cast <size_t>(-1))
+ {
+ out.write(invalidCharOutBuffer, 16 - invalidCharOutLen);
+ }
+}
+
+
+
+namespace vmime
+{
+
+
+charsetConverter_iconv::charsetConverter_iconv
+ (const charset& source, const charset& dest, const charsetConverterOptions& opts)
+ : m_desc(NULL), m_source(source), m_dest(dest), m_options(opts)
+{
+ // Get an iconv descriptor
+ const iconv_t cd = iconv_open(dest.getName().c_str(), source.getName().c_str());
+
+ if (cd != reinterpret_cast <iconv_t>(-1))
+ {
+ iconv_t* p = new iconv_t;
+ *p= cd;
+
+ m_desc = p;
+ }
+}
+
+
+charsetConverter_iconv::~charsetConverter_iconv()
+{
+ if (m_desc != NULL)
+ {
+ // Close iconv handle
+ iconv_close(*static_cast <iconv_t*>(m_desc));
+
+ delete static_cast <iconv_t*>(m_desc);
+ m_desc = NULL;
+ }
+}
+
+
+void charsetConverter_iconv::convert(utility::inputStream& in, utility::outputStream& out)
+{
+ if (m_desc == NULL)
+ throw exceptions::charset_conv_error("Cannot initialize converter.");
+
+ const iconv_t cd = *static_cast <iconv_t*>(m_desc);
+
+ char inBuffer[32768];
+ char outBuffer[32768];
+ size_t inPos = 0;
+
+ bool prevIsInvalid = false;
+ bool breakAfterNext = false;
+
+ while (true)
+ {
+ // Fullfill the buffer
+ size_t inLength = static_cast <size_t>(in.read(inBuffer + inPos, sizeof(inBuffer) - inPos) + inPos);
+ size_t outLength = sizeof(outBuffer);
+
+ const char* inPtr = breakAfterNext ? NULL : inBuffer;
+ size_t *ptrLength = breakAfterNext ? NULL : &inLength;
+ char* outPtr = outBuffer;
+
+ // Convert input bytes
+ if (iconv(cd, ICONV_HACK(&inPtr), ptrLength,
+ &outPtr, &outLength) == static_cast <size_t>(-1))
+ {
+ // Illegal input sequence or input sequence has no equivalent
+ // sequence in the destination charset.
+ if (prevIsInvalid)
+ {
+ // Write successfully converted bytes
+ out.write(outBuffer, sizeof(outBuffer) - outLength);
+
+ // Output a special character to indicate we don't known how to
+ // convert the sequence at this position
+ outputInvalidChar(out, cd, m_options);
+
+ // Skip a byte and leave unconverted bytes in the input buffer
+ std::copy(const_cast <char*>(inPtr + 1), inBuffer + sizeof(inBuffer), inBuffer);
+ inPos = inLength - 1;
+ }
+ else
+ {
+ // Write successfully converted bytes
+ out.write(outBuffer, sizeof(outBuffer) - outLength);
+
+ // Leave unconverted bytes in the input buffer
+ std::copy(const_cast <char*>(inPtr), inBuffer + sizeof(inBuffer), inBuffer);
+ inPos = inLength;
+
+ if (errno != E2BIG)
+ prevIsInvalid = true;
+ }
+ }
+ else
+ {
+ // Write successfully converted bytes
+ out.write(outBuffer, sizeof(outBuffer) - outLength);
+
+ inPos = 0;
+ prevIsInvalid = false;
+ }
+
+ if (breakAfterNext)
+ break;
+
+ // Check for end of data, loop again to flush stateful data from iconv
+ if (in.eof() && inPos == 0)
+ breakAfterNext = true;
+ }
+}
+
+
+void charsetConverter_iconv::convert(const string& in, string& out)
+{
+ if (m_source == m_dest)
+ {
+ // No conversion needed
+ out = in;
+ return;
+ }
+
+ out.clear();
+
+ utility::inputStreamStringAdapter is(in);
+ utility::outputStreamStringAdapter os(out);
+
+ convert(is, os);
+
+ os.flush();
+}
+
+
+ref <utility::charsetFilteredOutputStream> charsetConverter_iconv::getFilteredOutputStream(utility::outputStream& os)
+{
+ return vmime::create <utility::charsetFilteredOutputStream_iconv>(m_source, m_dest, &os);
+}
+
+
+
+// charsetFilteredOutputStream_iconv
+
+namespace utility {
+
+
+charsetFilteredOutputStream_iconv::charsetFilteredOutputStream_iconv
+ (const charset& source, const charset& dest, outputStream* os)
+ : m_desc(NULL), m_sourceCharset(source), m_destCharset(dest),
+ m_stream(*os), m_unconvCount(0)
+{
+ // Get an iconv descriptor
+ const iconv_t cd = iconv_open(dest.getName().c_str(), source.getName().c_str());
+
+ if (cd != reinterpret_cast <iconv_t>(-1))
+ {
+ iconv_t* p = new iconv_t;
+ *p= cd;
+
+ m_desc = p;
+ }
+}
+
+
+charsetFilteredOutputStream_iconv::~charsetFilteredOutputStream_iconv()
+{
+ if (m_desc != NULL)
+ {
+ // Close iconv handle
+ iconv_close(*static_cast <iconv_t*>(m_desc));
+
+ delete static_cast <iconv_t*>(m_desc);
+ m_desc = NULL;
+ }
+}
+
+
+outputStream& charsetFilteredOutputStream_iconv::getNextOutputStream()
+{
+ return m_stream;
+}
+
+
+void charsetFilteredOutputStream_iconv::write
+ (const value_type* const data, const size_type count)
+{
+ if (m_desc == NULL)
+ throw exceptions::charset_conv_error("Cannot initialize converter.");
+
+ const iconv_t cd = *static_cast <iconv_t*>(m_desc);
+
+ const value_type* curData = data;
+ size_type curDataLen = count;
+
+ // If there is some unconverted bytes left, add more data from this
+ // chunk to see if it can now be converted.
+ while (m_unconvCount != 0 || curDataLen != 0)
+ {
+ if (m_unconvCount != 0)
+ {
+ // Check if an incomplete input sequence is larger than the
+ // input buffer size: should not happen except if something
+ // in the input sequence is invalid. If so, output a special
+ // character and skip one byte in the invalid sequence.
+ if (m_unconvCount >= sizeof(m_unconvBuffer))
+ {
+ outputInvalidChar(m_stream, cd);
+
+ std::copy(m_unconvBuffer + 1,
+ m_unconvBuffer + m_unconvCount, m_unconvBuffer);
+
+ m_unconvCount--;
+ }
+
+ // Get more data
+ const size_type remaining =
+ std::min(curDataLen, sizeof(m_unconvBuffer) - m_unconvCount);
+
+ std::copy(curData, curData + remaining, m_unconvBuffer + m_unconvCount);
+
+ m_unconvCount += remaining;
+ curDataLen -= remaining;
+ curData += remaining;
+
+ if (remaining == 0)
+ return; // no more data
+
+ // Try a conversion
+ const char* inPtr = m_unconvBuffer;
+ size_t inLength = m_unconvCount;
+ char* outPtr = m_outputBuffer;
+ size_t outLength = sizeof(m_outputBuffer);
+
+ const size_t inLength0 = inLength;
+
+ if (iconv(cd, ICONV_HACK(&inPtr), &inLength, &outPtr, &outLength) == static_cast <size_t>(-1))
+ {
+ const size_t inputConverted = inLength0 - inLength;
+
+ // Write successfully converted bytes
+ m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
+
+ // Shift unconverted bytes
+ std::copy(m_unconvBuffer + inputConverted,
+ m_unconvBuffer + m_unconvCount, m_unconvBuffer);
+
+ m_unconvCount -= inputConverted;
+
+ continue;
+ }
+
+ // Write successfully converted bytes
+ m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
+
+ // Empty the unconverted buffer
+ m_unconvCount = 0;
+ }
+
+ if (curDataLen == 0)
+ return; // no more data
+
+ // Now, convert the current data buffer
+ const char* inPtr = curData;
+ size_t inLength = std::min(curDataLen, sizeof(m_outputBuffer) / MAX_CHARACTER_WIDTH);
+ char* outPtr = m_outputBuffer;
+ size_t outLength = sizeof(m_outputBuffer);
+
+ const size_t inLength0 = inLength;
+
+ if (iconv(cd, ICONV_HACK(&inPtr), &inLength, &outPtr, &outLength) == static_cast <size_t>(-1))
+ {
+ // Write successfully converted bytes
+ m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
+
+ const size_t inputConverted = inLength0 - inLength;
+
+ curData += inputConverted;
+ curDataLen -= inputConverted;
+
+ // Put one byte byte into the unconverted buffer so
+ // that the next iteration fill it
+ if (curDataLen != 0)
+ {
+ m_unconvCount = 1;
+ m_unconvBuffer[0] = *curData;
+
+ curData++;
+ curDataLen--;
+ }
+ }
+ else
+ {
+ // Write successfully converted bytes
+ m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
+
+ curData += inLength0;
+ curDataLen -= inLength0;
+ }
+ }
+}
+
+
+void charsetFilteredOutputStream_iconv::flush()
+{
+ if (m_desc == NULL)
+ throw exceptions::charset_conv_error("Cannot initialize converter.");
+
+ const iconv_t cd = *static_cast <iconv_t*>(m_desc);
+
+ size_t offset = 0;
+
+ // Process unconverted bytes
+ while (m_unconvCount != 0)
+ {
+ // Try a conversion
+ const char* inPtr = m_unconvBuffer + offset;
+ size_t inLength = m_unconvCount;
+ char* outPtr = m_outputBuffer;
+ size_t outLength = sizeof(m_outputBuffer);
+
+ const size_t inLength0 = inLength;
+
+ if (iconv(cd, ICONV_HACK(&inPtr), &inLength, &outPtr, &outLength) == static_cast <size_t>(-1))
+ {
+ const size_t inputConverted = inLength0 - inLength;
+
+ // Skip a "blocking" character
+ if (inputConverted == 0)
+ {
+ outputInvalidChar(m_stream, cd);
+
+ offset++;
+ m_unconvCount--;
+ }
+ else
+ {
+ // Write successfully converted bytes
+ m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
+
+ offset += inputConverted;
+ m_unconvCount -= inputConverted;
+ }
+ }
+ else
+ {
+ // Write successfully converted bytes
+ m_stream.write(m_outputBuffer, sizeof(m_outputBuffer) - outLength);
+
+ m_unconvCount = 0;
+ }
+ }
+
+ m_stream.flush();
+}
+
+
+} // utility
+
+
+} // vmime
diff --git a/src/charsetConverter_idna.cpp b/src/charsetConverter_idna.cpp
new file mode 100644
index 00000000..cde2209a
--- /dev/null
+++ b/src/charsetConverter_idna.cpp
@@ -0,0 +1,168 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 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 3 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.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/charsetConverter_idna.hpp"
+
+#include "vmime/exception.hpp"
+
+#include "vmime/utility/stringUtils.hpp"
+#include "vmime/utility/streamUtils.hpp"
+#include "vmime/utility/outputStreamStringAdapter.hpp"
+
+
+extern "C"
+{
+
+#include "contrib/punycode/punycode.h"
+#include "contrib/punycode/punycode.c"
+
+}
+
+#include "contrib/utf8/utf8.h"
+
+
+namespace vmime
+{
+
+
+charsetConverter_idna::charsetConverter_idna
+ (const charset& source, const charset& dest, const charsetConverterOptions& opts)
+ : m_source(source), m_dest(dest), m_options(opts)
+{
+}
+
+
+charsetConverter_idna::~charsetConverter_idna()
+{
+}
+
+
+void charsetConverter_idna::convert(utility::inputStream& in, utility::outputStream& out)
+{
+ // IDNA should be used for short strings, so it does not matter if we
+ // do not work directly on the stream
+ string inStr;
+ vmime::utility::outputStreamStringAdapter os(inStr);
+ vmime::utility::bufferedStreamCopy(in, os);
+
+ string outStr;
+ convert(inStr, outStr);
+
+ out << outStr;
+}
+
+
+void charsetConverter_idna::convert(const string& in, string& out)
+{
+ if (m_source == m_dest)
+ {
+ // No conversion needed
+ out = in;
+ return;
+ }
+
+ out.clear();
+
+ if (m_dest == "idna")
+ {
+ if (utility::stringUtils::is7bit(in))
+ {
+ // No need to encode as Punycode
+ out = in;
+ return;
+ }
+
+ string inUTF8;
+ charset::convert(in, inUTF8, m_source, vmime::charsets::UTF_8);
+
+ const string::value_type* ch = inUTF8.c_str();
+ const string::value_type* end = inUTF8.c_str() + inUTF8.length();
+
+ std::vector <punycode_uint> unichars;
+ unichars.reserve(inUTF8.length());
+
+ while (ch < end)
+ {
+ const utf8::uint32_t uc = utf8::unchecked::next(ch);
+ unichars.push_back(uc);
+ }
+
+ std::vector <char> output(inUTF8.length() * 2);
+ punycode_uint outputLen = output.size();
+
+ const punycode_status status = punycode_encode
+ (unichars.size(), &unichars[0], /* case_flags */ NULL, &outputLen, &output[0]);
+
+ if (status == punycode_success)
+ {
+ out = string("xn--") + string(output.begin(), output.begin() + outputLen);
+ }
+ else
+ {
+ // TODO
+ }
+ }
+ else if (m_source == "idna")
+ {
+ if (in.length() < 5 || in.substr(0, 4) != "xn--")
+ {
+ // Not an IDNA string
+ out = in;
+ return;
+ }
+
+ std::vector <punycode_uint> output(in.length() - 4);
+ punycode_uint outputLen = output.size();
+
+ const punycode_status status = punycode_decode
+ (in.length() - 4, &in[4], &outputLen, &output[0], /* case_flags */ NULL);
+
+ if (status == punycode_success)
+ {
+ std::vector <string::value_type> outUTF8Bytes(outputLen * 4);
+ string::value_type* p = &outUTF8Bytes[0];
+
+ for (std::vector <punycode_uint>::const_iterator it = output.begin() ;
+ it != output.begin() + outputLen ; ++it)
+ {
+ p = utf8::unchecked::append(*it, p);
+ }
+
+ string outUTF8(&outUTF8Bytes[0], p);
+ charset::convert(outUTF8, out, vmime::charsets::UTF_8, m_dest);
+ }
+ else
+ {
+ // TODO
+ }
+ }
+}
+
+
+ref <utility::charsetFilteredOutputStream> charsetConverter_idna::getFilteredOutputStream(utility::outputStream& /* os */)
+{
+ return NULL;
+}
+
+
+} // vmime
diff --git a/src/component.cpp b/src/component.cpp
index f2d34093..b102b45d 100644
--- a/src/component.cpp
+++ b/src/component.cpp
@@ -57,6 +57,15 @@ void component::parse
(ref <utility::inputStream> inputStream, const utility::stream::size_type position,
const utility::stream::size_type end, utility::stream::size_type* newPosition)
{
+ parse(parsingContext::getDefaultContext(), inputStream, position, end, newPosition);
+}
+
+
+void component::parse
+ (const parsingContext& ctx,
+ ref <utility::inputStream> inputStream, const utility::stream::size_type position,
+ const utility::stream::size_type end, utility::stream::size_type* newPosition)
+{
m_parsedOffset = m_parsedLength = 0;
ref <utility::seekableInputStream> seekableStream =
@@ -71,14 +80,14 @@ void component::parse
utility::bufferedStreamCopyRange(*inputStream, ossAdapter, position, end - position);
const string buffer = oss.str();
- parseImpl(buffer, 0, buffer.length(), NULL);
+ parseImpl(ctx, buffer, 0, buffer.length(), NULL);
}
else
{
ref <utility::parserInputStreamAdapter> parser =
vmime::create <utility::parserInputStreamAdapter>(seekableStream);
- parseImpl(parser, position, end, newPosition);
+ parseImpl(ctx, parser, position, end, newPosition);
}
}
@@ -87,7 +96,15 @@ void component::parse(const string& buffer)
{
m_parsedOffset = m_parsedLength = 0;
- parseImpl(buffer, 0, buffer.length(), NULL);
+ parseImpl(parsingContext::getDefaultContext(), buffer, 0, buffer.length(), NULL);
+}
+
+
+void component::parse(const parsingContext& ctx, const string& buffer)
+{
+ m_parsedOffset = m_parsedLength = 0;
+
+ parseImpl(ctx, buffer, 0, buffer.length(), NULL);
}
@@ -97,7 +114,18 @@ void component::parse
{
m_parsedOffset = m_parsedLength = 0;
- parseImpl(buffer, position, end, newPosition);
+ parseImpl(parsingContext::getDefaultContext(), buffer, position, end, newPosition);
+}
+
+
+void component::parse
+ (const parsingContext& ctx,
+ const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
+{
+ m_parsedOffset = m_parsedLength = 0;
+
+ parseImpl(ctx, buffer, position, end, newPosition);
}
@@ -116,11 +144,14 @@ void component::offsetParsedBounds(const utility::stream::size_type offset)
void component::parseImpl
- (ref <utility::parserInputStreamAdapter> parser, const utility::stream::size_type position,
+ (const parsingContext& ctx, ref <utility::parserInputStreamAdapter> parser,
+ const utility::stream::size_type position,
const utility::stream::size_type end, utility::stream::size_type* newPosition)
{
+ // This is the default implementation for parsing from an input stream:
+ // actually, we extract the substring and use the "parse from string" implementation
const std::string buffer = parser->extract(position, end);
- parseImpl(buffer, 0, buffer.length(), newPosition);
+ parseImpl(ctx, buffer, 0, buffer.length(), newPosition);
// Recursivey offset parsed bounds on children
if (position != 0)
@@ -132,16 +163,19 @@ void component::parseImpl
void component::parseImpl
- (const string& buffer, const string::size_type position,
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
const string::size_type end, string::size_type* newPosition)
{
+ // This is the default implementation for parsing from a string:
+ // actually, we encapsulate the string buffer in an input stream, then use
+ // the "parse from input stream" implementation
ref <utility::seekableInputStream> stream =
vmime::create <utility::inputStreamStringAdapter>(buffer);
ref <utility::parserInputStreamAdapter> parser =
vmime::create <utility::parserInputStreamAdapter>(stream);
- parseImpl(parser, position, end, newPosition);
+ parseImpl(ctx, parser, position, end, newPosition);
}
@@ -151,7 +185,10 @@ const string component::generate(const string::size_type maxLineLength,
std::ostringstream oss;
utility::outputStreamAdapter adapter(oss);
- generate(adapter, maxLineLength, curLinePos, NULL);
+ generationContext ctx(generationContext::getDefaultContext());
+ ctx.setMaxLineLength(maxLineLength);
+
+ generateImpl(ctx, adapter, curLinePos, NULL);
return (oss.str());
}
@@ -159,21 +196,21 @@ const string component::generate(const string::size_type maxLineLength,
void component::generate
(utility::outputStream& os,
- const string::size_type maxLineLength,
const string::size_type curLinePos,
string::size_type* newLinePos) const
{
- generateImpl(os, maxLineLength, curLinePos, newLinePos);
+ generateImpl(generationContext::getDefaultContext(),
+ os, curLinePos, newLinePos);
}
void component::generate
- (ref <utility::outputStream> os,
- const string::size_type maxLineLength,
+ (const generationContext& ctx,
+ utility::outputStream& outputStream,
const string::size_type curLinePos,
string::size_type* newLinePos) const
{
- generateImpl(*os, maxLineLength, curLinePos, newLinePos);
+ generateImpl(ctx, outputStream, curLinePos, newLinePos);
}
diff --git a/src/constants.cpp b/src/constants.cpp
index 9ce7189b..551d0a18 100644
--- a/src/constants.cpp
+++ b/src/constants.cpp
@@ -153,6 +153,8 @@ namespace charsets
const string::value_type* const WINDOWS_1256 = "windows-1256";
const string::value_type* const WINDOWS_1257 = "windows-1257";
const string::value_type* const WINDOWS_1258 = "windows-1258";
+
+ const string::value_type* const IDNA = "idna";
}
diff --git a/src/contentDisposition.cpp b/src/contentDisposition.cpp
index 401e9958..300d4ee3 100644
--- a/src/contentDisposition.cpp
+++ b/src/contentDisposition.cpp
@@ -47,8 +47,9 @@ contentDisposition::contentDisposition(const contentDisposition& type)
}
-void contentDisposition::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void contentDisposition::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
m_name = utility::stringUtils::trim(utility::stringUtils::toLower
(string(buffer.begin() + position, buffer.begin() + end)));
@@ -60,8 +61,9 @@ void contentDisposition::parseImpl(const string& buffer, const string::size_type
}
-void contentDisposition::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void contentDisposition::generateImpl
+ (const generationContext& /* ctx */, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
os << m_name;
diff --git a/src/context.cpp b/src/context.cpp
new file mode 100644
index 00000000..07fe4875
--- /dev/null
+++ b/src/context.cpp
@@ -0,0 +1,87 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 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 3 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.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/context.hpp"
+
+
+namespace vmime
+{
+
+
+context::context()
+ : m_internationalizedEmail(false)
+{
+}
+
+
+context::context(const context& ctx)
+ : object(),
+ m_internationalizedEmail(ctx.m_internationalizedEmail)
+{
+}
+
+
+context::~context()
+{
+}
+
+
+bool context::getInternationalizedEmailSupport() const
+{
+ return m_internationalizedEmail;
+}
+
+
+void context::setInternationalizedEmailSupport(const bool support)
+{
+ m_internationalizedEmail = support;
+}
+
+
+const charsetConverterOptions& context::getCharsetConversionOptions() const
+{
+ return m_charsetConvOptions;
+}
+
+
+void context::setCharsetConversionOptions(const charsetConverterOptions& opts)
+{
+ m_charsetConvOptions = opts;
+}
+
+
+context& context::operator=(const context& ctx)
+{
+ copyFrom(ctx);
+ return *this;
+}
+
+
+void context::copyFrom(const context& ctx)
+{
+ m_internationalizedEmail = ctx.m_internationalizedEmail;
+ m_charsetConvOptions = ctx.m_charsetConvOptions;
+}
+
+
+} // vmime
diff --git a/src/dateTime.cpp b/src/dateTime.cpp
index f98d7c64..eaf955c3 100644
--- a/src/dateTime.cpp
+++ b/src/dateTime.cpp
@@ -68,8 +68,9 @@ zone = "UT" / "GMT" ; Universal Time
*/
-void datetime::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void datetime::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
const string::value_type* const pend = buffer.data() + end;
const string::value_type* p = buffer.data() + position;
@@ -589,8 +590,9 @@ void datetime::parseImpl(const string& buffer, const string::size_type position,
}
-void datetime::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void datetime::generateImpl
+ (const generationContext& /* ctx */, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
static const string::value_type* dayNames[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
diff --git a/src/disposition.cpp b/src/disposition.cpp
index 7a31ed8c..c5da6e30 100644
--- a/src/disposition.cpp
+++ b/src/disposition.cpp
@@ -171,8 +171,9 @@ const std::vector <string> disposition::getModifierList() const
}
-void disposition::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void disposition::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
// disposition-mode ";" disposition-type
// [ "/" disposition-modifier *( "," disposition-modifier ) ]
@@ -276,8 +277,9 @@ void disposition::parseImpl(const string& buffer, const string::size_type positi
}
-void disposition::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void disposition::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
string::size_type pos = curLinePos;
@@ -287,7 +289,7 @@ void disposition::generateImpl(utility::outputStream& os, const string::size_typ
os << actionMode << "/" << sendingMode << ";";
pos += actionMode.length() + 1 + sendingMode.length() + 1;
- if (pos > maxLineLength)
+ if (pos > ctx.getMaxLineLength())
{
os << NEW_LINE_SEQUENCE;
pos = NEW_LINE_SEQUENCE_LENGTH;
diff --git a/src/emailAddress.cpp b/src/emailAddress.cpp
new file mode 100644
index 00000000..09d08780
--- /dev/null
+++ b/src/emailAddress.cpp
@@ -0,0 +1,513 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 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 3 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.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/emailAddress.hpp"
+
+#include "vmime/platform.hpp"
+
+#include "vmime/parserHelpers.hpp"
+#include "vmime/utility/outputStreamStringAdapter.hpp"
+#include "vmime/utility/stringUtils.hpp"
+
+
+namespace vmime
+{
+
+
+emailAddress::emailAddress()
+{
+}
+
+
+emailAddress::emailAddress(const emailAddress& eml)
+ : component(), m_localName(eml.m_localName), m_domainName(eml.m_domainName)
+{
+}
+
+
+emailAddress::emailAddress(const string& email)
+{
+ parse(email);
+}
+
+
+emailAddress::emailAddress(const char* email)
+{
+ parse(email);
+}
+
+
+emailAddress::emailAddress(const string& localName, const string& domainName)
+ : component(), m_localName(word(localName, vmime::charsets::UTF_8)),
+ m_domainName(word(domainName, vmime::charsets::UTF_8))
+{
+}
+
+
+emailAddress::emailAddress(const word& localName, const word& domainName)
+ : component(), m_localName(localName), m_domainName(domainName)
+{
+}
+
+
+void emailAddress::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
+{
+ const string::value_type* const pend = buffer.data() + end;
+ const string::value_type* const pstart = buffer.data() + position;
+ const string::value_type* p = pstart;
+
+ enum ParserStates
+ {
+ State_Before,
+ State_LocalPartStart,
+ State_LocalPartMiddle,
+ State_LocalPartComment,
+ State_LocalPartQuoted,
+ State_DomainPartStart,
+ State_DomainPartMiddle,
+ State_DomainPartComment,
+ State_End,
+ State_Error
+ } state = State_Before;
+
+ std::ostringstream localPart;
+ std::ostringstream domainPart;
+
+ bool escapeNext = false; // for quoting
+ bool prevIsDot = false;
+ bool atFound = false;
+ bool stop = false;
+ int commentLevel = 0;
+
+ while (p < pend && !stop)
+ {
+ const string::value_type c = *p;
+
+ if ((localPart.str().length() + domainPart.str().length()) >= 256)
+ {
+ state = State_Error;
+ break;
+ }
+
+ switch (state)
+ {
+ case State_Before:
+
+ if (parserHelpers::isSpace(c))
+ ++p;
+ else
+ state = State_LocalPartStart;
+
+ case State_LocalPartStart:
+
+ if (c == '"')
+ {
+ state = State_LocalPartQuoted;
+ ++p;
+ }
+ else if (c == '(')
+ {
+ state = State_LocalPartComment;
+ ++commentLevel;
+ ++p;
+ }
+ else
+ {
+ state = State_LocalPartMiddle;
+ localPart << c;
+ ++p;
+ }
+
+ break;
+
+ case State_LocalPartComment:
+
+ if (escapeNext)
+ {
+ escapeNext = false;
+ ++p;
+ }
+ else if (c == '\\')
+ {
+ escapeNext = true;
+ ++p;
+ }
+ else if (c == '(')
+ {
+ ++commentLevel;
+ ++p;
+ }
+ else if (c == ')')
+ {
+ if (--commentLevel == 0)
+ {
+ // End of comment
+ state = State_LocalPartMiddle;
+ }
+
+ ++p;
+ }
+ else
+ {
+ // Comment continues
+ ++p;
+ }
+
+ break;
+
+ case State_LocalPartQuoted:
+
+ if (escapeNext)
+ {
+ escapeNext = false;
+
+ if (c == '"' || c == '\\')
+ {
+ localPart << c;
+ ++p;
+ }
+ else
+ {
+ // This char cannot be escaped
+ state = State_Error;
+ }
+ }
+ else if (c == '"')
+ {
+ // End of quoted string
+ state = State_LocalPartMiddle;
+ ++p;
+ }
+ else if (c == '\\')
+ {
+ escapeNext = true;
+ ++p;
+ }
+ else
+ {
+ localPart << c;
+ ++p;
+ }
+
+ break;
+
+ case State_LocalPartMiddle:
+
+ if (c == '.')
+ {
+ prevIsDot = true;
+ localPart << c;
+ ++p;
+ }
+ else if (c == '"' && prevIsDot)
+ {
+ prevIsDot = false;
+ state = State_LocalPartQuoted;
+ ++p;
+ }
+ else if (c == '(')
+ {
+ // By allowing comments anywhere in the local part,
+ // we are more permissive than RFC-2822
+ state = State_LocalPartComment;
+ ++commentLevel;
+ ++p;
+ }
+ else if (c == '@')
+ {
+ atFound = true;
+ state = State_DomainPartStart;
+ ++p;
+ }
+ else if (parserHelpers::isSpace(c))
+ {
+ // Allow not specifying domain part
+ state = State_End;
+ }
+ else
+ {
+ prevIsDot = false;
+ localPart << c;
+ ++p;
+ }
+
+ break;
+
+ case State_DomainPartStart:
+
+ if (c == '(')
+ {
+ state = State_DomainPartComment;
+ ++commentLevel;
+ ++p;
+ }
+ else
+ {
+ state = State_DomainPartMiddle;
+ domainPart << c;
+ ++p;
+ }
+
+ break;
+
+ case State_DomainPartMiddle:
+
+ if (parserHelpers::isSpace(c))
+ {
+ state = State_End;
+ }
+ else if (c == '(')
+ {
+ // By allowing comments anywhere in the domain part,
+ // we are more permissive than RFC-2822
+ state = State_DomainPartComment;
+ ++commentLevel;
+ ++p;
+ }
+ else
+ {
+ domainPart << c;
+ ++p;
+ }
+
+ break;
+
+ case State_DomainPartComment:
+
+ if (escapeNext)
+ {
+ escapeNext = false;
+ ++p;
+ }
+ else if (c == '\\')
+ {
+ escapeNext = true;
+ ++p;
+ }
+ else if (c == '(')
+ {
+ ++commentLevel;
+ ++p;
+ }
+ else if (c == ')')
+ {
+ if (--commentLevel == 0)
+ {
+ // End of comment
+ state = State_DomainPartMiddle;
+ }
+
+ ++p;
+ }
+ else
+ {
+ // Comment continues
+ ++p;
+ }
+
+ break;
+
+ case State_End:
+ case State_Error:
+
+ stop = true;
+ break;
+ }
+ }
+
+ if (p == pend && state != State_Error)
+ {
+ if (state == State_DomainPartMiddle)
+ state = State_End;
+ else if (state == State_LocalPartMiddle)
+ state = State_End; // allow not specifying domain part
+ }
+
+ if (state != State_End)
+ {
+ m_localName = word("invalid", vmime::charsets::UTF_8);
+ m_domainName = word("invalid", vmime::charsets::UTF_8);
+ }
+ else
+ {
+ // If the domain part is missing, use local host name
+ if (domainPart.str().empty() && !atFound)
+ domainPart << platform::getHandler()->getHostName();
+
+ m_localName = word(localPart.str(), vmime::charsets::UTF_8);
+ m_domainName = word(domainPart.str(), vmime::charsets::UTF_8);
+ }
+
+ setParsedBounds(position, p - pend);
+
+ if (newPosition)
+ *newPosition = p - pend;
+}
+
+
+static const string domainNameToIDNA(const string& domainName)
+{
+ std::ostringstream idnaDomain;
+ string::size_type p = 0;
+
+ for (string::size_type n = domainName.find('.', p) ;
+ (n = domainName.find('.', p)) != string::npos ; p = n + 1)
+ {
+ string idnaPart;
+ charset::convert(string(domainName.begin() + p, domainName.begin() + n),
+ idnaPart, vmime::charsets::UTF_8, vmime::charsets::IDNA);
+
+ idnaDomain << idnaPart << '.';
+ }
+
+ if (p < domainName.length())
+ {
+ string idnaPart;
+ charset::convert(string(domainName.begin() + p, domainName.end()),
+ idnaPart, vmime::charsets::UTF_8, vmime::charsets::IDNA);
+
+ idnaDomain << idnaPart;
+ }
+
+ return idnaDomain.str();
+}
+
+
+void emailAddress::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
+{
+ string localPart, domainPart;
+
+ if (ctx.getInternationalizedEmailSupport() &&
+ (!utility::stringUtils::is7bit(m_localName.getBuffer()) ||
+ !utility::stringUtils::is7bit(m_domainName.getBuffer())))
+ {
+ // Local part
+ string localPartUTF8(m_localName.getConvertedText(vmime::charsets::UTF_8));
+ word localPartWord(localPartUTF8, vmime::charsets::UTF_8);
+
+ vmime::utility::outputStreamStringAdapter os(localPart);
+ localPartWord.generate(ctx, os, 0, NULL, text::FORCE_NO_ENCODING | text::QUOTE_IF_NEEDED, NULL);
+
+ // Domain part
+ domainPart = m_domainName.getConvertedText(vmime::charsets::UTF_8);
+ }
+ else
+ {
+ // Local part
+ vmime::utility::outputStreamStringAdapter os(localPart);
+ m_localName.generate(ctx, os, 0, NULL, text::QUOTE_IF_NEEDED, NULL);
+
+ // Domain part as IDNA
+ domainPart = domainNameToIDNA(m_domainName.getConvertedText(vmime::charsets::UTF_8));
+ }
+
+ os << localPart
+ << "@"
+ << domainPart;
+
+ if (newLinePos)
+ {
+ *newLinePos = curLinePos
+ + localPart.length()
+ + 1 // @
+ + domainPart.length();
+ }
+}
+
+
+bool emailAddress::operator==(const class emailAddress& eml) const
+{
+ return (m_localName == eml.m_localName &&
+ m_domainName == eml.m_domainName);
+}
+
+
+bool emailAddress::operator!=(const class emailAddress& eml) const
+{
+ return !(*this == eml);
+}
+
+
+void emailAddress::copyFrom(const component& other)
+{
+ const emailAddress& source = dynamic_cast <const emailAddress&>(other);
+
+ m_localName = source.m_localName;
+ m_domainName = source.m_domainName;
+}
+
+
+emailAddress& emailAddress::operator=(const emailAddress& other)
+{
+ copyFrom(other);
+ return (*this);
+}
+
+
+ref <component>emailAddress::clone() const
+{
+ return vmime::create <emailAddress>(*this);
+}
+
+
+const word& emailAddress::getLocalName() const
+{
+ return m_localName;
+}
+
+
+void emailAddress::setLocalName(const word& localName)
+{
+ m_localName = localName;
+}
+
+
+const word& emailAddress::getDomainName() const
+{
+ return m_domainName;
+}
+
+
+void emailAddress::setDomainName(const word& domainName)
+{
+ m_domainName = domainName;
+}
+
+
+const std::vector <ref <component> > emailAddress::getChildComponents()
+{
+ return std::vector <ref <component> >();
+}
+
+
+bool emailAddress::isEmpty() const
+{
+ return m_localName.isEmpty();
+}
+
+
+} // vmime
diff --git a/src/encoding.cpp b/src/encoding.cpp
index 49d78b75..53f88531 100644
--- a/src/encoding.cpp
+++ b/src/encoding.cpp
@@ -61,8 +61,9 @@ encoding::encoding(const encoding& enc)
}
-void encoding::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void encoding::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
m_usage = USAGE_UNKNOWN;
@@ -80,8 +81,9 @@ void encoding::parseImpl(const string& buffer, const string::size_type position,
}
-void encoding::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void encoding::generateImpl
+ (const generationContext& /* ctx */, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
os << m_name;
diff --git a/src/generationContext.cpp b/src/generationContext.cpp
new file mode 100644
index 00000000..0f19e623
--- /dev/null
+++ b/src/generationContext.cpp
@@ -0,0 +1,109 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 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 3 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.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/generationContext.hpp"
+
+
+namespace vmime
+{
+
+
+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("")
+{
+}
+
+
+generationContext::generationContext(const generationContext& ctx)
+ : context(ctx),
+ m_maxLineLength(ctx.m_maxLineLength),
+ m_prologText(ctx.m_prologText),
+ m_epilogText(ctx.m_epilogText)
+{
+}
+
+
+generationContext& generationContext::getDefaultContext()
+{
+ static generationContext ctx;
+ return ctx;
+}
+
+
+string::size_type generationContext::getMaxLineLength() const
+{
+ return m_maxLineLength;
+}
+
+
+void generationContext::setMaxLineLength(const string::size_type maxLineLength)
+{
+ m_maxLineLength = maxLineLength;
+}
+
+
+const string generationContext::getPrologText() const
+{
+ return m_prologText;
+}
+
+
+void generationContext::setPrologText(const string& prologText)
+{
+ m_prologText = prologText;
+}
+
+
+const string generationContext::getEpilogText() const
+{
+ return m_epilogText;
+}
+
+
+void generationContext::setEpilogText(const string& epilogText)
+{
+ m_epilogText = epilogText;
+}
+
+
+generationContext& generationContext::operator=(const generationContext& ctx)
+{
+ copyFrom(ctx);
+ return *this;
+}
+
+
+void generationContext::copyFrom(const generationContext& ctx)
+{
+ context::copyFrom(ctx);
+
+ m_maxLineLength = ctx.m_maxLineLength;
+ m_prologText = ctx.m_prologText;
+ m_epilogText = ctx.m_epilogText;
+}
+
+
+} // vmime
diff --git a/src/header.cpp b/src/header.cpp
index 6543a302..d1896d96 100644
--- a/src/header.cpp
+++ b/src/header.cpp
@@ -61,8 +61,9 @@ field-body-contents =
specials tokens, or else consisting of texts>
*/
-void header::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void header::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
string::size_type pos = position;
@@ -70,7 +71,7 @@ void header::parseImpl(const string& buffer, const string::size_type position,
while (pos < end)
{
- ref <headerField> field = headerField::parseNext(buffer, pos, end, &pos);
+ ref <headerField> field = headerField::parseNext(ctx, buffer, pos, end, &pos);
if (field == NULL) break;
m_fields.push_back(field);
@@ -83,14 +84,15 @@ void header::parseImpl(const string& buffer, const string::size_type position,
}
-void header::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type /* curLinePos */, string::size_type* newLinePos) const
+void header::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type /* curLinePos */, string::size_type* newLinePos) const
{
// Generate the fields
for (std::vector <ref <headerField> >::const_iterator it = m_fields.begin() ;
it != m_fields.end() ; ++it)
{
- (*it)->generate(os, maxLineLength);
+ (*it)->generate(ctx, os);
os << CRLF;
}
diff --git a/src/headerField.cpp b/src/headerField.cpp
index 3d0f8834..0a17abac 100644
--- a/src/headerField.cpp
+++ b/src/headerField.cpp
@@ -73,8 +73,9 @@ headerField& headerField::operator=(const headerField& other)
}
-ref <headerField> headerField::parseNext(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+ref <headerField> headerField::parseNext
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
string::size_type pos = position;
@@ -215,7 +216,7 @@ ref <headerField> headerField::parseNext(const string& buffer, const string::siz
// Return a new field
ref <headerField> field = headerFieldFactory::getInstance()->create(name);
- field->parse(buffer, contentsStart, contentsEnd, NULL);
+ field->parse(ctx, buffer, contentsStart, contentsEnd, NULL);
field->setParsedBounds(nameStart, pos);
if (newPosition)
@@ -262,19 +263,21 @@ ref <headerField> headerField::parseNext(const string& buffer, const string::siz
}
-void headerField::parseImpl(const string& buffer, const string::size_type position, const string::size_type end,
- string::size_type* newPosition)
+void headerField::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
- m_value->parse(buffer, position, end, newPosition);
+ m_value->parse(ctx, buffer, position, end, newPosition);
}
-void headerField::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void headerField::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
os << m_name + ": ";
- m_value->generate(os, maxLineLength, curLinePos + m_name.length() + 2, newLinePos);
+ m_value->generate(ctx, os, curLinePos + m_name.length() + 2, newLinePos);
}
diff --git a/src/mailbox.cpp b/src/mailbox.cpp
index a9d18958..1c199a76 100644
--- a/src/mailbox.cpp
+++ b/src/mailbox.cpp
@@ -23,6 +23,7 @@
#include "vmime/mailbox.hpp"
#include "vmime/parserHelpers.hpp"
+#include "vmime/utility/outputStreamStringAdapter.hpp"
namespace vmime
@@ -40,13 +41,13 @@ mailbox::mailbox(const mailbox& mbox)
}
-mailbox::mailbox(const string& email)
+mailbox::mailbox(const emailAddress& email)
: m_email(email)
{
}
-mailbox::mailbox(const text& name, const string& email)
+mailbox::mailbox(const text& name, const emailAddress& email)
: m_name(name), m_email(email)
{
}
@@ -65,8 +66,9 @@ angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
*/
-void mailbox::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void mailbox::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
const string::value_type* const pend = buffer.data() + end;
const string::value_type* const pstart = buffer.data() + position;
@@ -313,27 +315,13 @@ void mailbox::parseImpl(const string& buffer, const string::size_type position,
// (email address is mandatory, whereas name is optional).
if (address.empty() && !name.empty() && !hadBrackets)
{
- m_email.clear();
- m_email.reserve(name.size());
m_name.removeAllWords();
-
- for (string::size_type i = 0 ; i < name.size() ; ++i)
- {
- if (!parserHelpers::isSpace(name[i]))
- m_email += name[i];
- }
+ m_email.parse(ctx, name);
}
else
{
- text::decodeAndUnfold(name, &m_name);
- m_email.clear();
- m_email.reserve(address.size());
-
- for (string::size_type i = 0 ; i < address.size() ; ++i)
- {
- if (!parserHelpers::isSpace(address[i]))
- m_email += address[i];
- }
+ text::decodeAndUnfold(ctx, name, &m_name);
+ m_email.parse(ctx, address);
}
setParsedBounds(position, position + (p - pstart));
@@ -343,28 +331,30 @@ void mailbox::parseImpl(const string& buffer, const string::size_type position,
}
-void mailbox::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void mailbox::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
+ string generatedEmail;
+ utility::outputStreamStringAdapter generatedEmailStream(generatedEmail);
+ m_email.generate(ctx, generatedEmailStream, 0, NULL);
+
if (m_name.isEmpty())
{
- bool newLine = false;
+ string::size_type pos = curLinePos;
// No display name is specified, only email address.
- if (curLinePos /* + 2 */ + m_email.length() > maxLineLength)
+ if (curLinePos + generatedEmail.length() > ctx.getMaxLineLength())
{
os << NEW_LINE_SEQUENCE;
- newLine = true;
+ pos = NEW_LINE_SEQUENCE.length();
}
- //os << "<" << m_email << ">";
- os << m_email;
+ os << generatedEmail;
+ pos += generatedEmail.length();
if (newLinePos)
- {
- *newLinePos = curLinePos + m_email.length() /* + 2 */;
- if (newLine) *newLinePos += 1;
- }
+ *newLinePos = pos;
}
else
{
@@ -415,24 +405,21 @@ void mailbox::generateImpl(utility::outputStream& os, const string::size_type ma
}
string::size_type pos = curLinePos;
- bool newLine = true;
- m_name.encodeAndFold(os, maxLineLength, pos, &pos,
+ m_name.encodeAndFold(ctx, os, pos, &pos,
text::QUOTE_IF_POSSIBLE | (forceEncode ? text::FORCE_ENCODING : 0));
- if (pos + m_email.length() + 3 > maxLineLength)
+ if (pos + generatedEmail.length() + 3 > ctx.getMaxLineLength())
{
os << NEW_LINE_SEQUENCE;
- newLine = true;
+ pos = NEW_LINE_SEQUENCE.length();
}
- os << " <" << m_email << ">";
+ os << " <" << generatedEmail << ">";
+ pos += 2 + generatedEmail.length() + 1;
if (newLinePos)
- {
- *newLinePos = pos + m_email.length() + 3;
- if (newLine) *newLinePos += NEW_LINE_SEQUENCE.length();
- }
+ *newLinePos = pos;
}
}
@@ -473,14 +460,14 @@ ref <component>mailbox::clone() const
bool mailbox::isEmpty() const
{
- return (m_email.empty());
+ return m_email.isEmpty();
}
void mailbox::clear()
{
m_name.removeAllWords();
- m_email.clear();
+ m_email = emailAddress();
}
@@ -502,13 +489,13 @@ void mailbox::setName(const text& name)
}
-const string& mailbox::getEmail() const
+const emailAddress& mailbox::getEmail() const
{
return (m_email);
}
-void mailbox::setEmail(const string& email)
+void mailbox::setEmail(const emailAddress& email)
{
m_email = email;
}
diff --git a/src/mailboxField.cpp b/src/mailboxField.cpp
index c3c5214e..1f11f49c 100644
--- a/src/mailboxField.cpp
+++ b/src/mailboxField.cpp
@@ -43,15 +43,16 @@ mailboxField::mailboxField(const mailboxField&)
}
-void mailboxField::parse(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void mailboxField::parse
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
ref <mailbox> mbox = vmime::create <mailbox>();
// Here, we cannot simply call "m_mailbox.parse()" because it
// may have more than one address specified (even if this field
// should contain only one). We are never too much careful...
- ref <address> parsedAddress = address::parseNext(buffer, position, end, newPosition);
+ ref <address> parsedAddress = address::parseNext(ctx, buffer, position, end, newPosition);
if (parsedAddress)
{
diff --git a/src/mailboxGroup.cpp b/src/mailboxGroup.cpp
index 251f920b..65611b33 100644
--- a/src/mailboxGroup.cpp
+++ b/src/mailboxGroup.cpp
@@ -54,8 +54,9 @@ mailboxGroup::~mailboxGroup()
}
-void mailboxGroup::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void mailboxGroup::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
const string::value_type* const pend = buffer.data() + end;
const string::value_type* const pstart = buffer.data() + position;
@@ -80,7 +81,7 @@ void mailboxGroup::parseImpl(const string& buffer, const string::size_type posit
while (pos < end)
{
- ref <address> parsedAddress = address::parseNext(buffer, pos, end, &pos);
+ ref <address> parsedAddress = address::parseNext(ctx, buffer, pos, end, &pos);
if (parsedAddress)
{
@@ -102,7 +103,7 @@ void mailboxGroup::parseImpl(const string& buffer, const string::size_type posit
}
}
- text::decodeAndUnfold(name, &m_name);
+ text::decodeAndUnfold(ctx, name, &m_name);
setParsedBounds(position, end);
@@ -111,8 +112,9 @@ void mailboxGroup::parseImpl(const string& buffer, const string::size_type posit
}
-void mailboxGroup::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void mailboxGroup::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
// We have to encode the name:
// - if it contains characters in a charset different from "US-ASCII",
@@ -156,7 +158,10 @@ void mailboxGroup::generateImpl(utility::outputStream& os, const string::size_ty
string::size_type pos = curLinePos;
- m_name.encodeAndFold(os, maxLineLength - 2, pos, &pos,
+ generationContext tmpCtx(ctx);
+ tmpCtx.setMaxLineLength(ctx.getMaxLineLength() - 2);
+
+ m_name.encodeAndFold(ctx, os, pos, &pos,
forceEncode ? text::FORCE_ENCODING : 0);
os << ":";
@@ -176,7 +181,7 @@ void mailboxGroup::generateImpl(utility::outputStream& os, const string::size_ty
++pos;
}
- (*it)->generate(os, maxLineLength - 2, pos, &pos);
+ (*it)->generate(tmpCtx, os, pos, &pos);
}
os << ";";
diff --git a/src/mailboxList.cpp b/src/mailboxList.cpp
index b3106fb8..03a225f8 100644
--- a/src/mailboxList.cpp
+++ b/src/mailboxList.cpp
@@ -196,17 +196,18 @@ const std::vector <ref <component> > mailboxList::getChildComponents()
}
-void mailboxList::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void mailboxList::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
- m_list.parse(buffer, position, end, newPosition);
+ m_list.parse(ctx, buffer, position, end, newPosition);
}
-void mailboxList::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
+void mailboxList::generateImpl(const generationContext& ctx, utility::outputStream& os,
const string::size_type curLinePos, string::size_type* newLinePos) const
{
- m_list.generate(os, maxLineLength, curLinePos, newLinePos);
+ m_list.generate(ctx, os, curLinePos, newLinePos);
}
diff --git a/src/mdn/MDNHelper.cpp b/src/mdn/MDNHelper.cpp
index 533813b1..a0d48599 100644
--- a/src/mdn/MDNHelper.cpp
+++ b/src/mdn/MDNHelper.cpp
@@ -269,7 +269,7 @@ ref <bodyPart> MDNHelper::createSecondMDNPart(const sendableMDNInfos& mdnInfos,
ref <headerField> fr = headerFieldFactory::getInstance()->
create(vmime::fields::FINAL_RECIPIENT);
- fr->setValue("rfc822; " + mdnInfos.getRecipient().getEmail());
+ fr->setValue("rfc822; " + mdnInfos.getRecipient().getEmail().generate());
fields.appendField(fr);
diff --git a/src/mediaType.cpp b/src/mediaType.cpp
index 92e8d058..62d65c23 100644
--- a/src/mediaType.cpp
+++ b/src/mediaType.cpp
@@ -48,8 +48,9 @@ mediaType::mediaType(const string& type, const string& subType)
}
-void mediaType::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void mediaType::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
const string::value_type* const pend = buffer.data() + end;
const string::value_type* const pstart = buffer.data() + position;
@@ -82,12 +83,13 @@ void mediaType::parseImpl(const string& buffer, const string::size_type position
}
-void mediaType::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void mediaType::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
const string value = m_type + "/" + m_subType;
- if (curLinePos + value.length() > maxLineLength)
+ if (curLinePos + value.length() > ctx.getMaxLineLength())
{
os << NEW_LINE_SEQUENCE;
os << value;
diff --git a/src/message.cpp b/src/message.cpp
index c52d54c2..75016c36 100644
--- a/src/message.cpp
+++ b/src/message.cpp
@@ -22,7 +22,6 @@
//
#include "vmime/message.hpp"
-#include "vmime/options.hpp"
#include "vmime/utility/outputStreamAdapter.hpp"
@@ -38,37 +37,10 @@ message::message()
}
-void message::generate(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
-{
- // We override this function to change the default value for the
- // "maxLineLength" parameter. So, the user will just have to call
- // message::generate() without any argument to use the maximum line
- // length specified in vmime::options...
- bodyPart::generate(os, maxLineLength, curLinePos, newLinePos);
-}
-
-
const string message::generate(const string::size_type maxLineLength,
const string::size_type curLinePos) const
{
- std::ostringstream oss;
- utility::outputStreamAdapter adapter(oss);
-
- generate(adapter, maxLineLength, curLinePos, NULL);
-
- return (oss.str());
-}
-
-
-
-void message::generate
- (ref <utility::outputStream> os,
- const string::size_type maxLineLength,
- const string::size_type curLinePos,
- string::size_type* newLinePos) const
-{
- bodyPart::generate(os, maxLineLength, curLinePos, newLinePos);
+ return bodyPart::generate(maxLineLength, curLinePos);
}
diff --git a/src/messageId.cpp b/src/messageId.cpp
index 3102294e..6b558e1b 100644
--- a/src/messageId.cpp
+++ b/src/messageId.cpp
@@ -61,8 +61,9 @@ messageId::messageId(const string& left, const string& right)
msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
*/
-void messageId::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void messageId::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
const string::value_type* const pend = buffer.data() + end;
const string::value_type* const pstart = buffer.data() + position;
@@ -145,8 +146,9 @@ void messageId::parseImpl(const string& buffer, const string::size_type position
}
-ref <messageId> messageId::parseNext(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+ref <messageId> messageId::parseNext
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
string::size_type pos = position;
@@ -161,7 +163,7 @@ ref <messageId> messageId::parseNext(const string& buffer, const string::size_ty
++pos;
ref <messageId> mid = vmime::create <messageId>();
- mid->parse(buffer, begin, pos, NULL);
+ mid->parse(ctx, buffer, begin, pos, NULL);
if (newPosition != NULL)
*newPosition = pos;
@@ -185,12 +187,13 @@ const string messageId::getId() const
}
-void messageId::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void messageId::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
string::size_type pos = curLinePos;
- if (curLinePos + m_left.length() + m_right.length() + 3 > maxLineLength)
+ if (curLinePos + m_left.length() + m_right.length() + 3 > ctx.getMaxLineLength())
{
os << NEW_LINE_SEQUENCE;
pos = NEW_LINE_SEQUENCE_LENGTH;
diff --git a/src/messageIdSequence.cpp b/src/messageIdSequence.cpp
index a255235a..99c96319 100644
--- a/src/messageIdSequence.cpp
+++ b/src/messageIdSequence.cpp
@@ -84,8 +84,9 @@ const std::vector <ref <component> > messageIdSequence::getChildComponents()
}
-void messageIdSequence::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void messageIdSequence::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
removeAllMessageIds();
@@ -93,7 +94,7 @@ void messageIdSequence::parseImpl(const string& buffer, const string::size_type
while (pos < end)
{
- ref <messageId> parsedMid = messageId::parseNext(buffer, pos, end, &pos);
+ ref <messageId> parsedMid = messageId::parseNext(ctx, buffer, pos, end, &pos);
if (parsedMid != NULL)
m_list.push_back(parsedMid);
@@ -106,16 +107,20 @@ void messageIdSequence::parseImpl(const string& buffer, const string::size_type
}
-void messageIdSequence::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void messageIdSequence::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
string::size_type pos = curLinePos;
if (!m_list.empty())
{
+ generationContext tmpCtx(ctx);
+ tmpCtx.setMaxLineLength(ctx.getMaxLineLength() - 2);
+
for (std::vector <ref <messageId> >::const_iterator it = m_list.begin() ; ; )
{
- (*it)->generate(os, maxLineLength - 2, pos, &pos);
+ (*it)->generate(ctx, os, pos, &pos);
if (++it == m_list.end())
break;
diff --git a/src/net/sendmail/sendmailTransport.cpp b/src/net/sendmail/sendmailTransport.cpp
index 181d0d7f..dbbb55a8 100644
--- a/src/net/sendmail/sendmailTransport.cpp
+++ b/src/net/sendmail/sendmailTransport.cpp
@@ -152,11 +152,11 @@ void sendmailTransport::send
args.push_back("-i");
args.push_back("-f");
- args.push_back(expeditor.getEmail());
+ args.push_back(expeditor.getEmail().generate());
args.push_back("--");
for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)
- args.push_back(recipients.getMailboxAt(i)->getEmail());
+ args.push_back(recipients.getMailboxAt(i)->getEmail().generate());
// Call sendmail
try
diff --git a/src/net/smtp/SMTPCommand.cpp b/src/net/smtp/SMTPCommand.cpp
index f338e248..99a3ac17 100644
--- a/src/net/smtp/SMTPCommand.cpp
+++ b/src/net/smtp/SMTPCommand.cpp
@@ -32,6 +32,7 @@
#include "vmime/net/socket.hpp"
#include "vmime/mailbox.hpp"
+#include "vmime/utility/outputStreamAdapter.hpp"
namespace vmime {
@@ -90,7 +91,12 @@ ref <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox)
{
std::ostringstream cmd;
cmd.imbue(std::locale::classic());
- cmd << "MAIL FROM:<" << mbox.getEmail() << ">";
+ cmd << "MAIL FROM:<";
+
+ vmime::utility::outputStreamAdapter cmd2(cmd);
+ mbox.getEmail().generate(cmd2);
+
+ cmd << ">";
return createCommand(cmd.str());
}
@@ -101,7 +107,12 @@ ref <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox)
{
std::ostringstream cmd;
cmd.imbue(std::locale::classic());
- cmd << "RCPT TO:<" << mbox.getEmail() << ">";
+ cmd << "RCPT TO:<";
+
+ vmime::utility::outputStreamAdapter cmd2(cmd);
+ mbox.getEmail().generate(cmd2);
+
+ cmd << ">";
return createCommand(cmd.str());
}
diff --git a/src/parameter.cpp b/src/parameter.cpp
index fd39c641..37a59890 100644
--- a/src/parameter.cpp
+++ b/src/parameter.cpp
@@ -113,18 +113,23 @@ void parameter::setValue(const word& value)
}
-void parameter::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void parameter::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
m_value->setBuffer(string(buffer.begin() + position, buffer.begin() + end));
- m_value->setCharset(charset(charsets::US_ASCII));
+
+ if (ctx.getInternationalizedEmailSupport())
+ m_value->setCharset(charset(charsets::UTF_8));
+ else
+ m_value->setCharset(charset(charsets::US_ASCII));
if (newPosition)
*newPosition = end;
}
-void parameter::parse(const std::vector <valueChunk>& chunks)
+void parameter::parse(const parsingContext& ctx, const std::vector <valueChunk>& chunks)
{
bool foundCharsetChunk = false;
@@ -236,7 +241,7 @@ void parameter::parse(const std::vector <valueChunk>& chunks)
// if the data is not encoded, because it can recover
// from parsing errors.
vmime::text t;
- t.parse(chunk.data);
+ t.parse(ctx, chunk.data);
if (t.getWordCount() != 0)
{
@@ -253,8 +258,9 @@ void parameter::parse(const std::vector <valueChunk>& chunks)
}
-void parameter::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void parameter::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
const string& name = m_name;
const string& value = m_value->getBuffer();
@@ -276,7 +282,7 @@ void parameter::generateImpl(utility::outputStream& os, const string::size_type
string::size_type pos = curLinePos;
- if (pos + name.length() + 10 + value.length() > maxLineLength)
+ if (pos + name.length() + 10 + value.length() > ctx.getMaxLineLength())
{
sevenBitStream << NEW_LINE_SEQUENCE;
pos = NEW_LINE_SEQUENCE_LENGTH;
@@ -287,7 +293,7 @@ void parameter::generateImpl(utility::outputStream& os, const string::size_type
string::size_type valueLength = 0;
// Use worst-case length name.length()+2 for 'name=' part of line
- for (string::size_type i = 0 ; (i < value.length()) && (pos + name.length() + 2 + valueLength < maxLineLength - 4) ; ++i, ++valueLength)
+ for (string::size_type i = 0 ; (i < value.length()) && (pos + name.length() + 2 + valueLength < ctx.getMaxLineLength() - 4) ; ++i, ++valueLength)
{
switch (value[i])
{
@@ -431,7 +437,7 @@ void parameter::generateImpl(utility::outputStream& os, const string::size_type
name.length() + 4 /* *0*= */ + 2 /* '' */
+ m_value->getCharset().getName().length();
- if (pos + firstSectionLength + 5 >= maxLineLength)
+ if (pos + firstSectionLength + 5 >= ctx.getMaxLineLength())
{
os << NEW_LINE_SEQUENCE;
pos = NEW_LINE_SEQUENCE_LENGTH;
@@ -448,7 +454,7 @@ void parameter::generateImpl(utility::outputStream& os, const string::size_type
{
// Check whether we should start a new line (taking into
// account the next character will be encoded = worst case)
- if (currentSectionLength + 3 >= maxLineLength)
+ if (currentSectionLength + 3 >= ctx.getMaxLineLength())
{
sectionText.push_back(currentSection);
sectionCount++;
diff --git a/src/parameterizedHeaderField.cpp b/src/parameterizedHeaderField.cpp
index 77d732df..619fe7cf 100644
--- a/src/parameterizedHeaderField.cpp
+++ b/src/parameterizedHeaderField.cpp
@@ -78,8 +78,9 @@ struct paramInfo
#endif // VMIME_BUILDING_DOC
-void parameterizedHeaderField::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void parameterizedHeaderField::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
const string::value_type* const pend = buffer.data() + end;
const string::value_type* const pstart = buffer.data() + position;
@@ -108,7 +109,7 @@ void parameterizedHeaderField::parseImpl(const string& buffer, const string::siz
--valueLength;
// Parse value
- getValue()->parse(buffer, valueStart, valueStart + valueLength);
+ getValue()->parse(ctx, buffer, valueStart, valueStart + valueLength);
// Reset parameters
removeAllParameters();
@@ -316,7 +317,7 @@ void parameterizedHeaderField::parseImpl(const string& buffer, const string::siz
// Append this parameter to the list
ref <parameter> param = vmime::create <parameter>((*it).first);
- param->parse(info.value);
+ param->parse(ctx, info.value);
param->setParsedBounds(info.start, info.end);
appendParameter(param);
@@ -328,13 +329,14 @@ void parameterizedHeaderField::parseImpl(const string& buffer, const string::siz
}
-void parameterizedHeaderField::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void parameterizedHeaderField::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
string::size_type pos = curLinePos;
// Parent header field
- headerField::generateImpl(os, maxLineLength, pos, &pos);
+ headerField::generateImpl(ctx, os, pos, &pos);
// Parameters
for (std::vector <ref <parameter> >::const_iterator
@@ -343,7 +345,7 @@ void parameterizedHeaderField::generateImpl(utility::outputStream& os, const str
os << "; ";
pos += 2;
- (*it)->generate(os, maxLineLength, pos, &pos);
+ (*it)->generate(ctx, os, pos, &pos);
}
if (newLinePos)
diff --git a/src/options.cpp b/src/parsingContext.cpp
index 9ec6056b..b440ef1e 100644
--- a/src/options.cpp
+++ b/src/parsingContext.cpp
@@ -21,49 +21,28 @@
// the GNU General Public License cover the whole combination.
//
-#include "vmime/options.hpp"
+#include "vmime/parsingContext.hpp"
namespace vmime
{
-options* options::getInstance()
+parsingContext::parsingContext()
{
- static options instance;
- return (&instance);
}
-options::multipartOptions::multipartOptions()
- : m_prologText("This is a multi-part message in MIME format. Your mail reader " \
- "does not understand MIME message format."),
- m_epilogText("")
+parsingContext::parsingContext(const parsingContext& ctx)
+ : context()
{
}
-const string& options::multipartOptions::getPrologText() const
+parsingContext& parsingContext::getDefaultContext()
{
- return (m_prologText);
-}
-
-
-void options::multipartOptions::setPrologText(const string& prologText)
-{
- m_prologText = prologText;
-}
-
-
-const string& options::multipartOptions::getEpilogText() const
-{
- return (m_epilogText);
-}
-
-
-void options::multipartOptions::setEpilogText(const string& epilogText)
-{
- m_epilogText = epilogText;
+ static parsingContext ctx;
+ return ctx;
}
diff --git a/src/path.cpp b/src/path.cpp
index 6fe3e7aa..3e6e7a84 100644
--- a/src/path.cpp
+++ b/src/path.cpp
@@ -112,8 +112,9 @@ const std::vector <ref <component> > path::getChildComponents()
}
-void path::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void path::parseImpl
+ (const parsingContext& /* ctx */, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
string::size_type pos = position;
@@ -165,8 +166,9 @@ void path::parseImpl(const string& buffer, const string::size_type position,
}
-void path::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void path::generateImpl
+ (const generationContext& /* ctx */, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
if (m_localPart.empty() && m_domain.empty())
{
diff --git a/src/relay.cpp b/src/relay.cpp
index 2262fa7e..90957dbe 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -24,6 +24,7 @@
#include "vmime/relay.hpp"
#include "vmime/text.hpp"
#include "vmime/parserHelpers.hpp"
+#include "vmime/utility/outputStreamAdapter.hpp"
#include <sstream>
@@ -57,8 +58,9 @@ relay::relay(const relay& r)
["for" addr-spec] ; initial form
*/
-void relay::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void relay::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
const string::value_type* const pend = buffer.data() + end;
const string::value_type* const pstart = buffer.data() + position;
@@ -71,7 +73,7 @@ void relay::parseImpl(const string& buffer, const string::size_type position,
if (p >= pstart)
{
// Parse the date/time part
- m_date.parse(buffer, position + (p - pstart) + 1, end);
+ m_date.parse(ctx, buffer, position + (p - pstart) + 1, end);
// Parse the components
std::istringstream iss(string
@@ -198,8 +200,9 @@ void relay::parseImpl(const string& buffer, const string::size_type position,
}
-void relay::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void relay::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
std::ostringstream oss;
int count = 0;
@@ -217,9 +220,12 @@ void relay::generateImpl(utility::outputStream& os, const string::size_type maxL
if (m_id.length()) oss << (count++ > 0 ? " " : "") << "id " << m_id;
if (m_for.length()) oss << (count++ > 0 ? " " : "") << "for " << m_for;
- oss << "; " << m_date.generate();
+ oss << "; ";
- text(oss.str()).encodeAndFold(os, maxLineLength,
+ vmime::utility::outputStreamAdapter dos(oss);
+ m_date.generate(ctx, dos, 0, NULL);
+
+ text(oss.str()).encodeAndFold(ctx, os,
curLinePos, newLinePos, text::FORCE_NO_ENCODING);
}
diff --git a/src/text.cpp b/src/text.cpp
index 1ba83101..d1ae6075 100644
--- a/src/text.cpp
+++ b/src/text.cpp
@@ -67,14 +67,15 @@ text::~text()
}
-void text::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void text::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
removeAllWords();
string::size_type newPos;
- const std::vector <ref <word> > words = word::parseMultiple(buffer, position, end, &newPos);
+ const std::vector <ref <word> > words = word::parseMultiple(ctx, buffer, position, end, &newPos);
copy_vector(words, m_words);
@@ -85,10 +86,11 @@ void text::parseImpl(const string& buffer, const string::size_type position,
}
-void text::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type curLinePos, string::size_type* newLinePos) const
+void text::generateImpl
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type curLinePos, string::size_type* newLinePos) const
{
- encodeAndFold(os, maxLineLength, curLinePos, newLinePos, 0);
+ encodeAndFold(ctx, os, curLinePos, newLinePos, 0);
}
@@ -142,12 +144,12 @@ bool text::operator!=(const text& t) const
}
-const string text::getConvertedText(const charset& dest) const
+const string text::getConvertedText(const charset& dest, const charsetConverterOptions& opts) const
{
string out;
for (std::vector <ref <word> >::const_iterator i = m_words.begin() ; i != m_words.end() ; ++i)
- out += (*i)->getConvertedText(dest);
+ out += (*i)->getConvertedText(dest, opts);
return (out);
}
@@ -348,15 +350,16 @@ void text::createFromString(const string& in, const charset& ch)
}
-void text::encodeAndFold(utility::outputStream& os, const string::size_type maxLineLength,
- const string::size_type firstLineOffset, string::size_type* lastLineLength, const int flags) const
+void text::encodeAndFold
+ (const generationContext& ctx, utility::outputStream& os,
+ const string::size_type firstLineOffset, string::size_type* lastLineLength, const int flags) const
{
string::size_type curLineLength = firstLineOffset;
word::generatorState state;
for (size_t wi = 0 ; wi < getWordCount() ; ++wi)
{
- getWordAt(wi)->generate(os, maxLineLength, curLineLength,
+ getWordAt(wi)->generate(ctx, os, curLineLength,
&curLineLength, flags, &state);
}
@@ -369,7 +372,17 @@ ref <text> text::decodeAndUnfold(const string& in)
{
ref <text> t = vmime::create <text>();
- decodeAndUnfold(in, t.get());
+ decodeAndUnfold(parsingContext::getDefaultContext(), in, t.get());
+
+ return t;
+}
+
+
+ref <text> text::decodeAndUnfold(const parsingContext& ctx, const string& in)
+{
+ ref <text> t = vmime::create <text>();
+
+ decodeAndUnfold(ctx, in, t.get());
return t;
}
@@ -377,11 +390,17 @@ ref <text> text::decodeAndUnfold(const string& in)
text* text::decodeAndUnfold(const string& in, text* generateInExisting)
{
+ return decodeAndUnfold(parsingContext::getDefaultContext(), in, generateInExisting);
+}
+
+
+text* text::decodeAndUnfold(const parsingContext& ctx, const string& in, text* generateInExisting)
+{
text* out = (generateInExisting != NULL) ? generateInExisting : new text();
out->removeAllWords();
- const std::vector <ref <word> > words = word::parseMultiple(in, 0, in.length(), NULL);
+ const std::vector <ref <word> > words = word::parseMultiple(ctx, in, 0, in.length(), NULL);
copy_vector(words, out->m_words);
diff --git a/src/utility/stringUtils.cpp b/src/utility/stringUtils.cpp
index ad498342..8e5f7205 100644
--- a/src/utility/stringUtils.cpp
+++ b/src/utility/stringUtils.cpp
@@ -151,6 +151,12 @@ string::size_type stringUtils::countASCIIchars
}
+bool stringUtils::is7bit(const string& str)
+{
+ return countASCIIchars(str.begin(), str.end()) == str.length();
+}
+
+
string::size_type stringUtils::findFirstNonASCIIchar
(const string::const_iterator begin, const string::const_iterator end)
{
@@ -205,5 +211,32 @@ const string stringUtils::unquote(const string& str)
}
+bool stringUtils::needQuoting(const string& str, const string& specialChars)
+{
+ return str.find_first_of(specialChars.c_str()) != string::npos;
+}
+
+
+string stringUtils::quote
+ (const string& str, const string& escapeSpecialChars, const string& escapeChar)
+{
+ std::ostringstream oss;
+ string::size_type lastPos = 0, pos = 0;
+
+ while ((pos = str.find_first_of(escapeSpecialChars, lastPos)) != string::npos)
+ {
+ oss << str.substr(lastPos, pos - lastPos)
+ << escapeChar
+ << str[pos];
+
+ lastPos = pos + 1;
+ }
+
+ oss << str.substr(lastPos);
+
+ return oss.str();
+}
+
+
} // utility
} // vmime
diff --git a/src/word.cpp b/src/word.cpp
index 9ab31087..3be9998e 100644
--- a/src/word.cpp
+++ b/src/word.cpp
@@ -66,9 +66,10 @@ word::word(const string& buffer, const charset& charset)
}
-ref <word> word::parseNext(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition,
- bool prevIsEncoded, bool* isEncoded, bool isFirst)
+ref <word> word::parseNext
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition,
+ bool prevIsEncoded, bool* isEncoded, bool isFirst)
{
string::size_type pos = position;
@@ -87,6 +88,9 @@ ref <word> word::parseNext(const string& buffer, const string::size_type positio
string::size_type startPos = pos;
string unencoded;
+ const charset defaultCharset = ctx.getInternationalizedEmailSupport()
+ ? charset(charsets::UTF_8) : charset(charsets::US_ASCII);
+
while (pos < end)
{
// End of line: does not occur in the middle of an encoded word. This is
@@ -124,7 +128,7 @@ ref <word> word::parseNext(const string& buffer, const string::size_type positio
if (prevIsEncoded)
unencoded = whiteSpaces + unencoded;
- ref <word> w = vmime::create <word>(unencoded, charset(charsets::US_ASCII));
+ ref <word> w = vmime::create <word>(unencoded, defaultCharset);
w->setParsedBounds(position, pos);
if (newPosition)
@@ -205,7 +209,7 @@ ref <word> word::parseNext(const string& buffer, const string::size_type positio
// Treat unencoded text at the end of the buffer
if (!unencoded.empty())
{
- ref <word> w = vmime::create <word>(unencoded, charset(charsets::US_ASCII));
+ ref <word> w = vmime::create <word>(unencoded, defaultCharset);
w->setParsedBounds(position, end);
if (newPosition)
@@ -221,8 +225,9 @@ ref <word> word::parseNext(const string& buffer, const string::size_type positio
}
-const std::vector <ref <word> > word::parseMultiple(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+const std::vector <ref <word> > word::parseMultiple
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
std::vector <ref <word> > res;
ref <word> w;
@@ -231,7 +236,7 @@ const std::vector <ref <word> > word::parseMultiple(const string& buffer, const
bool prevIsEncoded = false;
- while ((w = word::parseNext(buffer, pos, end, &pos, prevIsEncoded, &prevIsEncoded, (w == NULL))) != NULL)
+ while ((w = word::parseNext(ctx, buffer, pos, end, &pos, prevIsEncoded, &prevIsEncoded, (w == NULL))) != NULL)
res.push_back(w);
if (newPosition)
@@ -241,8 +246,9 @@ const std::vector <ref <word> > word::parseMultiple(const string& buffer, const
}
-void word::parseImpl(const string& buffer, const string::size_type position,
- const string::size_type end, string::size_type* newPosition)
+void word::parseImpl
+ (const parsingContext& ctx, const string& buffer, const string::size_type position,
+ const string::size_type end, string::size_type* newPosition)
{
if (position + 6 < end && // 6 = "=?(.+)?(.*)?="
buffer[position] == '=' && buffer[position + 1] == '?')
@@ -315,7 +321,8 @@ void word::parseImpl(const string& buffer, const string::size_type position,
// Unknown encoding or malformed encoded word: treat the buffer as ordinary text (RFC-2047, Page 9).
m_buffer = string(buffer.begin() + position, buffer.begin() + end);
- m_charset = charsets::US_ASCII;
+ m_charset = ctx.getInternationalizedEmailSupport()
+ ? charset(charsets::UTF_8) : charset(charsets::US_ASCII);
setParsedBounds(position, end);
@@ -324,14 +331,14 @@ void word::parseImpl(const string& buffer, const string::size_type position,
}
-void word::generateImpl(utility::outputStream& os, const string::size_type maxLineLength,
+void word::generateImpl(const generationContext& ctx, utility::outputStream& os,
const string::size_type curLinePos, string::size_type* newLinePos) const
{
- generate(os, maxLineLength, curLinePos, newLinePos, 0, NULL);
+ generate(ctx, os, curLinePos, newLinePos, 0, NULL);
}
-void word::generate(utility::outputStream& os, const string::size_type maxLineLength,
+void word::generate(const generationContext& ctx, utility::outputStream& os,
const string::size_type curLinePos, string::size_type* newLinePos, const int flags,
generatorState* state) const
{
@@ -350,17 +357,27 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
else if ((flags & text::FORCE_ENCODING) != 0)
encodingNeeded = true;
else // auto-detect
- encodingNeeded = wordEncoder::isEncodingNeeded(m_buffer, m_charset);
+ encodingNeeded = wordEncoder::isEncodingNeeded(ctx, m_buffer, m_charset);
+
+ // If text does not need to be encoded, quote the buffer (no folding is performed).
+ if (!encodingNeeded &&
+ (flags & text::QUOTE_IF_NEEDED) &&
+ utility::stringUtils::needQuoting(m_buffer))
+ {
+ const string quoted = utility::stringUtils::quote(m_buffer, "\\\"", "\\");
+ os << '"' << quoted << '"';
+ curLineLength += 1 + quoted.length() + 1;
+ }
// If possible and requested (with flag), quote the buffer (no folding is performed).
// Quoting is possible if and only if:
// - the buffer does not need to be encoded
// - the buffer does not contain quoting character (")
// - there is enough remaining space on the current line to hold the whole buffer
- if (!encodingNeeded &&
- (flags & text::QUOTE_IF_POSSIBLE) &&
- m_buffer.find('"') == string::npos &&
- (curLineLength + 2 /* 2 x " */ + m_buffer.length()) < maxLineLength)
+ else if (!encodingNeeded &&
+ (flags & text::QUOTE_IF_POSSIBLE) &&
+ m_buffer.find('"') == string::npos &&
+ (curLineLength + 2 /* 2 x " */ + m_buffer.length()) < ctx.getMaxLineLength())
{
os << '"' << m_buffer << '"';
curLineLength += 2 + m_buffer.length();
@@ -368,6 +385,19 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
// We will fold lines without encoding them.
else if (!encodingNeeded)
{
+ string buffer;
+
+ if (ctx.getInternationalizedEmailSupport())
+ {
+ // Convert the buffer to UTF-8
+ charset::convert(m_buffer, buffer, m_charset, charsets::UTF_8);
+ }
+ else
+ {
+ // Leave the buffer as-is
+ buffer = m_buffer;
+ }
+
// Here, we could have the following conditions:
//
// * a maximum line length of N bytes
@@ -379,7 +409,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
string::size_type maxRunLength = 0;
string::size_type curRunLength = 0;
- for (string::const_iterator p = m_buffer.begin(), end = m_buffer.end() ; p != end ; ++p)
+ for (string::const_iterator p = buffer.begin(), end = buffer.end() ; p != end ; ++p)
{
if (parserHelpers::isSpace(*p))
{
@@ -394,19 +424,19 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
maxRunLength = std::max(maxRunLength, curRunLength);
- if (((flags & text::FORCE_NO_ENCODING) == 0) && maxRunLength >= maxLineLength - 3)
+ if (((flags & text::FORCE_NO_ENCODING) == 0) && maxRunLength >= ctx.getMaxLineLength() - 3)
{
// Generate with encoding forced
- generate(os, maxLineLength, curLinePos, newLinePos, flags | text::FORCE_ENCODING, state);
+ generate(ctx, os, curLinePos, newLinePos, flags | text::FORCE_ENCODING, state);
return;
}
// Output runs, and fold line when a whitespace is encountered
- string::const_iterator lastWSpos = m_buffer.end(); // last white-space position
- string::const_iterator curLineStart = m_buffer.begin(); // current line start
+ string::const_iterator lastWSpos = buffer.end(); // last white-space position
+ string::const_iterator curLineStart = buffer.begin(); // current line start
- string::const_iterator p = m_buffer.begin();
- const string::const_iterator end = m_buffer.end();
+ string::const_iterator p = buffer.begin();
+ const string::const_iterator end = buffer.end();
bool finished = false;
bool newLine = false;
@@ -417,7 +447,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
{
// Exceeded maximum line length, but we have found a white-space
// where we can cut the line...
- if (curLineLength >= maxLineLength && lastWSpos != end)
+ if (curLineLength >= ctx.getMaxLineLength() && lastWSpos != end)
break;
if (*p == ' ' || *p == '\t')
@@ -437,7 +467,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
// we write the full line no matter of the max line length...
if (!newLine && p != end && lastWSpos == end &&
- !state->isFirstWord && curLineStart == m_buffer.begin())
+ !state->isFirstWord && curLineStart == buffer.begin())
{
// Here, we are continuing on the line of previous encoded
// word, but there is not even enough space to put the
@@ -468,7 +498,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
os << string(curLineStart, p);
- if (p != m_buffer.begin() && parserHelpers::isSpace(*(p - 1)))
+ if (p != buffer.begin() && parserHelpers::isSpace(*(p - 1)))
state->lastCharIsSpace = true;
else
state->lastCharIsSpace = false;
@@ -563,9 +593,9 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe
*/
const string::size_type maxLineLength3 =
- (maxLineLength == lineLengthLimits::infinite)
- ? maxLineLength
- : std::min(maxLineLength, static_cast <string::size_type>(76));
+ (ctx.getMaxLineLength() == lineLengthLimits::infinite)
+ ? ctx.getMaxLineLength()
+ : std::min(ctx.getMaxLineLength(), static_cast <string::size_type>(76));
wordEncoder wordEnc(m_buffer, m_charset);
@@ -691,13 +721,13 @@ bool word::operator!=(const word& w) const
}
-const string word::getConvertedText(const charset& dest) const
+const string word::getConvertedText(const charset& dest, const charsetConverterOptions& opts) const
{
string out;
try
{
- charset::convert(m_buffer, out, m_charset, dest);
+ charset::convert(m_buffer, out, m_charset, dest, opts);
}
catch (vmime::exceptions::charset_conv_error& e)
{
diff --git a/src/wordEncoder.cpp b/src/wordEncoder.cpp
index 32e46df1..82a74cff 100644
--- a/src/wordEncoder.cpp
+++ b/src/wordEncoder.cpp
@@ -168,7 +168,7 @@ const string wordEncoder::getNextChunk(const string::size_type maxLength)
// Fully RFC-compliant encoding
else
{
- charsetConverter conv(charsets::UTF_8, m_charset);
+ ref <charsetConverter> conv = charsetConverter::create(charsets::UTF_8, m_charset);
string::size_type inputCount = 0;
string::size_type outputCount = 0;
@@ -185,7 +185,7 @@ const string wordEncoder::getNextChunk(const string::size_type maxLength)
// Convert back to original encoding
string encodeBytes;
- conv.convert(inputChar, encodeBytes);
+ conv->convert(inputChar, encodeBytes);
encodeBuffer += encodeBytes;
@@ -225,23 +225,31 @@ wordEncoder::Encoding wordEncoder::getEncoding() const
// static
-bool wordEncoder::isEncodingNeeded(const string& buffer, const charset& charset)
+bool wordEncoder::isEncodingNeeded
+ (const generationContext& ctx, const string& buffer, const charset& charset)
{
- // Charset-specific encoding
- encoding recEncoding;
+ if (!ctx.getInternationalizedEmailSupport())
+ {
+ // Charset-specific encoding
+ encoding recEncoding;
- if (charset.getRecommendedEncoding(recEncoding))
- return true;
+ if (charset.getRecommendedEncoding(recEncoding))
+ return true;
- // No encoding is needed if the buffer only contains ASCII chars
- if (utility::stringUtils::findFirstNonASCIIchar(buffer.begin(), buffer.end()) != string::npos)
- return true;
+ // No encoding is needed if the buffer only contains ASCII chars
+ if (utility::stringUtils::findFirstNonASCIIchar(buffer.begin(), buffer.end()) != string::npos)
+ return true;
+ }
// Force encoding when there are only ASCII chars, but there is
// also at least one of '\n' or '\r' (header fields)
if (buffer.find_first_of("\n\r") != string::npos)
return true;
+ // If any RFC-2047 sequence is found in the buffer, encode it
+ if (buffer.find("=?") != string::npos)
+ return true;
+
return false;
}