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