diff options
author | Vincent Richard <[email protected]> | 2013-06-24 13:32:40 +0000 |
---|---|---|
committer | Vincent Richard <[email protected]> | 2013-06-24 13:32:40 +0000 |
commit | 895b07cae9741f44a1272b2f3875f8dd94763222 (patch) | |
tree | 6532de59e01676c78b423b5de5ebc1411c7da111 /tests | |
parent | Return after sending message when sending is supported. (diff) | |
download | vmime-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.cpp | 20 | ||||
-rw-r--r-- | tests/net/smtp/SMTPTransportTest.cpp | 442 | ||||
-rw-r--r-- | tests/net/smtp/SMTPTransportTestUtils.hpp | 615 | ||||
-rw-r--r-- | tests/parser/messageTest.cpp | 57 | ||||
-rw-r--r-- | tests/utility/encoder/b64EncoderTest.cpp | 8 | ||||
-rw-r--r-- | tests/utility/encoder/encoderTestUtils.hpp | 22 | ||||
-rw-r--r-- | tests/utility/encoder/qpEncoderTest.cpp | 8 |
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()); } } |