aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2013-06-24 13:32:40 +0000
committerVincent Richard <[email protected]>2013-06-24 13:32:40 +0000
commit895b07cae9741f44a1272b2f3875f8dd94763222 (patch)
tree6532de59e01676c78b423b5de5ebc1411c7da111 /tests
parentReturn after sending message when sending is supported. (diff)
downloadvmime-895b07cae9741f44a1272b2f3875f8dd94763222.tar.gz
vmime-895b07cae9741f44a1272b2f3875f8dd94763222.zip
Added support for SIZE SMTP extension (RFC-1870).
Diffstat (limited to 'tests')
-rw-r--r--tests/net/smtp/SMTPCommandTest.cpp20
-rw-r--r--tests/net/smtp/SMTPTransportTest.cpp442
-rw-r--r--tests/net/smtp/SMTPTransportTestUtils.hpp615
-rw-r--r--tests/parser/messageTest.cpp57
-rw-r--r--tests/utility/encoder/b64EncoderTest.cpp8
-rw-r--r--tests/utility/encoder/encoderTestUtils.hpp22
-rw-r--r--tests/utility/encoder/qpEncoderTest.cpp8
7 files changed, 758 insertions, 414 deletions
diff --git a/tests/net/smtp/SMTPCommandTest.cpp b/tests/net/smtp/SMTPCommandTest.cpp
index ce9e7ce5..9a2c90fc 100644
--- a/tests/net/smtp/SMTPCommandTest.cpp
+++ b/tests/net/smtp/SMTPCommandTest.cpp
@@ -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("[email protected]"), false, 123456789);
+
+ VASSERT_NOT_NULL("Not null", cmd);
+ VASSERT_EQ("Text", "MAIL FROM:<[email protected]> 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("[email protected]"), false);
diff --git a/tests/net/smtp/SMTPTransportTest.cpp b/tests/net/smtp/SMTPTransportTest.cpp
index c91ffe90..70aaa4fd 100644
--- a/tests/net/smtp/SMTPTransportTest.cpp
+++ b/tests/net/smtp/SMTPTransportTest.cpp
@@ -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);
}
-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()
+ void testSize_Chunking()
{
- m_recipients.insert("[email protected]");
- m_recipients.insert("[email protected]");
- m_recipients.insert("[email protected]");
-
- 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:<[email protected]>"), 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;
-};
-
+ vmime::ref <vmime::net::session> session =
+ vmime::create <vmime::net::session>();
+ vmime::ref <vmime::net::transport> tr = session->getTransport
+ (vmime::utility::url("smtp://localhost"));
-/** SMTP test server 2.
- *
- * Test CHUNKING extension/BDAT command.
- */
-class chunkingSMTPTestSocket : public testSocket
-{
-public:
+ tr->setSocketFactory(vmime::create <testSocketFactory <bigMessageSMTPTestSocket <true> > >());
+ tr->setTimeoutHandlerFactory(vmime::create <testTimeoutHandlerFactory>());
- chunkingSMTPTestSocket()
- {
- m_state = STATE_NOT_CONNECTED;
- m_bdatChunkCount = 0;
- m_ehloSent = m_mailSent = m_rcptSent = m_quitSent = false;
- }
+ tr->connect();
- ~chunkingSMTPTestSocket()
- {
- VASSERT_EQ("BDAT chunk count", 3, m_bdatChunkCount);
- VASSERT("Client must send the QUIT command", m_quitSent);
- }
+ VASSERT("Test server should report it supports the SIZE extension!",
+ tr.dynamicCast <vmime::net::smtp::SMTPTransport>()->getConnection()->hasExtension("SIZE"));
- void onConnected()
- {
- localSend("220 test.vmime.org Service ready\r\n");
- processCommand();
+ vmime::mailbox exp("[email protected]");
- m_state = STATE_COMMAND;
- }
+ vmime::mailboxList recips;
+ recips.appendMailbox(vmime::create <vmime::mailbox>("[email protected]"));
- 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();
- }
+ vmime::ref <vmime::message> msg = vmime::create <SMTPBigTestMessage4MB>();
- 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();
+ VASSERT_THROW("Connection", tr->send(msg, exp, recips),
+ vmime::exceptions::message_size_exceeds_max_limits);
}
-private:
-
- enum State
+ void testSize_NoChunking()
{
- STATE_NOT_CONNECTED,
- STATE_COMMAND,
- STATE_DATA
- };
+ vmime::ref <vmime::net::session> session =
+ vmime::create <vmime::net::session>();
- int m_state;
- int m_bdatChunkCount;
- int m_bdatChunkSize, m_bdatChunkReceived;
+ vmime::ref <vmime::net::transport> tr = session->getTransport
+ (vmime::utility::url("smtp://localhost"));
- bool m_ehloSent, m_mailSent, m_rcptSent, m_quitSent;
-};
+ tr->setSocketFactory(vmime::create <testSocketFactory <bigMessageSMTPTestSocket <false> > >());
+ tr->setTimeoutHandlerFactory(vmime::create <testTimeoutHandlerFactory>());
+ tr->connect();
-class SMTPTestMessage : public vmime::message
-{
-public:
+ VASSERT("Test server should report it supports the SIZE extension!",
+ tr.dynamicCast <vmime::net::smtp::SMTPTransport>()->getConnection()->hasExtension("SIZE"));
- 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;
+ vmime::mailbox exp("[email protected]");
- 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'));
- }
+ vmime::mailboxList recips;
+ recips.appendMailbox(vmime::create <vmime::mailbox>("[email protected]"));
- return chunks;
- }
+ vmime::ref <vmime::message> msg = vmime::create <SMTPBigTestMessage4MB>();
- 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());
- }
+ VASSERT_THROW("Connection", tr->send(msg, exp, recips),
+ vmime::exceptions::message_size_exceeds_max_limits);
}
-};
+VMIME_TEST_SUITE_END
diff --git a/tests/net/smtp/SMTPTransportTestUtils.hpp b/tests/net/smtp/SMTPTransportTestUtils.hpp
new file mode 100644
index 00000000..b74f9783
--- /dev/null
+++ b/tests/net/smtp/SMTPTransportTestUtils.hpp
@@ -0,0 +1,615 @@
+//
+// 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.
+//
+
+
+/** 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("[email protected]");
+ m_recipients.insert("[email protected]");
+ m_recipients.insert("[email protected]");
+
+ 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:<[email protected]>"), 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:<[email protected]>", 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;
diff --git a/tests/parser/messageTest.cpp b/tests/parser/messageTest.cpp
new file mode 100644
index 00000000..56605e39
--- /dev/null
+++ b/tests/parser/messageTest.cpp
@@ -0,0 +1,57 @@
+//
+// 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 "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
+
diff --git a/tests/utility/encoder/b64EncoderTest.cpp b/tests/utility/encoder/b64EncoderTest.cpp
index 589afaba..fa6fd766 100644
--- a/tests/utility/encoder/b64EncoderTest.cpp
+++ b/tests/utility/encoder/b64EncoderTest.cpp
@@ -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());
}
}
diff --git a/tests/utility/encoder/encoderTestUtils.hpp b/tests/utility/encoder/encoderTestUtils.hpp
index dd2484ed..0eb93871 100644
--- a/tests/utility/encoder/encoderTestUtils.hpp
+++ b/tests/utility/encoder/encoderTestUtils.hpp
@@ -22,9 +22,9 @@
//
-// 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())
+// 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 =
vmime::utility::encoder::encoderFactory::getInstance()->create(name);
@@ -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);
diff --git a/tests/utility/encoder/qpEncoderTest.cpp b/tests/utility/encoder/qpEncoderTest.cpp
index 790c5fb3..50183488 100644
--- a/tests/utility/encoder/qpEncoderTest.cpp
+++ b/tests/utility/encoder/qpEncoderTest.cpp
@@ -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());
}
}