Beispiel #1
0
// static
bool wordEncoder::isEncodingNeeded
	(const generationContext& ctx, const string& buffer,
	 const charset& charset, const string& lang)
{
	if (!ctx.getInternationalizedEmailSupport())
	{
		// Charset-specific encoding
		encoding recEncoding;

		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;
	}

	// 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 || buffer.find("?=") != string::npos)
		return true;

	// If a language is specified, force encoding
	if (!lang.empty())
		return true;

	return false;
}
Beispiel #2
0
void mediaType::generateImpl(
	const generationContext& ctx,
	utility::outputStream& os,
	const size_t curLinePos,
	size_t* newLinePos
) const {

	const string value = m_type + "/" + m_subType;

	if (curLinePos + value.length() > ctx.getMaxLineLength()) {

		os << NEW_LINE_SEQUENCE;
		os << value;

		if (newLinePos) {
			*newLinePos = NEW_LINE_SEQUENCE_LENGTH + value.length();
		}

	} else {

		os << value;

		if (newLinePos) {
			*newLinePos = curLinePos + value.length();
		}
	}
}
void messageIdSequence::generateImpl
	(const generationContext& ctx, utility::outputStream& os,
	 const size_t curLinePos, size_t* newLinePos) const
{
	size_t pos = curLinePos;

	if (!m_list.empty())
	{
		generationContext tmpCtx(ctx);
		tmpCtx.setMaxLineLength(ctx.getMaxLineLength() - 2);

		for (std::vector <shared_ptr <messageId> >::const_iterator it = m_list.begin() ; ; )
		{
			(*it)->generate(ctx, os, pos, &pos);

			if (++it == m_list.end())
				break;

			os << " ";
			pos++;
		}
	}

	if (newLinePos)
		*newLinePos = pos;
}
text body::getActualEpilogText(const generationContext& ctx) const
{
	const string& epilogText =
		m_epilogText.empty()
			? (isRootPart()
				? ctx.getEpilogText()
				: NULL_STRING
			  )
			: m_epilogText;

	if (epilogText.empty())
		return text();
	else
		return text(epilogText, vmime::charset("us-ascii"));
}
Beispiel #5
0
void messageId::generateImpl
	(const generationContext& ctx, utility::outputStream& os,
	 const size_t curLinePos, size_t* newLinePos) const
{
	size_t pos = curLinePos;

	if (ctx.getWrapMessageId() && (curLinePos + m_left.length() + m_right.length() + 3 > ctx.getMaxLineLength()))
	{
		os << NEW_LINE_SEQUENCE;
		pos = NEW_LINE_SEQUENCE_LENGTH;
	}

	os << '<' << m_left;
	if (m_right != "") os << '@' << m_right;
	os << '>';

	if (newLinePos)
		*newLinePos = pos + m_left.length() + m_right.length() + 3;
}
Beispiel #6
0
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();
	}
}
Beispiel #7
0
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();

	// For compatibility with implementations that do not understand RFC-2231,
	// also generate a normal "7bit/us-ascii" parameter

	// [By Eugene A. Shatokhin]
	// Note that if both the normal "7bit/us-ascii" value and the extended
	// value are present, the latter can be ignored by mail processing systems.
	// This may lead to annoying problems, for example, with strange names of
	// attachments with all but 7-bit ascii characters removed, etc. To avoid
	// this, I would suggest not to create "7bit/us-ascii" value if the extended
	// value is to be generated.

	// A stream for a temporary storage
	string sevenBitBuffer;
	utility::outputStreamStringAdapter sevenBitStream(sevenBitBuffer);

	string::size_type pos = curLinePos;

	if (pos + name.length() + 10 + value.length() > ctx.getMaxLineLength())
	{
		sevenBitStream << NEW_LINE_SEQUENCE;
		pos = NEW_LINE_SEQUENCE_LENGTH;
	}

	bool needQuoting = false;
	bool needQuotedPrintable = false;
	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 < ctx.getMaxLineLength() - 4) ; ++i, ++valueLength)
	{
		switch (value[i])
		{
		// Characters that need to be quoted _and_ escaped
		case '"':
		case '\\':
		// Other characters that need quoting
		case ' ':
		case '\t':
		case '(':
		case ')':
		case '<':
		case '>':
		case '@':
		case ',':
		case ';':
		case ':':
		case '/':
		case '[':
		case ']':
		case '?':
		case '=':

			needQuoting = true;
			break;

		default:

			if (!parserHelpers::isAscii(value[i]))
			{
				needQuotedPrintable = true;
				needQuoting = true;
			}

			break;
		}
	}

	const bool cutValue = (valueLength != value.length());  // has the value been cut?

	if (needQuoting)
	{
		sevenBitStream << name << "=\"";
		pos += name.length() + 2;
	}
	else
	{
		sevenBitStream << name << "=";
		pos += name.length() + 1;
	}

	// Check whether there is a recommended encoding for this charset.
	// If so, the whole buffer will be encoded. Else, the number of
	// 7-bit (ASCII) bytes in the input will be used to determine if
	// we need to encode the whole buffer.
	encoding recommendedEnc;
	const bool alwaysEncode = m_value->getCharset().getRecommendedEncoding(recommendedEnc);
	bool extended = alwaysEncode;

	if (needQuotedPrintable)
	{
		// Send the name in quoted-printable, so outlook express et.al.
		// will understand the real filename
		size_t oldLen = sevenBitBuffer.length();
		m_value->generate(sevenBitStream);
		pos += sevenBitBuffer.length() - oldLen;
		extended = true;		// also send with RFC-2231 encoding
	}
	else
	{
		// Do not chop off this value, but just add the complete name as one header line.
		for (string::size_type i = 0 ; i < value.length() ; ++i)
		{
			const char_t c = value[i];

			if (/* needQuoting && */ (c == '"' || c == '\\'))  // 'needQuoting' is implicit
			{
				sevenBitStream << '\\' << value[i];  // escape 'x' with '\x'
				pos += 2;
			}
			else if (parserHelpers::isAscii(c))
			{
				sevenBitStream << value[i];
				++pos;
			}
			else
			{
				extended = true;
			}
		}

	} // !needQuotedPrintable

	if (needQuoting)
	{
		sevenBitStream << '"';
		++pos;
	}

