// // VMime library (http://www.vmime.org) // Copyright (C) 2002-2013 Vincent Richard // // 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" class greetingErrorSMTPTestSocket; class MAILandRCPTSMTPTestSocket; VMIME_TEST_SUITE_BEGIN(SMTPTransportTest) VMIME_TEST_LIST_BEGIN VMIME_TEST(testGreetingError) VMIME_TEST(testMAILandRCPT) VMIME_TEST_LIST_END void testGreetingError() { vmime::ref session = vmime::create (); vmime::ref tr = session->getTransport (vmime::utility::url("smtp://localhost")); tr->setSocketFactory(vmime::create >()); tr->setTimeoutHandlerFactory(vmime::create ()); VASSERT_THROW("Connection", tr->connect(), vmime::exceptions::connection_greeting_error); } void testMAILandRCPT() { vmime::ref session = vmime::create (); vmime::ref tr = session->getTransport (vmime::utility::url("smtp://localhost")); tr->setSocketFactory(vmime::create >()); tr->setTimeoutHandlerFactory(vmime::create ()); VASSERT_NO_THROW("Connection", tr->connect()); vmime::mailbox exp("expeditor@test.vmime.org"); vmime::mailboxList recips; recips.appendMailbox(vmime::create ("recipient1@test.vmime.org")); recips.appendMailbox(vmime::create ("recipient2@test.vmime.org")); recips.appendMailbox(vmime::create ("recipient3@test.vmime.org")); vmime::string data("Message data"); vmime::utility::inputStreamStringAdapter is(data); tr->send(exp, recips, is, 0); } 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; } 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 == "HELO") { localSend("250 OK\r\n"); } else if (cmd == "MAIL") { VASSERT_EQ("MAIL", std::string("MAIL FROM:"), line); localSend("250 OK\r\n"); } 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 ::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"); } else if (cmd == "DATA") { VASSERT("All recipients", m_recipients.empty()); localSend("354 Ready to accept data; end with .\r\n"); m_state = STATE_DATA; m_msgData.clear(); } else if (cmd == "NOOP") { localSend("250 Completed\r\n"); } else if (cmd == "QUIT") { 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 m_recipients; std::string m_msgData; };