Added support for SIZE SMTP extension (RFC-1870).
This commit is contained in:
parent
87f6acb0b5
commit
895b07cae9
@ -80,7 +80,7 @@ libvmime_sources = [
|
||||
'header.cpp', 'header.hpp',
|
||||
'headerFieldFactory.cpp', 'headerFieldFactory.hpp',
|
||||
'headerField.cpp', 'headerField.hpp',
|
||||
'headerFieldValue.hpp',
|
||||
'headerFieldValue.cpp', 'headerFieldValue.hpp',
|
||||
'htmlTextPart.cpp', 'htmlTextPart.hpp',
|
||||
'mailbox.cpp', 'mailbox.hpp',
|
||||
'mailboxField.cpp', 'mailboxField.hpp',
|
||||
@ -352,6 +352,7 @@ libvmime_tests = [
|
||||
|
||||
libvmimetest_common = [
|
||||
'tests/testUtils.hpp',
|
||||
'tests/net/smtp/SMTPTransportTestUtils.hpp',
|
||||
'tests/utility/encoder/encoderTestUtils.hpp'
|
||||
]
|
||||
|
||||
@ -374,6 +375,7 @@ libvmimetest_sources = [
|
||||
'tests/parser/mediaTypeTest.cpp',
|
||||
'tests/parser/messageIdTest.cpp',
|
||||
'tests/parser/messageIdSequenceTest.cpp',
|
||||
'tests/parser/messageTest.cpp',
|
||||
'tests/parser/pathTest.cpp',
|
||||
'tests/parser/parameterTest.cpp',
|
||||
'tests/parser/textTest.cpp',
|
||||
|
118
src/body.cpp
118
src/body.cpp
@ -30,6 +30,7 @@
|
||||
#include "vmime/utility/random.hpp"
|
||||
|
||||
#include "vmime/utility/seekableInputStreamRegionAdapter.hpp"
|
||||
#include "vmime/utility/outputStreamAdapter.hpp"
|
||||
|
||||
#include "vmime/parserHelpers.hpp"
|
||||
|
||||
@ -385,6 +386,40 @@ void body::parseImpl
|
||||
}
|
||||
|
||||
|
||||
text body::getActualPrologText(const generationContext& ctx) const
|
||||
{
|
||||
const string& prologText =
|
||||
m_prologText.empty()
|
||||
? (isRootPart()
|
||||
? ctx.getPrologText()
|
||||
: NULL_STRING
|
||||
)
|
||||
: m_prologText;
|
||||
|
||||
if (prologText.empty())
|
||||
return text();
|
||||
else
|
||||
return text(prologText, vmime::charset("us-ascii"));
|
||||
}
|
||||
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
|
||||
void body::generateImpl
|
||||
(const generationContext& ctx, utility::outputStream& os,
|
||||
const string::size_type /* curLinePos */, string::size_type* newLinePos) const
|
||||
@ -420,27 +455,12 @@ void body::generateImpl
|
||||
}
|
||||
}
|
||||
|
||||
const string& prologText =
|
||||
m_prologText.empty()
|
||||
? (isRootPart()
|
||||
? ctx.getPrologText()
|
||||
: NULL_STRING
|
||||
)
|
||||
: m_prologText;
|
||||
const text prologText = getActualPrologText(ctx);
|
||||
const text epilogText = getActualEpilogText(ctx);
|
||||
|
||||
const string& epilogText =
|
||||
m_epilogText.empty()
|
||||
? (isRootPart()
|
||||
? ctx.getEpilogText()
|
||||
: NULL_STRING
|
||||
)
|
||||
: m_epilogText;
|
||||
|
||||
if (!prologText.empty())
|
||||
if (!prologText.isEmpty())
|
||||
{
|
||||
text prolog(prologText, vmime::charset("us-ascii"));
|
||||
|
||||
prolog.encodeAndFold(ctx, os, 0,
|
||||
prologText.encodeAndFold(ctx, os, 0,
|
||||
NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);
|
||||
|
||||
os << CRLF;
|
||||
@ -459,11 +479,9 @@ void body::generateImpl
|
||||
|
||||
os << "--" << CRLF;
|
||||
|
||||
if (!epilogText.empty())
|
||||
if (!epilogText.isEmpty())
|
||||
{
|
||||
text epilog(epilogText, vmime::charset("us-ascii"));
|
||||
|
||||
epilog.encodeAndFold(ctx, os, 0,
|
||||
epilogText.encodeAndFold(ctx, os, 0,
|
||||
NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);
|
||||
|
||||
os << CRLF;
|
||||
@ -481,6 +499,60 @@ void body::generateImpl
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type body::getGeneratedSize(const generationContext& ctx)
|
||||
{
|
||||
// MIME-Multipart
|
||||
if (getPartCount() != 0)
|
||||
{
|
||||
utility::stream::size_type size = 0;
|
||||
|
||||
// Size of parts and boundaries
|
||||
for (size_t p = 0 ; p < getPartCount() ; ++p)
|
||||
{
|
||||
size += 100; // boundary, CRLF...
|
||||
size += getPartAt(p)->getGeneratedSize(ctx);
|
||||
}
|
||||
|
||||
// Size of prolog/epilog text
|
||||
const text prologText = getActualPrologText(ctx);
|
||||
|
||||
if (!prologText.isEmpty())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
utility::outputStreamAdapter osa(oss);
|
||||
|
||||
prologText.encodeAndFold(ctx, osa, 0,
|
||||
NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);
|
||||
|
||||
size += oss.str().size();
|
||||
}
|
||||
|
||||
const text epilogText = getActualEpilogText(ctx);
|
||||
|
||||
if (!epilogText.isEmpty())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
utility::outputStreamAdapter osa(oss);
|
||||
|
||||
epilogText.encodeAndFold(ctx, osa, 0,
|
||||
NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE);
|
||||
|
||||
size += oss.str().size();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
// Simple body
|
||||
else
|
||||
{
|
||||
ref <utility::encoder::encoder> srcEncoder = m_contents->getEncoding().getEncoder();
|
||||
ref <utility::encoder::encoder> dstEncoder = getEncoding().getEncoder();
|
||||
|
||||
return dstEncoder->getEncodedSize(srcEncoder->getDecodedSize(m_contents->getLength()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
RFC #1521, Page 32:
|
||||
7.2.1. Multipart: The common syntax
|
||||
|
@ -82,6 +82,12 @@ void bodyPart::generateImpl
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type bodyPart::getGeneratedSize(const generationContext& ctx)
|
||||
{
|
||||
return m_header->getGeneratedSize(ctx) + 2 /* CRLF */ + m_body->getGeneratedSize(ctx);
|
||||
}
|
||||
|
||||
|
||||
ref <component> bodyPart::clone() const
|
||||
{
|
||||
ref <bodyPart> p = vmime::create <bodyPart>();
|
||||
|
@ -233,5 +233,17 @@ void component::setParsedBounds(const string::size_type start, const string::siz
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type component::getGeneratedSize(const generationContext& ctx)
|
||||
{
|
||||
std::vector <ref <component> > children = getChildComponents();
|
||||
utility::stream::size_type totalSize = 0;
|
||||
|
||||
for (std::vector <ref <component> >::iterator it = children.begin() ; it != children.end() ; ++it)
|
||||
totalSize += (*it)->getGeneratedSize(ctx);
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
|
||||
} // vmime
|
||||
|
||||
|
@ -636,6 +636,34 @@ exception* invalid_folder_name::clone() const { return new invalid_folder_name(*
|
||||
const char* invalid_folder_name::name() const throw() { return "invalid_folder_name"; }
|
||||
|
||||
|
||||
//
|
||||
// message_size_exceeds_max_limits
|
||||
//
|
||||
|
||||
message_size_exceeds_max_limits::~message_size_exceeds_max_limits() throw() {}
|
||||
message_size_exceeds_max_limits::message_size_exceeds_max_limits(const string& error, const exception& other)
|
||||
: net_exception(error.empty()
|
||||
? "Transport error: message size exceeds maximum server limits (permanent error)."
|
||||
: error , other) {}
|
||||
|
||||
exception* message_size_exceeds_max_limits::clone() const { return new message_size_exceeds_max_limits(*this); }
|
||||
const char* message_size_exceeds_max_limits::name() const throw() { return "message_size_exceeds_max_limits"; }
|
||||
|
||||
|
||||
//
|
||||
// message_size_exceeds_cur_limits
|
||||
//
|
||||
|
||||
message_size_exceeds_cur_limits::~message_size_exceeds_cur_limits() throw() {}
|
||||
message_size_exceeds_cur_limits::message_size_exceeds_cur_limits(const string& error, const exception& other)
|
||||
: net_exception(error.empty()
|
||||
? "Transport error: message size exceeds current server limits (temporary storage error)."
|
||||
: error, other) {}
|
||||
|
||||
exception* message_size_exceeds_cur_limits::clone() const { return new message_size_exceeds_cur_limits(*this); }
|
||||
const char* message_size_exceeds_cur_limits::name() const throw() { return "message_size_exceeds_cur_limits"; }
|
||||
|
||||
|
||||
#endif // VMIME_HAVE_MESSAGING_FEATURES
|
||||
|
||||
|
||||
|
@ -102,6 +102,12 @@ void header::generateImpl
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type header::getGeneratedSize(const generationContext& ctx)
|
||||
{
|
||||
return component::getGeneratedSize(ctx) + 2 * m_fields.size() /* CRLF */;
|
||||
}
|
||||
|
||||
|
||||
ref <component> header::clone() const
|
||||
{
|
||||
ref <header> hdr = vmime::create <header>();
|
||||
|
@ -297,6 +297,12 @@ void headerField::generateImpl
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type headerField::getGeneratedSize(const generationContext& ctx)
|
||||
{
|
||||
return m_name.length() + 2 /* ": " */ + m_value->getGeneratedSize(ctx);
|
||||
}
|
||||
|
||||
|
||||
const string headerField::getName() const
|
||||
{
|
||||
return m_name;
|
||||
|
44
src/headerFieldValue.cpp
Normal file
44
src/headerFieldValue.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// VMime library (http://www.vmime.org)
|
||||
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
|
||||
//
|
||||
// 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/headerFieldValue.hpp"
|
||||
|
||||
#include "vmime/utility/outputStreamAdapter.hpp"
|
||||
|
||||
|
||||
namespace vmime
|
||||
{
|
||||
|
||||
|
||||
utility::stream::size_type headerFieldValue::getGeneratedSize(const generationContext& ctx)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
utility::outputStreamAdapter osa(oss);
|
||||
|
||||
generate(ctx, osa);
|
||||
|
||||
return oss.str().length();
|
||||
}
|
||||
|
||||
|
||||
} // vmime
|
@ -88,6 +88,13 @@ ref <SMTPCommand> SMTPCommand::STARTTLS()
|
||||
|
||||
// static
|
||||
ref <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8)
|
||||
{
|
||||
return MAIL(mbox, utf8, 0);
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
ref <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, const unsigned long size)
|
||||
{
|
||||
std::ostringstream cmd;
|
||||
cmd.imbue(std::locale::classic());
|
||||
@ -108,6 +115,9 @@ ref <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8)
|
||||
if (utf8)
|
||||
cmd << " SMTPUTF8";
|
||||
|
||||
if (size != 0)
|
||||
cmd << " SIZE=" << size;
|
||||
|
||||
return createCommand(cmd.str());
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,8 @@ void SMTPTransport::noop()
|
||||
|
||||
void SMTPTransport::sendEnvelope
|
||||
(const mailbox& expeditor, const mailboxList& recipients,
|
||||
const mailbox& sender, bool sendDATACommand)
|
||||
const mailbox& sender, bool sendDATACommand,
|
||||
const utility::stream::size_type size)
|
||||
{
|
||||
// If no recipient/expeditor was found, throw an exception
|
||||
if (recipients.isEmpty())
|
||||
@ -179,11 +180,12 @@ void SMTPTransport::sendEnvelope
|
||||
|
||||
// Emit the "MAIL" command
|
||||
const bool hasSMTPUTF8 = m_connection->hasExtension("SMTPUTF8");
|
||||
const bool hasSize = m_connection->hasExtension("SIZE");
|
||||
|
||||
if (!sender.isEmpty())
|
||||
commands->addCommand(SMTPCommand::MAIL(sender, hasSMTPUTF8));
|
||||
commands->addCommand(SMTPCommand::MAIL(sender, hasSMTPUTF8, hasSize ? size : 0));
|
||||
else
|
||||
commands->addCommand(SMTPCommand::MAIL(expeditor, hasSMTPUTF8));
|
||||
commands->addCommand(SMTPCommand::MAIL(expeditor, hasSMTPUTF8, hasSize ? size : 0));
|
||||
|
||||
// Now, we will need to reset next time
|
||||
m_needReset = true;
|
||||
@ -215,10 +217,28 @@ void SMTPTransport::sendEnvelope
|
||||
commands->writeToSocket(m_connection->getSocket());
|
||||
|
||||
if ((resp = m_connection->readResponse())->getCode() != 250)
|
||||
{
|
||||
// SIZE extension: insufficient system storage
|
||||
if (resp->getCode() == 452)
|
||||
{
|
||||
disconnect();
|
||||
throw exceptions::message_size_exceeds_cur_limits
|
||||
(commands->getLastCommandSent()->getText(), resp->getText());
|
||||
}
|
||||
// SIZE extension: message size exceeds fixed maximum message size
|
||||
else if (resp->getCode() == 552)
|
||||
{
|
||||
disconnect();
|
||||
throw exceptions::message_size_exceeds_max_limits
|
||||
(commands->getLastCommandSent()->getText(), resp->getText());
|
||||
}
|
||||
// Other error
|
||||
else
|
||||
{
|
||||
disconnect();
|
||||
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
|
||||
}
|
||||
}
|
||||
|
||||
// Read responses for "RCPT TO" commands
|
||||
for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i)
|
||||
@ -229,11 +249,29 @@ void SMTPTransport::sendEnvelope
|
||||
|
||||
if (resp->getCode() != 250 &&
|
||||
resp->getCode() != 251)
|
||||
{
|
||||
// SIZE extension: insufficient system storage
|
||||
if (resp->getCode() == 452)
|
||||
{
|
||||
disconnect();
|
||||
throw exceptions::message_size_exceeds_cur_limits
|
||||
(commands->getLastCommandSent()->getText(), resp->getText());
|
||||
}
|
||||
// SIZE extension: message size exceeds fixed maximum message size
|
||||
else if (resp->getCode() == 552)
|
||||
{
|
||||
disconnect();
|
||||
throw exceptions::message_size_exceeds_max_limits
|
||||
(commands->getLastCommandSent()->getText(), resp->getText());
|
||||
}
|
||||
// Other error
|
||||
else
|
||||
{
|
||||
disconnect();
|
||||
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read response for "DATA" command
|
||||
if (sendDATACommand)
|
||||
@ -258,7 +296,7 @@ void SMTPTransport::send
|
||||
throw exceptions::not_connected();
|
||||
|
||||
// Send message envelope
|
||||
sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ true);
|
||||
sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ true, size);
|
||||
|
||||
// Send the message data
|
||||
// Stream copy with "\n." to "\n.." transformation
|
||||
@ -312,7 +350,8 @@ void SMTPTransport::send
|
||||
}
|
||||
|
||||
// Send message envelope
|
||||
sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ false);
|
||||
sendEnvelope(expeditor, recipients, sender,
|
||||
/* sendDATACommand */ false, msg->getGeneratedSize(ctx));
|
||||
|
||||
// Send the message by chunks
|
||||
SMTPChunkingOutputStreamAdapter chunkStream(m_connection);
|
||||
|
@ -304,6 +304,27 @@ utility::stream::size_type b64Encoder::decode(utility::inputStream& in,
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type b64Encoder::getEncodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
const string::size_type propMaxLineLength =
|
||||
getProperties().getProperty <string::size_type>("maxlinelength", static_cast <string::size_type>(-1));
|
||||
|
||||
const bool cutLines = (propMaxLineLength != static_cast <string::size_type>(-1));
|
||||
const string::size_type maxLineLength = std::min(propMaxLineLength, static_cast <string::size_type>(76));
|
||||
|
||||
return (n * 4) / 3 // 3 bytes of input provide 4 bytes of output
|
||||
+ (cutLines ? (n / maxLineLength) * 2 : 0) // CRLF (2 bytes) for each line.
|
||||
+ 4; // padding
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type b64Encoder::getDecodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
// 4 bytes of input provide 3 bytes of output
|
||||
return (n * 3) / 4;
|
||||
}
|
||||
|
||||
|
||||
} // encoder
|
||||
} // utility
|
||||
} // vmime
|
||||
|
@ -70,6 +70,18 @@ utility::stream::size_type defaultEncoder::decode(utility::inputStream& in,
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type defaultEncoder::getEncodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type defaultEncoder::getDecodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
} // encoder
|
||||
} // utility
|
||||
} // vmime
|
||||
|
@ -532,6 +532,27 @@ utility::stream::size_type qpEncoder::decode(utility::inputStream& in,
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type qpEncoder::getEncodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
const string::size_type propMaxLineLength =
|
||||
getProperties().getProperty <string::size_type>("maxlinelength", static_cast <string::size_type>(-1));
|
||||
|
||||
const bool cutLines = (propMaxLineLength != static_cast <string::size_type>(-1));
|
||||
const string::size_type maxLineLength = std::min(propMaxLineLength, static_cast <string::size_type>(74));
|
||||
|
||||
// Worst cast: 1 byte of input provide 3 bytes of output
|
||||
// Count CRLF (2 bytes) for each line.
|
||||
return n * 3 + (cutLines ? (n / maxLineLength) * 2 : 0);
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type qpEncoder::getDecodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
// Worst case: 1 byte of input equals 1 byte of output
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
} // encoder
|
||||
} // utility
|
||||
} // vmime
|
||||
|
@ -326,6 +326,22 @@ utility::stream::size_type uuEncoder::decode(utility::inputStream& in,
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type uuEncoder::getEncodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
// 3 bytes of input provide 4 bytes of output.
|
||||
// Count CRLF (2 bytes) for each line of 45 characters.
|
||||
// Also reserve some space for header and footer.
|
||||
return 200 + n * 3 + (n / 45) * 2;
|
||||
}
|
||||
|
||||
|
||||
utility::stream::size_type uuEncoder::getDecodedSize(const utility::stream::size_type n) const
|
||||
{
|
||||
// 4 bytes of input provide 3 bytes of output
|
||||
return (n * 3) / 4;
|
||||
}
|
||||
|
||||
|
||||
} // encoder
|
||||
} // utility
|
||||
} // vmime
|
||||
|
@ -41,6 +41,8 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandTest)
|
||||
VMIME_TEST(testMAIL)
|
||||
VMIME_TEST(testMAIL_Encoded)
|
||||
VMIME_TEST(testMAIL_UTF8)
|
||||
VMIME_TEST(testMAIL_SIZE)
|
||||
VMIME_TEST(testMAIL_SIZE_UTF8)
|
||||
VMIME_TEST(testRCPT)
|
||||
VMIME_TEST(testRCPT_Encoded)
|
||||
VMIME_TEST(testRCPT_UTF8)
|
||||
@ -127,6 +129,24 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandTest)
|
||||
VASSERT_EQ("Text", "MAIL FROM:<mailtest@例え.テスト> SMTPUTF8", cmd->getText());
|
||||
}
|
||||
|
||||
void testMAIL_SIZE()
|
||||
{
|
||||
vmime::ref <SMTPCommand> cmd = SMTPCommand::MAIL
|
||||
(vmime::mailbox("me@vmime.org"), false, 123456789);
|
||||
|
||||
VASSERT_NOT_NULL("Not null", cmd);
|
||||
VASSERT_EQ("Text", "MAIL FROM:<me@vmime.org> SIZE=123456789", cmd->getText());
|
||||
}
|
||||
|
||||
void testMAIL_SIZE_UTF8()
|
||||
{
|
||||
vmime::ref <SMTPCommand> cmd = SMTPCommand::MAIL
|
||||
(vmime::mailbox(vmime::emailAddress("mailtest", "例え.テスト")), true, 123456789);
|
||||
|
||||
VASSERT_NOT_NULL("Not null", cmd);
|
||||
VASSERT_EQ("Text", "MAIL FROM:<mailtest@例え.テスト> SMTPUTF8 SIZE=123456789", cmd->getText());
|
||||
}
|
||||
|
||||
void testRCPT()
|
||||
{
|
||||
vmime::ref <SMTPCommand> cmd = SMTPCommand::RCPT(vmime::mailbox("someone@vmime.org"), false);
|
||||
|
@ -26,11 +26,7 @@
|
||||
#include "vmime/net/smtp/SMTPTransport.hpp"
|
||||
#include "vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp"
|
||||
|
||||
|
||||
class greetingErrorSMTPTestSocket;
|
||||
class MAILandRCPTSMTPTestSocket;
|
||||
class chunkingSMTPTestSocket;
|
||||
class SMTPTestMessage;
|
||||
#include "SMTPTransportTestUtils.hpp"
|
||||
|
||||
|
||||
VMIME_TEST_SUITE_BEGIN(SMTPTransportTest)
|
||||
@ -39,6 +35,8 @@ VMIME_TEST_SUITE_BEGIN(SMTPTransportTest)
|
||||
VMIME_TEST(testGreetingError)
|
||||
VMIME_TEST(testMAILandRCPT)
|
||||
VMIME_TEST(testChunking)
|
||||
VMIME_TEST(testSize_Chunking)
|
||||
VMIME_TEST(testSize_NoChunking)
|
||||
VMIME_TEST_LIST_END
|
||||
|
||||
|
||||
@ -109,427 +107,59 @@ VMIME_TEST_SUITE_BEGIN(SMTPTransportTest)
|
||||
tr->send(msg, exp, recips);
|
||||
}
|
||||
|
||||
void testSize_Chunking()
|
||||
{
|
||||
vmime::ref <vmime::net::session> session =
|
||||
vmime::create <vmime::net::session>();
|
||||
|
||||
vmime::ref <vmime::net::transport> tr = session->getTransport
|
||||
(vmime::utility::url("smtp://localhost"));
|
||||
|
||||
tr->setSocketFactory(vmime::create <testSocketFactory <bigMessageSMTPTestSocket <true> > >());
|
||||
tr->setTimeoutHandlerFactory(vmime::create <testTimeoutHandlerFactory>());
|
||||
|
||||
tr->connect();
|
||||
|
||||
VASSERT("Test server should report it supports the SIZE extension!",
|
||||
tr.dynamicCast <vmime::net::smtp::SMTPTransport>()->getConnection()->hasExtension("SIZE"));
|
||||
|
||||
vmime::mailbox exp("expeditor@test.vmime.org");
|
||||
|
||||
vmime::mailboxList recips;
|
||||
recips.appendMailbox(vmime::create <vmime::mailbox>("recipient@test.vmime.org"));
|
||||
|
||||
vmime::ref <vmime::message> msg = vmime::create <SMTPBigTestMessage4MB>();
|
||||
|
||||
VASSERT_THROW("Connection", tr->send(msg, exp, recips),
|
||||
vmime::exceptions::message_size_exceeds_max_limits);
|
||||
}
|
||||
|
||||
void testSize_NoChunking()
|
||||
{
|
||||
vmime::ref <vmime::net::session> session =
|
||||
vmime::create <vmime::net::session>();
|
||||
|
||||
vmime::ref <vmime::net::transport> tr = session->getTransport
|
||||
(vmime::utility::url("smtp://localhost"));
|
||||
|
||||
tr->setSocketFactory(vmime::create <testSocketFactory <bigMessageSMTPTestSocket <false> > >());
|
||||
tr->setTimeoutHandlerFactory(vmime::create <testTimeoutHandlerFactory>());
|
||||
|
||||
tr->connect();
|
||||
|
||||
VASSERT("Test server should report it supports the SIZE extension!",
|
||||
tr.dynamicCast <vmime::net::smtp::SMTPTransport>()->getConnection()->hasExtension("SIZE"));
|
||||
|
||||
vmime::mailbox exp("expeditor@test.vmime.org");
|
||||
|
||||
vmime::mailboxList recips;
|
||||
recips.appendMailbox(vmime::create <vmime::mailbox>("recipient@test.vmime.org"));
|
||||
|
||||
vmime::ref <vmime::message> msg = vmime::create <SMTPBigTestMessage4MB>();
|
||||
|
||||
VASSERT_THROW("Connection", tr->send(msg, exp, recips),
|
||||
vmime::exceptions::message_size_exceeds_max_limits);
|
||||
}
|
||||
|
||||
VMIME_TEST_SUITE_END
|
||||
|
||||
|
||||
/** Accepts connection and fails on greeting.
|
||||
*/
|
||||
class greetingErrorSMTPTestSocket : public lineBasedTestSocket
|
||||
{
|
||||
public:
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
localSend("421 test.vmime.org Service not available, closing transmission channel\r\n");
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void processCommand()
|
||||
{
|
||||
if (!haveMoreLines())
|
||||
return;
|
||||
|
||||
getNextLine();
|
||||
|
||||
localSend("502 Command not implemented\r\n");
|
||||
processCommand();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** SMTP test server 1.
|
||||
*
|
||||
* Test send().
|
||||
* Ensure MAIL and RCPT commands are sent correctly.
|
||||
*/
|
||||
class MAILandRCPTSMTPTestSocket : public lineBasedTestSocket
|
||||
{
|
||||
public:
|
||||
|
||||
MAILandRCPTSMTPTestSocket()
|
||||
{
|
||||
m_recipients.insert("recipient1@test.vmime.org");
|
||||
m_recipients.insert("recipient2@test.vmime.org");
|
||||
m_recipients.insert("recipient3@test.vmime.org");
|
||||
|
||||
m_state = STATE_NOT_CONNECTED;
|
||||
m_ehloSent = m_heloSent = m_mailSent = m_rcptSent = m_dataSent = m_quitSent = false;
|
||||
}
|
||||
|
||||
~MAILandRCPTSMTPTestSocket()
|
||||
{
|
||||
VASSERT("Client must send the DATA command", m_dataSent);
|
||||
VASSERT("Client must send the QUIT command", m_quitSent);
|
||||
}
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
localSend("220 test.vmime.org Service ready\r\n");
|
||||
processCommand();
|
||||
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
|
||||
void processCommand()
|
||||
{
|
||||
if (!haveMoreLines())
|
||||
return;
|
||||
|
||||
vmime::string line = getNextLine();
|
||||
std::istringstream iss(line);
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_NOT_CONNECTED:
|
||||
|
||||
localSend("451 Requested action aborted: invalid state\r\n");
|
||||
break;
|
||||
|
||||
case STATE_COMMAND:
|
||||
{
|
||||
std::string cmd;
|
||||
iss >> cmd;
|
||||
|
||||
if (cmd.empty())
|
||||
{
|
||||
localSend("500 Syntax error, command unrecognized\r\n");
|
||||
}
|
||||
else if (cmd == "EHLO")
|
||||
{
|
||||
localSend("502 Command not implemented\r\n");
|
||||
|
||||
m_ehloSent = true;
|
||||
}
|
||||
else if (cmd == "HELO")
|
||||
{
|
||||
VASSERT("Client must send the EHLO command before HELO", m_ehloSent);
|
||||
|
||||
localSend("250 OK\r\n");
|
||||
|
||||
m_heloSent = true;
|
||||
}
|
||||
else if (cmd == "MAIL")
|
||||
{
|
||||
VASSERT("Client must send the HELO command", m_heloSent);
|
||||
VASSERT("The MAIL command must be sent only one time", !m_mailSent);
|
||||
|
||||
VASSERT_EQ("MAIL", std::string("MAIL FROM:<expeditor@test.vmime.org>"), line);
|
||||
|
||||
localSend("250 OK\r\n");
|
||||
|
||||
m_mailSent = true;
|
||||
}
|
||||
else if (cmd == "RCPT")
|
||||
{
|
||||
const vmime::string::size_type lt = line.find('<');
|
||||
const vmime::string::size_type gt = line.find('>');
|
||||
|
||||
VASSERT("RCPT <", lt != vmime::string::npos);
|
||||
VASSERT("RCPT >", gt != vmime::string::npos);
|
||||
VASSERT("RCPT ><", gt >= lt);
|
||||
|
||||
const vmime::string recip = vmime::string
|
||||
(line.begin() + lt + 1, line.begin() + gt);
|
||||
|
||||
std::set <vmime::string>::iterator it =
|
||||
m_recipients.find(recip);
|
||||
|
||||
VASSERT(std::string("Recipient not found: '") + recip + "'",
|
||||
it != m_recipients.end());
|
||||
|
||||
m_recipients.erase(it);
|
||||
|
||||
localSend("250 OK, recipient accepted\r\n");
|
||||
|
||||
m_rcptSent = true;
|
||||
}
|
||||
else if (cmd == "DATA")
|
||||
{
|
||||
VASSERT("Client must send the MAIL command", m_mailSent);
|
||||
VASSERT("Client must send the RCPT command", m_rcptSent);
|
||||
VASSERT("All recipients", m_recipients.empty());
|
||||
|
||||
localSend("354 Ready to accept data; end with <CRLF>.<CRLF>\r\n");
|
||||
|
||||
m_state = STATE_DATA;
|
||||
m_msgData.clear();
|
||||
|
||||
m_dataSent = true;
|
||||
}
|
||||
else if (cmd == "NOOP")
|
||||
{
|
||||
localSend("250 Completed\r\n");
|
||||
}
|
||||
else if (cmd == "QUIT")
|
||||
{
|
||||
m_quitSent = true;
|
||||
|
||||
localSend("221 test.vmime.org Service closing transmission channel\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
localSend("502 Command not implemented\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case STATE_DATA:
|
||||
{
|
||||
if (line == ".")
|
||||
{
|
||||
VASSERT_EQ("Data", "Message data\r\n", m_msgData);
|
||||
|
||||
localSend("250 Message accepted for delivery\r\n");
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_msgData += line + "\r\n";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum State
|
||||
{
|
||||
STATE_NOT_CONNECTED,
|
||||
STATE_COMMAND,
|
||||
STATE_DATA
|
||||
};
|
||||
|
||||
int m_state;
|
||||
|
||||
std::set <vmime::string> m_recipients;
|
||||
|
||||
std::string m_msgData;
|
||||
|
||||
bool m_ehloSent, m_heloSent, m_mailSent, m_rcptSent,
|
||||
m_dataSent, m_quitSent;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** SMTP test server 2.
|
||||
*
|
||||
* Test CHUNKING extension/BDAT command.
|
||||
*/
|
||||
class chunkingSMTPTestSocket : public testSocket
|
||||
{
|
||||
public:
|
||||
|
||||
chunkingSMTPTestSocket()
|
||||
{
|
||||
m_state = STATE_NOT_CONNECTED;
|
||||
m_bdatChunkCount = 0;
|
||||
m_ehloSent = m_mailSent = m_rcptSent = m_quitSent = false;
|
||||
}
|
||||
|
||||
~chunkingSMTPTestSocket()
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk count", 3, m_bdatChunkCount);
|
||||
VASSERT("Client must send the QUIT command", m_quitSent);
|
||||
}
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
localSend("220 test.vmime.org Service ready\r\n");
|
||||
processCommand();
|
||||
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
|
||||
void onDataReceived()
|
||||
{
|
||||
if (m_state == STATE_DATA)
|
||||
{
|
||||
if (m_bdatChunkReceived != m_bdatChunkSize)
|
||||
{
|
||||
const size_type remaining = m_bdatChunkSize - m_bdatChunkReceived;
|
||||
const size_type received = localReceiveRaw(NULL, remaining);
|
||||
|
||||
m_bdatChunkReceived += received;
|
||||
}
|
||||
|
||||
if (m_bdatChunkReceived == m_bdatChunkSize)
|
||||
{
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
void processCommand()
|
||||
{
|
||||
vmime::string line;
|
||||
|
||||
if (!localReceiveLine(line))
|
||||
return;
|
||||
|
||||
std::istringstream iss(line);
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_NOT_CONNECTED:
|
||||
|
||||
localSend("451 Requested action aborted: invalid state\r\n");
|
||||
break;
|
||||
|
||||
case STATE_COMMAND:
|
||||
{
|
||||
std::string cmd;
|
||||
iss >> cmd;
|
||||
|
||||
if (cmd == "EHLO")
|
||||
{
|
||||
localSend("250-test.vmime.org says hello\r\n");
|
||||
localSend("250 CHUNKING\r\n");
|
||||
|
||||
m_ehloSent = true;
|
||||
}
|
||||
else if (cmd == "HELO")
|
||||
{
|
||||
VASSERT("Client must not send the HELO command, as EHLO succeeded", false);
|
||||
}
|
||||
else if (cmd == "MAIL")
|
||||
{
|
||||
VASSERT("The MAIL command must be sent only one time", !m_mailSent);
|
||||
|
||||
localSend("250 OK\r\n");
|
||||
|
||||
m_mailSent = true;
|
||||
}
|
||||
else if (cmd == "RCPT")
|
||||
{
|
||||
localSend("250 OK, recipient accepted\r\n");
|
||||
|
||||
m_rcptSent = true;
|
||||
}
|
||||
else if (cmd == "DATA")
|
||||
{
|
||||
VASSERT("BDAT must be used here!", false);
|
||||
}
|
||||
else if (cmd == "BDAT")
|
||||
{
|
||||
VASSERT("Client must send the MAIL command", m_mailSent);
|
||||
VASSERT("Client must send the RCPT command", m_rcptSent);
|
||||
|
||||
unsigned long chunkSize = 0;
|
||||
iss >> chunkSize;
|
||||
|
||||
std::string last;
|
||||
iss >> last;
|
||||
|
||||
if (m_bdatChunkCount == 0)
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk1 size", 262144, chunkSize);
|
||||
VASSERT_EQ("BDAT chunk1 last", "", last);
|
||||
}
|
||||
else if (m_bdatChunkCount == 1)
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk2 size", 262144, chunkSize);
|
||||
VASSERT_EQ("BDAT chunk2 last", "", last);
|
||||
}
|
||||
else if (m_bdatChunkCount == 2)
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk3 size", 4712, chunkSize);
|
||||
VASSERT_EQ("BDAT chunk3 last", "LAST", last);
|
||||
}
|
||||
else
|
||||
{
|
||||
VASSERT("No more BDAT command should be issued!", false);
|
||||
}
|
||||
|
||||
m_bdatChunkSize = chunkSize;
|
||||
m_bdatChunkReceived = 0;
|
||||
m_bdatChunkCount++;
|
||||
m_state = STATE_DATA;
|
||||
|
||||
localSend("250 chunk received\r\n");
|
||||
}
|
||||
else if (cmd == "NOOP")
|
||||
{
|
||||
localSend("250 Completed\r\n");
|
||||
}
|
||||
else if (cmd == "QUIT")
|
||||
{
|
||||
localSend("221 test.vmime.org Service closing transmission channel\r\n");
|
||||
|
||||
m_quitSent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
localSend("502 Command not implemented\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum State
|
||||
{
|
||||
STATE_NOT_CONNECTED,
|
||||
STATE_COMMAND,
|
||||
STATE_DATA
|
||||
};
|
||||
|
||||
int m_state;
|
||||
int m_bdatChunkCount;
|
||||
int m_bdatChunkSize, m_bdatChunkReceived;
|
||||
|
||||
bool m_ehloSent, m_mailSent, m_rcptSent, m_quitSent;
|
||||
};
|
||||
|
||||
|
||||
class SMTPTestMessage : public vmime::message
|
||||
{
|
||||
public:
|
||||
|
||||
vmime::utility::stream::size_type getChunkBufferSize() const
|
||||
{
|
||||
static vmime::net::smtp::SMTPChunkingOutputStreamAdapter chunkStream(NULL);
|
||||
return chunkStream.getBlockSize();
|
||||
}
|
||||
|
||||
const std::vector <vmime::string>& getChunks() const
|
||||
{
|
||||
static std::vector <vmime::string> chunks;
|
||||
|
||||
if (chunks.size() == 0)
|
||||
{
|
||||
chunks.push_back(vmime::string(1000, 'A'));
|
||||
chunks.push_back(vmime::string(3000, 'B'));
|
||||
chunks.push_back(vmime::string(500000, 'C'));
|
||||
chunks.push_back(vmime::string(25000, 'D'));
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
void generateImpl(const vmime::generationContext& /* ctx */,
|
||||
vmime::utility::outputStream& outputStream,
|
||||
const vmime::string::size_type /* curLinePos */ = 0,
|
||||
vmime::string::size_type* /* newLinePos */ = NULL) const
|
||||
{
|
||||
for (unsigned int i = 0, n = getChunks().size() ; i < n ; ++i)
|
||||
{
|
||||
const vmime::string& chunk = getChunks()[i];
|
||||
outputStream.write(chunk.data(), chunk.size());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
615
tests/net/smtp/SMTPTransportTestUtils.hpp
Normal file
615
tests/net/smtp/SMTPTransportTestUtils.hpp
Normal file
@ -0,0 +1,615 @@
|
||||
//
|
||||
// VMime library (http://www.vmime.org)
|
||||
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
|
||||
/** Accepts connection and fails on greeting.
|
||||
*/
|
||||
class greetingErrorSMTPTestSocket : public lineBasedTestSocket
|
||||
{
|
||||
public:
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
localSend("421 test.vmime.org Service not available, closing transmission channel\r\n");
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void processCommand()
|
||||
{
|
||||
if (!haveMoreLines())
|
||||
return;
|
||||
|
||||
getNextLine();
|
||||
|
||||
localSend("502 Command not implemented\r\n");
|
||||
processCommand();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** SMTP test server 1.
|
||||
*
|
||||
* Test send().
|
||||
* Ensure MAIL and RCPT commands are sent correctly.
|
||||
*/
|
||||
class MAILandRCPTSMTPTestSocket : public lineBasedTestSocket
|
||||
{
|
||||
public:
|
||||
|
||||
MAILandRCPTSMTPTestSocket()
|
||||
{
|
||||
m_recipients.insert("recipient1@test.vmime.org");
|
||||
m_recipients.insert("recipient2@test.vmime.org");
|
||||
m_recipients.insert("recipient3@test.vmime.org");
|
||||
|
||||
m_state = STATE_NOT_CONNECTED;
|
||||
m_ehloSent = m_heloSent = m_mailSent = m_rcptSent = m_dataSent = m_quitSent = false;
|
||||
}
|
||||
|
||||
~MAILandRCPTSMTPTestSocket()
|
||||
{
|
||||
VASSERT("Client must send the DATA command", m_dataSent);
|
||||
VASSERT("Client must send the QUIT command", m_quitSent);
|
||||
}
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
localSend("220 test.vmime.org Service ready\r\n");
|
||||
processCommand();
|
||||
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
|
||||
void processCommand()
|
||||
{
|
||||
if (!haveMoreLines())
|
||||
return;
|
||||
|
||||
vmime::string line = getNextLine();
|
||||
std::istringstream iss(line);
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_NOT_CONNECTED:
|
||||
|
||||
localSend("451 Requested action aborted: invalid state\r\n");
|
||||
break;
|
||||
|
||||
case STATE_COMMAND:
|
||||
{
|
||||
std::string cmd;
|
||||
iss >> cmd;
|
||||
|
||||
if (cmd.empty())
|
||||
{
|
||||
localSend("500 Syntax error, command unrecognized\r\n");
|
||||
}
|
||||
else if (cmd == "EHLO")
|
||||
{
|
||||
localSend("502 Command not implemented\r\n");
|
||||
|
||||
m_ehloSent = true;
|
||||
}
|
||||
else if (cmd == "HELO")
|
||||
{
|
||||
VASSERT("Client must send the EHLO command before HELO", m_ehloSent);
|
||||
|
||||
localSend("250 OK\r\n");
|
||||
|
||||
m_heloSent = true;
|
||||
}
|
||||
else if (cmd == "MAIL")
|
||||
{
|
||||
VASSERT("Client must send the HELO command", m_heloSent);
|
||||
VASSERT("The MAIL command must be sent only one time", !m_mailSent);
|
||||
|
||||
VASSERT_EQ("MAIL", std::string("MAIL FROM:<expeditor@test.vmime.org>"), line);
|
||||
|
||||
localSend("250 OK\r\n");
|
||||
|
||||
m_mailSent = true;
|
||||
}
|
||||
else if (cmd == "RCPT")
|
||||
{
|
||||
const vmime::string::size_type lt = line.find('<');
|
||||
const vmime::string::size_type gt = line.find('>');
|
||||
|
||||
VASSERT("RCPT <", lt != vmime::string::npos);
|
||||
VASSERT("RCPT >", gt != vmime::string::npos);
|
||||
VASSERT("RCPT ><", gt >= lt);
|
||||
|
||||
const vmime::string recip = vmime::string
|
||||
(line.begin() + lt + 1, line.begin() + gt);
|
||||
|
||||
std::set <vmime::string>::iterator it =
|
||||
m_recipients.find(recip);
|
||||
|
||||
VASSERT(std::string("Recipient not found: '") + recip + "'",
|
||||
it != m_recipients.end());
|
||||
|
||||
m_recipients.erase(it);
|
||||
|
||||
localSend("250 OK, recipient accepted\r\n");
|
||||
|
||||
m_rcptSent = true;
|
||||
}
|
||||
else if (cmd == "DATA")
|
||||
{
|
||||
VASSERT("Client must send the MAIL command", m_mailSent);
|
||||
VASSERT("Client must send the RCPT command", m_rcptSent);
|
||||
VASSERT("All recipients", m_recipients.empty());
|
||||
|
||||
localSend("354 Ready to accept data; end with <CRLF>.<CRLF>\r\n");
|
||||
|
||||
m_state = STATE_DATA;
|
||||
m_msgData.clear();
|
||||
|
||||
m_dataSent = true;
|
||||
}
|
||||
else if (cmd == "NOOP")
|
||||
{
|
||||
localSend("250 Completed\r\n");
|
||||
}
|
||||
else if (cmd == "QUIT")
|
||||
{
|
||||
m_quitSent = true;
|
||||
|
||||
localSend("221 test.vmime.org Service closing transmission channel\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
localSend("502 Command not implemented\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case STATE_DATA:
|
||||
{
|
||||
if (line == ".")
|
||||
{
|
||||
VASSERT_EQ("Data", "Message data\r\n", m_msgData);
|
||||
|
||||
localSend("250 Message accepted for delivery\r\n");
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_msgData += line + "\r\n";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum State
|
||||
{
|
||||
STATE_NOT_CONNECTED,
|
||||
STATE_COMMAND,
|
||||
STATE_DATA
|
||||
};
|
||||
|
||||
int m_state;
|
||||
|
||||
std::set <vmime::string> m_recipients;
|
||||
|
||||
std::string m_msgData;
|
||||
|
||||
bool m_ehloSent, m_heloSent, m_mailSent, m_rcptSent,
|
||||
m_dataSent, m_quitSent;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** SMTP test server 2.
|
||||
*
|
||||
* Test CHUNKING extension/BDAT command.
|
||||
*/
|
||||
class chunkingSMTPTestSocket : public testSocket
|
||||
{
|
||||
public:
|
||||
|
||||
chunkingSMTPTestSocket()
|
||||
{
|
||||
m_state = STATE_NOT_CONNECTED;
|
||||
m_bdatChunkCount = 0;
|
||||
m_ehloSent = m_mailSent = m_rcptSent = m_quitSent = false;
|
||||
}
|
||||
|
||||
~chunkingSMTPTestSocket()
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk count", 3, m_bdatChunkCount);
|
||||
VASSERT("Client must send the QUIT command", m_quitSent);
|
||||
}
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
localSend("220 test.vmime.org Service ready\r\n");
|
||||
processCommand();
|
||||
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
|
||||
void onDataReceived()
|
||||
{
|
||||
if (m_state == STATE_DATA)
|
||||
{
|
||||
if (m_bdatChunkReceived != m_bdatChunkSize)
|
||||
{
|
||||
const size_type remaining = m_bdatChunkSize - m_bdatChunkReceived;
|
||||
const size_type received = localReceiveRaw(NULL, remaining);
|
||||
|
||||
m_bdatChunkReceived += received;
|
||||
}
|
||||
|
||||
if (m_bdatChunkReceived == m_bdatChunkSize)
|
||||
{
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
void processCommand()
|
||||
{
|
||||
vmime::string line;
|
||||
|
||||
if (!localReceiveLine(line))
|
||||
return;
|
||||
|
||||
std::istringstream iss(line);
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_NOT_CONNECTED:
|
||||
|
||||
localSend("451 Requested action aborted: invalid state\r\n");
|
||||
break;
|
||||
|
||||
case STATE_COMMAND:
|
||||
{
|
||||
std::string cmd;
|
||||
iss >> cmd;
|
||||
|
||||
if (cmd == "EHLO")
|
||||
{
|
||||
localSend("250-test.vmime.org says hello\r\n");
|
||||
localSend("250 CHUNKING\r\n");
|
||||
|
||||
m_ehloSent = true;
|
||||
}
|
||||
else if (cmd == "HELO")
|
||||
{
|
||||
VASSERT("Client must not send the HELO command, as EHLO succeeded", false);
|
||||
}
|
||||
else if (cmd == "MAIL")
|
||||
{
|
||||
VASSERT("The MAIL command must be sent only one time", !m_mailSent);
|
||||
|
||||
localSend("250 OK\r\n");
|
||||
|
||||
m_mailSent = true;
|
||||
}
|
||||
else if (cmd == "RCPT")
|
||||
{
|
||||
localSend("250 OK, recipient accepted\r\n");
|
||||
|
||||
m_rcptSent = true;
|
||||
}
|
||||
else if (cmd == "DATA")
|
||||
{
|
||||
VASSERT("BDAT must be used here!", false);
|
||||
}
|
||||
else if (cmd == "BDAT")
|
||||
{
|
||||
VASSERT("Client must send the MAIL command", m_mailSent);
|
||||
VASSERT("Client must send the RCPT command", m_rcptSent);
|
||||
|
||||
unsigned long chunkSize = 0;
|
||||
iss >> chunkSize;
|
||||
|
||||
std::string last;
|
||||
iss >> last;
|
||||
|
||||
if (m_bdatChunkCount == 0)
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk1 size", 262144, chunkSize);
|
||||
VASSERT_EQ("BDAT chunk1 last", "", last);
|
||||
}
|
||||
else if (m_bdatChunkCount == 1)
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk2 size", 262144, chunkSize);
|
||||
VASSERT_EQ("BDAT chunk2 last", "", last);
|
||||
}
|
||||
else if (m_bdatChunkCount == 2)
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk3 size", 4712, chunkSize);
|
||||
VASSERT_EQ("BDAT chunk3 last", "LAST", last);
|
||||
}
|
||||
else
|
||||
{
|
||||
VASSERT("No more BDAT command should be issued!", false);
|
||||
}
|
||||
|
||||
m_bdatChunkSize = chunkSize;
|
||||
m_bdatChunkReceived = 0;
|
||||
m_bdatChunkCount++;
|
||||
m_state = STATE_DATA;
|
||||
|
||||
localSend("250 chunk received\r\n");
|
||||
}
|
||||
else if (cmd == "NOOP")
|
||||
{
|
||||
localSend("250 Completed\r\n");
|
||||
}
|
||||
else if (cmd == "QUIT")
|
||||
{
|
||||
localSend("221 test.vmime.org Service closing transmission channel\r\n");
|
||||
|
||||
m_quitSent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
localSend("502 Command not implemented\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum State
|
||||
{
|
||||
STATE_NOT_CONNECTED,
|
||||
STATE_COMMAND,
|
||||
STATE_DATA
|
||||
};
|
||||
|
||||
int m_state;
|
||||
int m_bdatChunkCount;
|
||||
int m_bdatChunkSize, m_bdatChunkReceived;
|
||||
|
||||
bool m_ehloSent, m_mailSent, m_rcptSent, m_quitSent;
|
||||
};
|
||||
|
||||
|
||||
class SMTPTestMessage : public vmime::message
|
||||
{
|
||||
public:
|
||||
|
||||
vmime::utility::stream::size_type getChunkBufferSize() const
|
||||
{
|
||||
static vmime::net::smtp::SMTPChunkingOutputStreamAdapter chunkStream(NULL);
|
||||
return chunkStream.getBlockSize();
|
||||
}
|
||||
|
||||
const std::vector <vmime::string>& getChunks() const
|
||||
{
|
||||
static std::vector <vmime::string> chunks;
|
||||
|
||||
if (chunks.size() == 0)
|
||||
{
|
||||
chunks.push_back(vmime::string(1000, 'A'));
|
||||
chunks.push_back(vmime::string(3000, 'B'));
|
||||
chunks.push_back(vmime::string(500000, 'C'));
|
||||
chunks.push_back(vmime::string(25000, 'D'));
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
void generateImpl(const vmime::generationContext& /* ctx */,
|
||||
vmime::utility::outputStream& outputStream,
|
||||
const vmime::string::size_type /* curLinePos */ = 0,
|
||||
vmime::string::size_type* /* newLinePos */ = NULL) const
|
||||
{
|
||||
for (unsigned int i = 0, n = getChunks().size() ; i < n ; ++i)
|
||||
{
|
||||
const vmime::string& chunk = getChunks()[i];
|
||||
outputStream.write(chunk.data(), chunk.size());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** SMTP test server 3.
|
||||
*
|
||||
* Test SIZE extension.
|
||||
*/
|
||||
template <bool WITH_CHUNKING>
|
||||
class bigMessageSMTPTestSocket : public testSocket
|
||||
{
|
||||
public:
|
||||
|
||||
bigMessageSMTPTestSocket()
|
||||
{
|
||||
m_state = STATE_NOT_CONNECTED;
|
||||
m_bdatChunkCount = 0;
|
||||
m_ehloSent = m_mailSent = m_rcptSent = m_quitSent = false;
|
||||
}
|
||||
|
||||
~bigMessageSMTPTestSocket()
|
||||
{
|
||||
VASSERT_EQ("BDAT chunk count", 3, m_bdatChunkCount);
|
||||
VASSERT("Client must send the QUIT command", m_quitSent);
|
||||
}
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
localSend("220 test.vmime.org Service ready\r\n");
|
||||
processCommand();
|
||||
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
|
||||
void onDataReceived()
|
||||
{
|
||||
if (m_state == STATE_DATA)
|
||||
{
|
||||
if (m_bdatChunkReceived != m_bdatChunkSize)
|
||||
{
|
||||
const size_type remaining = m_bdatChunkSize - m_bdatChunkReceived;
|
||||
const size_type received = localReceiveRaw(NULL, remaining);
|
||||
|
||||
m_bdatChunkReceived += received;
|
||||
}
|
||||
|
||||
if (m_bdatChunkReceived == m_bdatChunkSize)
|
||||
{
|
||||
m_state = STATE_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
void processCommand()
|
||||
{
|
||||
vmime::string line;
|
||||
|
||||
if (!localReceiveLine(line))
|
||||
return;
|
||||
|
||||
std::istringstream iss(line);
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_NOT_CONNECTED:
|
||||
|
||||
localSend("451 Requested action aborted: invalid state\r\n");
|
||||
break;
|
||||
|
||||
case STATE_COMMAND:
|
||||
{
|
||||
std::string cmd;
|
||||
iss >> cmd;
|
||||
|
||||
if (cmd == "EHLO")
|
||||
{
|
||||
localSend("250-test.vmime.org says hello\r\n");
|
||||
|
||||
if (WITH_CHUNKING)
|
||||
localSend("250-CHUNKING\r\n");
|
||||
|
||||
localSend("250 SIZE 1000000\r\n");
|
||||
|
||||
m_ehloSent = true;
|
||||
}
|
||||
else if (cmd == "HELO")
|
||||
{
|
||||
VASSERT("Client must not send the HELO command, as EHLO succeeded", false);
|
||||
}
|
||||
else if (cmd == "MAIL")
|
||||
{
|
||||
VASSERT("The MAIL command must be sent only one time", !m_mailSent);
|
||||
|
||||
std::string address;
|
||||
iss >> address;
|
||||
|
||||
VASSERT_EQ("MAIL/address", "FROM:<expeditor@test.vmime.org>", address);
|
||||
|
||||
std::string option;
|
||||
iss >> option;
|
||||
|
||||
VASSERT_EQ("MAIL/size", "SIZE=4194304", option);
|
||||
|
||||
localSend("552 Channel size limit exceeded\r\n");
|
||||
|
||||
m_mailSent = true;
|
||||
}
|
||||
else if (cmd == "NOOP")
|
||||
{
|
||||
localSend("250 Completed\r\n");
|
||||
}
|
||||
else if (cmd == "QUIT")
|
||||
{
|
||||
localSend("221 test.vmime.org Service closing transmission channel\r\n");
|
||||
|
||||
m_quitSent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
VASSERT("No other command should be sent", false);
|
||||
|
||||
localSend("502 Command not implemented\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processCommand();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum State
|
||||
{
|
||||
STATE_NOT_CONNECTED,
|
||||
STATE_COMMAND,
|
||||
STATE_DATA
|
||||
};
|
||||
|
||||
int m_state;
|
||||
int m_bdatChunkCount;
|
||||
int m_bdatChunkSize, m_bdatChunkReceived;
|
||||
|
||||
bool m_ehloSent, m_mailSent, m_rcptSent, m_quitSent;
|
||||
};
|
||||
|
||||
|
||||
template <unsigned long SIZE>
|
||||
class SMTPBigTestMessage : public vmime::message
|
||||
{
|
||||
public:
|
||||
|
||||
vmime::utility::stream::size_type getGeneratedSize
|
||||
(const vmime::generationContext& /* ctx */)
|
||||
{
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
void generateImpl(const vmime::generationContext& /* ctx */,
|
||||
vmime::utility::outputStream& outputStream,
|
||||
const vmime::string::size_type /* curLinePos */ = 0,
|
||||
vmime::string::size_type* /* newLinePos */ = NULL) const
|
||||
{
|
||||
for (unsigned int i = 0, n = SIZE ; i < n ; ++i)
|
||||
outputStream.write("X", 1);
|
||||
}
|
||||
};
|
||||
|
||||
typedef SMTPBigTestMessage <4194304> SMTPBigTestMessage4MB;
|
57
tests/parser/messageTest.cpp
Normal file
57
tests/parser/messageTest.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
//
|
||||
// VMime library (http://www.vmime.org)
|
||||
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
|
||||
//
|
||||
// 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 "tests/testUtils.hpp"
|
||||
|
||||
|
||||
VMIME_TEST_SUITE_BEGIN(messageTest)
|
||||
|
||||
VMIME_TEST_LIST_BEGIN
|
||||
VMIME_TEST(testGetGeneratedSize)
|
||||
VMIME_TEST_LIST_END
|
||||
|
||||
|
||||
void testGetGeneratedSize()
|
||||
{
|
||||
vmime::generationContext ctx;
|
||||
|
||||
vmime::ref <vmime::message> msg = vmime::create <vmime::message>();
|
||||
msg->getHeader()->getField("Foo")->setValue(vmime::string("bar"));
|
||||
|
||||
vmime::htmlTextPart textPart;
|
||||
textPart.setPlainText(vmime::create <vmime::stringContentHandler>("Foo bar bazé foo foo foo"));
|
||||
textPart.setText(vmime::create <vmime::stringContentHandler>("Foo bar <strong>bazé</strong> foo foo foo"));
|
||||
textPart.generateIn(msg, msg);
|
||||
|
||||
// Estimated/computed generated size must be greater than the actual generated size
|
||||
const unsigned long genSize = msg->getGeneratedSize(ctx);
|
||||
const unsigned long actualSize = msg->generate().length();
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "estimated size (" << genSize << ") >= actual size (" << actualSize << ")";
|
||||
|
||||
VASSERT(oss.str(), genSize >= actualSize);
|
||||
}
|
||||
|
||||
VMIME_TEST_SUITE_END
|
||||
|
@ -138,6 +138,14 @@ VMIME_TEST_SUITE_BEGIN(b64EncoderTest)
|
||||
encode("base64",
|
||||
encode("base64",
|
||||
encode("base64", decoded)))))))));
|
||||
|
||||
VASSERT(oss.str() + "encoded size",
|
||||
getEncoder("base64")->getEncodedSize(decoded.length())
|
||||
>= encode("base64", decoded).length());
|
||||
|
||||
VASSERT(oss.str() + "decoded size",
|
||||
getEncoder("base64")->getDecodedSize(encoded.length())
|
||||
>= decode("base64", encoded).length());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,8 @@
|
||||
//
|
||||
|
||||
|
||||
// Encoding helper function
|
||||
static const vmime::string encode(const vmime::string& name, const vmime::string& in,
|
||||
// Helper function to obtain an encoder given its name
|
||||
static vmime::ref <vmime::utility::encoder::encoder> getEncoder(const vmime::string& name,
|
||||
int maxLineLength = 0, const vmime::propertySet props = vmime::propertySet())
|
||||
{
|
||||
vmime::ref <vmime::utility::encoder::encoder> enc =
|
||||
@ -34,6 +34,16 @@ static const vmime::string encode(const vmime::string& name, const vmime::string
|
||||
if (maxLineLength != 0)
|
||||
enc->getProperties()["maxlinelength"] = maxLineLength;
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
|
||||
// Encoding helper function
|
||||
static const vmime::string encode(const vmime::string& name, const vmime::string& in,
|
||||
int maxLineLength = 0, const vmime::propertySet props = vmime::propertySet())
|
||||
{
|
||||
vmime::ref <vmime::utility::encoder::encoder> enc = getEncoder(name, maxLineLength, props);
|
||||
|
||||
vmime::utility::inputStreamStringAdapter vin(in);
|
||||
|
||||
std::ostringstream out;
|
||||
@ -48,11 +58,7 @@ static const vmime::string encode(const vmime::string& name, const vmime::string
|
||||
// Decoding helper function
|
||||
static const vmime::string decode(const vmime::string& name, const vmime::string& in, int maxLineLength = 0)
|
||||
{
|
||||
vmime::ref <vmime::utility::encoder::encoder> enc =
|
||||
vmime::utility::encoder::encoderFactory::getInstance()->create(name);
|
||||
|
||||
if (maxLineLength != 0)
|
||||
enc->getProperties()["maxlinelength"] = maxLineLength;
|
||||
vmime::ref <vmime::utility::encoder::encoder> enc = getEncoder(name, maxLineLength);
|
||||
|
||||
vmime::utility::inputStreamStringAdapter vin(in);
|
||||
|
||||
|
@ -134,6 +134,14 @@ VMIME_TEST_SUITE_BEGIN(qpEncoderTest)
|
||||
encode("quoted-printable",
|
||||
encode("quoted-printable",
|
||||
encode("quoted-printable", decoded)))))))));
|
||||
|
||||
VASSERT(oss.str() + "encoded size",
|
||||
getEncoder("quoted-printable")->getEncodedSize(decoded.length())
|
||||
>= encode("quoted-printable", decoded).length());
|
||||
|
||||
VASSERT(oss.str() + "decoded size",
|
||||
getEncoder("quoted-printable")->getDecodedSize(encoded.length())
|
||||
>= decode("quoted-printable", encoded).length());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,8 +280,13 @@ public:
|
||||
|
||||
const std::vector <ref <component> > getChildComponents();
|
||||
|
||||
utility::stream::size_type getGeneratedSize(const generationContext& ctx);
|
||||
|
||||
private:
|
||||
|
||||
text getActualPrologText(const generationContext& ctx) const;
|
||||
text getActualEpilogText(const generationContext& ctx) const;
|
||||
|
||||
void setParentPart(ref <bodyPart> parent);
|
||||
|
||||
|
||||
|
@ -103,6 +103,8 @@ public:
|
||||
|
||||
const std::vector <ref <component> > getChildComponents();
|
||||
|
||||
utility::stream::size_type getGeneratedSize(const generationContext& ctx);
|
||||
|
||||
private:
|
||||
|
||||
ref <header> m_header;
|
||||
|
@ -209,6 +209,16 @@ public:
|
||||
*/
|
||||
virtual const std::vector <ref <component> > getChildComponents() = 0;
|
||||
|
||||
/** Get the number of bytes that will be used by this component when
|
||||
* it is generated. This may be a heuristically-derived estimate,
|
||||
* but such an estimated size should always be larger than the actual
|
||||
* generated size.
|
||||
*
|
||||
* @param ctx generation context
|
||||
* @return component size when generated
|
||||
*/
|
||||
virtual utility::stream::size_type getGeneratedSize(const generationContext& ctx);
|
||||
|
||||
protected:
|
||||
|
||||
void setParsedBounds(const utility::stream::size_type start, const utility::stream::size_type end);
|
||||
|
@ -757,6 +757,36 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** Transport error: message size exceeds maximum server limits.
|
||||
*/
|
||||
|
||||
class VMIME_EXPORT message_size_exceeds_max_limits : public net_exception
|
||||
{
|
||||
public:
|
||||
|
||||
message_size_exceeds_max_limits(const string& error = "", const exception& other = NO_EXCEPTION);
|
||||
~message_size_exceeds_max_limits() throw();
|
||||
|
||||
exception* clone() const;
|
||||
const char* name() const throw();
|
||||
};
|
||||
|
||||
|
||||
/** Transport error: message size exceeds current server limits.
|
||||
*/
|
||||
|
||||
class VMIME_EXPORT message_size_exceeds_cur_limits : public net_exception
|
||||
{
|
||||
public:
|
||||
|
||||
message_size_exceeds_cur_limits(const string& error = "", const exception& other = NO_EXCEPTION);
|
||||
~message_size_exceeds_cur_limits() throw();
|
||||
|
||||
exception* clone() const;
|
||||
const char* name() const throw();
|
||||
};
|
||||
|
||||
|
||||
#endif // VMIME_HAVE_MESSAGING_FEATURES
|
||||
|
||||
|
||||
|
@ -230,6 +230,8 @@ public:
|
||||
|
||||
const std::vector <ref <component> > getChildComponents();
|
||||
|
||||
utility::stream::size_type getGeneratedSize(const generationContext& ctx);
|
||||
|
||||
private:
|
||||
|
||||
std::vector <ref <headerField> > m_fields;
|
||||
|
@ -141,6 +141,8 @@ public:
|
||||
const string::size_type end,
|
||||
string::size_type* newPosition = NULL);
|
||||
|
||||
utility::stream::size_type getGeneratedSize(const generationContext& ctx);
|
||||
|
||||
protected:
|
||||
|
||||
void parseImpl
|
||||
|
@ -41,6 +41,7 @@ class VMIME_EXPORT headerFieldValue : public component
|
||||
{
|
||||
public:
|
||||
|
||||
utility::stream::size_type getGeneratedSize(const generationContext& ctx);
|
||||
};
|
||||
|
||||
|
||||
|
@ -64,6 +64,7 @@ public:
|
||||
static ref <SMTPCommand> AUTH(const string& mechName);
|
||||
static ref <SMTPCommand> STARTTLS();
|
||||
static ref <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8);
|
||||
static ref <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const unsigned long size);
|
||||
static ref <SMTPCommand> RCPT(const mailbox& mbox, const bool utf8);
|
||||
static ref <SMTPCommand> RSET();
|
||||
static ref <SMTPCommand> DATA();
|
||||
|
@ -99,12 +99,14 @@ private:
|
||||
* @param recipients list of recipient mailboxes
|
||||
* @param sender envelope sender (if empty, expeditor will be used)
|
||||
* @param sendDATACommand if true, the DATA command will be sent
|
||||
* @param size message size, in bytes (or 0, if not known)
|
||||
*/
|
||||
void sendEnvelope
|
||||
(const mailbox& expeditor,
|
||||
const mailboxList& recipients,
|
||||
const mailbox& sender,
|
||||
bool sendDATACommand);
|
||||
bool sendDATACommand,
|
||||
const utility::stream::size_type size);
|
||||
|
||||
|
||||
ref <SMTPConnection> m_connection;
|
||||
|
@ -58,9 +58,6 @@ public:
|
||||
// encoding/decoding will be performed on generate()/extract()). Note that the
|
||||
// data may be re-encoded (that is, decoded and encoded) if the encoding passed
|
||||
// to generate() is different from this one...
|
||||
//
|
||||
// The 'length' parameter is optional (user-defined). You can pass 0 if you want,
|
||||
// VMime does not make use of it.
|
||||
void setData(const utility::stringProxy& str, const vmime::encoding& enc = NO_ENCODING);
|
||||
void setData(const string& buffer, const vmime::encoding& enc = NO_ENCODING);
|
||||
void setData(const string& buffer, const string::size_type start, const string::size_type end, const vmime::encoding& enc = NO_ENCODING);
|
||||
|
@ -47,6 +47,9 @@ public:
|
||||
|
||||
const std::vector <string> getAvailableProperties() const;
|
||||
|
||||
utility::stream::size_type getEncodedSize(const utility::stream::size_type n) const;
|
||||
utility::stream::size_type getDecodedSize(const utility::stream::size_type n) const;
|
||||
|
||||
protected:
|
||||
|
||||
static const unsigned char sm_alphabet[];
|
||||
|
@ -44,6 +44,9 @@ public:
|
||||
|
||||
utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out, utility::progressListener* progress = NULL);
|
||||
utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out, utility::progressListener* progress = NULL);
|
||||
|
||||
utility::stream::size_type getEncodedSize(const utility::stream::size_type n) const;
|
||||
utility::stream::size_type getDecodedSize(const utility::stream::size_type n) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -91,6 +91,24 @@ public:
|
||||
*/
|
||||
const propertySet& getResults() const;
|
||||
|
||||
/** Return the encoded size for the specified input (decoded) size.
|
||||
* If the size is not exact, it may be an estimate which should always
|
||||
* be larger than the actual encoded size.
|
||||
*
|
||||
* @param n count of input (decoded) bytes
|
||||
* @return count of output (encoded) bytes
|
||||
*/
|
||||
virtual utility::stream::size_type getEncodedSize(const utility::stream::size_type n) const = 0;
|
||||
|
||||
/** Return the encoded size for the specified input (encoded) size.
|
||||
* If the size is not exact, it may be an estimate which should always
|
||||
* be larger than the actual decoded size.
|
||||
*
|
||||
* @param n count of input (encoded) bytes
|
||||
* @return count of output (decoded) bytes
|
||||
*/
|
||||
virtual utility::stream::size_type getDecodedSize(const utility::stream::size_type n) const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
propertySet& getResults();
|
||||
|
@ -50,6 +50,9 @@ public:
|
||||
static bool RFC2047_isEncodingNeededForChar(const unsigned char c);
|
||||
static int RFC2047_getEncodedLength(const unsigned char c);
|
||||
|
||||
utility::stream::size_type getEncodedSize(const utility::stream::size_type n) const;
|
||||
utility::stream::size_type getDecodedSize(const utility::stream::size_type n) const;
|
||||
|
||||
protected:
|
||||
|
||||
static const unsigned char sm_hexDigits[17];
|
||||
|
@ -46,6 +46,9 @@ public:
|
||||
utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out, utility::progressListener* progress = NULL);
|
||||
|
||||
const std::vector <string> getAvailableProperties() const;
|
||||
|
||||
utility::stream::size_type getEncodedSize(const utility::stream::size_type n) const;
|
||||
utility::stream::size_type getDecodedSize(const utility::stream::size_type n) const;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user