#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
	os << sevenBitBuffer;
#endif // !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER

	// Also generate an extended parameter if the value contains 8-bit characters
	// or is too long for a single line
	if (extended || cutValue)
	{

#if VMIME_ALWAYS_GENERATE_7BIT_PARAMETER

		os << ';';
		++pos;

#else // !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER

		// The data output to 'sevenBitBuffer' will be discarded in this case
		pos = curLinePos;

#endif // VMIME_ALWAYS_GENERATE_7BIT_PARAMETER

		/* RFC-2231
		 * ========
		 *
		 * Content-Type: message/external-body; access-type=URL;
		 *    URL*0="ftp://";
		 *    URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"
		 *
		 * Content-Type: application/x-stuff;
		 *    title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A
		 *
		 * Content-Type: application/x-stuff;
		 *    title*0*=us-ascii'en'This%20is%20even%20more%20
		 *    title*1*=%2A%2A%2Afun%2A%2A%2A%20
		 *    title*2="isn't it!"
		 */

		// Check whether there is enough space for the first section:
		// parameter name, section identifier, charset and separators
		// + at least 5 characters for the value
		const string::size_type firstSectionLength =
			  name.length() + 4 /* *0*= */ + 2 /* '' */
			+ m_value->getCharset().getName().length();

		if (pos + firstSectionLength + 5 >= ctx.getMaxLineLength())
		{
			os << NEW_LINE_SEQUENCE;
			pos = NEW_LINE_SEQUENCE_LENGTH;
		}

		// Split text into multiple sections that fit on one line
		int sectionCount = 0;
		std::vector <string> sectionText;

		string currentSection;
		string::size_type currentSectionLength = firstSectionLength;

		for (string::size_type i = 0 ; i < value.length() ; ++i)
		{
			// Check whether we should start a new line (taking into
			// account the next character will be encoded = worst case)
			if (currentSectionLength + 3 >= ctx.getMaxLineLength())
			{
				sectionText.push_back(currentSection);
				sectionCount++;

				currentSection.clear();
				currentSectionLength = NEW_LINE_SEQUENCE_LENGTH
					+ name.length() + 6;
			}

			// Output next character
			const char_t c = value[i];
			bool encode = false;

			switch (c)
			{
			// special characters
			case ' ':
			case '\t':
			case '\r':
			case '\n':
			case '%':
			case '"':
			case ';':
			case ',':
			case '(':
			case ')':
			case '<':
			case '>':
			case '@':
			case ':':
			case '/':
			case '[':
			case ']':
			case '?':
			case '=':

				encode = true;
				break;

			default:

				encode = (!parserHelpers::isPrint(c) ||
				          !parserHelpers::isAscii(c) ||
				          alwaysEncode);

				break;
			}

			if (encode)  // need encoding
			{
				const int h1 = static_cast <unsigned char>(c) / 16;
				const int h2 = static_cast <unsigned char>(c) % 16;

				currentSection += '%';
				currentSection += "0123456789ABCDEF"[h1];
				currentSection += "0123456789ABCDEF"[h2];

				pos += 3;
				currentSectionLength += 3;
			}
			else
			{
				currentSection += value[i];

				++pos;
				++currentSectionLength;
			}
		}

		if (!currentSection.empty())
		{
			sectionText.push_back(currentSection);
			sectionCount++;
		}

		// Output sections
		for (int sectionNumber = 0 ; sectionNumber < sectionCount ; ++sectionNumber)
		{
			os << name;

			if (sectionCount != 1) // no section specifier when only a single one
			{
				os << '*';
				os << sectionNumber;
			}

			os << "*=";

			if (sectionNumber == 0)
			{
				os << m_value->getCharset().getName();
				os << '\'' << /* No language */ '\'';
			}

			os << sectionText[sectionNumber];

			if (sectionNumber + 1 < sectionCount)
			{
				os << ';';
				os << NEW_LINE_SEQUENCE;
				pos = NEW_LINE_SEQUENCE_LENGTH;
			}
		}
	}
#if !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER
	else
	{
		// The value does not contain 8-bit characters and
		// is short enough for a single line.
		// "7bit/us-ascii" will suffice in this case.

		// Output what has been stored in temporary buffer so far
		os << sevenBitBuffer;
	}
#endif // !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER

	if (newLinePos)
		*newLinePos = pos;
}
void body::generateImpl
	(const generationContext& ctx, utility::outputStream& os,
	 const string::size_type /* curLinePos */, string::size_type* newLinePos) const
{
	// MIME-Multipart
	if (getPartCount() != 0)
	{
		string boundary;

		if (m_header.acquire() == NULL)
		{
			boundary = generateRandomBoundaryString();
		}
		else
		{
			try
			{
				ref <const contentTypeField> ctf =
					m_header.acquire()->findField(fields::CONTENT_TYPE)
						.dynamicCast <const contentTypeField>();

				boundary = ctf->getBoundary();
			}
			catch (exceptions::no_such_field&)
			{
				// Warning: no content-type and no boundary string specified!
				boundary = generateRandomBoundaryString();
			}
			catch (exceptions::no_such_parameter&)
			{
				// Warning: no boundary string specified!
				boundary = generateRandomBoundaryString();
			}
		}

		const text prologText = getActualPrologText(ctx);
		const text epilogText = getActualEpilogText(ctx);

		if (!prologText.isEmpty())
		{
			prologText.encodeAndFold(ctx, os, 0,
				NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);

			os << CRLF;
		}

		os << "--" << boundary;

		for (size_t p = 0 ; p < getPartCount() ; ++p)
		{
			os << CRLF;

			getPartAt(p)->generate(ctx, os, 0);

			os << CRLF << "--" << boundary;
		}

		os << "--" << CRLF;

		if (!epilogText.isEmpty())
		{
			epilogText.encodeAndFold(ctx, os, 0,
				NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);

			os << CRLF;
		}

		if (newLinePos)
			*newLinePos = 0;
	}
	// Simple body
	else
	{
		// Generate the contents
		ref <contentHandler> contents = m_contents->clone();
		contents->setContentTypeHint(getContentType());

		contents->generate(os, getEncoding(), ctx.getMaxLineLength());
	}
}
Beispiel #9
0
void body::generateImpl
	(const generationContext& ctx, utility::outputStream& os,
	 const size_t /* curLinePos */, size_t* newLinePos) const
{
	// MIME-Multipart
	if (getPartCount() != 0)
	{
		string boundary;

		if (!m_part)
		{
			boundary = generateRandomBoundaryString();
		}
		else
		{
			// Use current boundary string, if specified. If no "Content-Type" field is
			// present, or the boundary is not specified, generate a random one
			shared_ptr <contentTypeField> ctf =
				m_part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);

			if (ctf)
			{
				if (ctf->hasBoundary())
				{
					boundary = ctf->getBoundary();
				}
				else
				{
					// No boundary string specified
					boundary = generateRandomBoundaryString();
				}
			}
			else
			{
				// No Content-Type (and no boundary string specified)
				boundary = generateRandomBoundaryString();
			}
		}

		const text prologText = getActualPrologText(ctx);
		const text epilogText = getActualEpilogText(ctx);

		if (!prologText.isEmpty())
		{
			prologText.encodeAndFold(ctx, os, 0,
				NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);

			os << CRLF;
		}

		os << "--" << boundary;

		for (size_t p = 0 ; p < getPartCount() ; ++p)
		{
			os << CRLF;

			getPartAt(p)->generate(ctx, os, 0);

			os << CRLF << "--" << boundary;
		}

		os << "--" << CRLF;

		if (!epilogText.isEmpty())
		{
			epilogText.encodeAndFold(ctx, os, 0,
				NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);

			os << CRLF;
		}

		if (newLinePos)
			*newLinePos = 0;
	}
	// Simple body
	else
	{
		// Generate the contents
		shared_ptr <contentHandler> contents = m_contents->clone();
		contents->setContentTypeHint(getContentType());

		contents->generate(os, getEncoding(), ctx.getMaxLineLength());
	}
}