aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vmime/net/smtp/SMTPCommand.cpp3
-rw-r--r--src/vmime/net/smtp/SMTPTransport.cpp34
-rw-r--r--src/vmime/net/smtp/SMTPTransport.hpp2
-rw-r--r--tests/net/smtp/SMTPTransportTest.cpp72
-rw-r--r--tests/net/smtp/SMTPTransportTestUtils.hpp182
-rw-r--r--tests/testUtils.cpp14
6 files changed, 304 insertions, 3 deletions
diff --git a/src/vmime/net/smtp/SMTPCommand.cpp b/src/vmime/net/smtp/SMTPCommand.cpp
index 27c8ec1b..04c78722 100644
--- a/src/vmime/net/smtp/SMTPCommand.cpp
+++ b/src/vmime/net/smtp/SMTPCommand.cpp
@@ -153,6 +153,9 @@ shared_ptr <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox, const bool utf8)
cmd << ">";
+ if (utf8)
+ cmd << " SMTPUTF8";
+
return createCommand(cmd.str());
}
diff --git a/src/vmime/net/smtp/SMTPTransport.cpp b/src/vmime/net/smtp/SMTPTransport.cpp
index 2af6a195..fb9ea9aa 100644
--- a/src/vmime/net/smtp/SMTPTransport.cpp
+++ b/src/vmime/net/smtp/SMTPTransport.cpp
@@ -152,6 +152,22 @@ void SMTPTransport::noop()
}
+// static
+bool SMTPTransport::mailboxNeedsUTF8(const mailbox& mb)
+{
+ bool all7bit =
+ utility::stringUtils::is7bit(mb.getEmail().getLocalName().getBuffer())
+ && utility::stringUtils::is7bit(mb.getEmail().getDomainName().getBuffer());
+
+ for (size_t i = 0, n = mb.getName().getWordCount() ; all7bit && i != n ; ++i)
+ {
+ all7bit = utility::stringUtils::is7bit(mb.getName().getWordAt(i)->getBuffer());
+ }
+
+ return !all7bit;
+}
+
+
void SMTPTransport::sendEnvelope
(const mailbox& expeditor, const mailboxList& recipients,
const mailbox& sender, bool sendDATACommand,
@@ -181,9 +197,21 @@ void SMTPTransport::sendEnvelope
const bool hasSize = m_connection->hasExtension("SIZE");
if (!sender.isEmpty())
- commands->addCommand(SMTPCommand::MAIL(sender, hasSMTPUTF8, hasSize ? size : 0));
+ {
+ commands->addCommand(
+ SMTPCommand::MAIL(
+ sender, hasSMTPUTF8 && mailboxNeedsUTF8(sender), hasSize ? size : 0
+ )
+ );
+ }
else
- commands->addCommand(SMTPCommand::MAIL(expeditor, hasSMTPUTF8, hasSize ? size : 0));
+ {
+ commands->addCommand(
+ SMTPCommand::MAIL(
+ expeditor, hasSMTPUTF8 && mailboxNeedsUTF8(expeditor), hasSize ? size : 0
+ )
+ );
+ }
// Now, we will need to reset next time
m_needReset = true;
@@ -192,7 +220,7 @@ void SMTPTransport::sendEnvelope
for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i)
{
const mailbox& mbox = *recipients.getMailboxAt(i);
- commands->addCommand(SMTPCommand::RCPT(mbox, hasSMTPUTF8));
+ commands->addCommand(SMTPCommand::RCPT(mbox, hasSMTPUTF8 && mailboxNeedsUTF8(mbox)));
}
// Prepare sending of message data
diff --git a/src/vmime/net/smtp/SMTPTransport.hpp b/src/vmime/net/smtp/SMTPTransport.hpp
index a0f02418..7b266d3d 100644
--- a/src/vmime/net/smtp/SMTPTransport.hpp
+++ b/src/vmime/net/smtp/SMTPTransport.hpp
@@ -91,6 +91,8 @@ public:
private:
+ static bool mailboxNeedsUTF8(const mailbox& mb);
+
/** Send the MAIL and RCPT commands to the server, checking the
* response, and using pipelining if supported by the server.
* Optionally, the DATA command can also be sent.
diff --git a/tests/net/smtp/SMTPTransportTest.cpp b/tests/net/smtp/SMTPTransportTest.cpp
index dd5546cc..d1a31a5a 100644
--- a/tests/net/smtp/SMTPTransportTest.cpp
+++ b/tests/net/smtp/SMTPTransportTest.cpp
@@ -39,6 +39,8 @@ VMIME_TEST_SUITE_BEGIN(SMTPTransportTest)
VMIME_TEST(testChunking)
VMIME_TEST(testSize_Chunking)
VMIME_TEST(testSize_NoChunking)
+ VMIME_TEST(testSMTPUTF8_available)
+ VMIME_TEST(testSMTPUTF8_notAvailable)
VMIME_TEST_LIST_END
@@ -168,5 +170,75 @@ VMIME_TEST_SUITE_BEGIN(SMTPTransportTest)
vmime::net::smtp::SMTPMessageSizeExceedsMaxLimitsException);
}
+ void testSMTPUTF8_available()
+ {
+ vmime::shared_ptr <vmime::net::session> session = vmime::net::session::create();
+
+ vmime::shared_ptr <vmime::net::transport> tr = session->getTransport
+ (vmime::utility::url("smtp://localhost"));
+
+ tr->setSocketFactory(vmime::make_shared <testSocketFactory <UTF8SMTPTestSocket <true> > >());
+ tr->setTimeoutHandlerFactory(vmime::make_shared <testTimeoutHandlerFactory>());
+
+ VASSERT_NO_THROW("Connection", tr->connect());
+
+ vmime::mailbox exp(
+ vmime::emailAddress(
+ vmime::word("expéditeur", vmime::charsets::UTF_8),
+ vmime::word("test.vmime.org")
+ )
+ );
+
+ vmime::mailboxList recips;
+ recips.appendMailbox(vmime::make_shared <vmime::mailbox>("[email protected]"));
+ recips.appendMailbox(vmime::make_shared <vmime::mailbox>("[email protected]"));
+ recips.appendMailbox(vmime::make_shared <vmime::mailbox>(
+ vmime::emailAddress(
+ vmime::word("récepteur", vmime::charsets::UTF_8),
+ vmime::word("test.vmime.org")
+ )
+ ));
+
+ vmime::string data("Message data");
+ vmime::utility::inputStreamStringAdapter is(data);
+
+ tr->send(exp, recips, is, 0);
+ }
+
+ void testSMTPUTF8_notAvailable()
+ {
+ vmime::shared_ptr <vmime::net::session> session = vmime::net::session::create();
+
+ vmime::shared_ptr <vmime::net::transport> tr = session->getTransport
+ (vmime::utility::url("smtp://localhost"));
+
+ tr->setSocketFactory(vmime::make_shared <testSocketFactory <UTF8SMTPTestSocket <false> > >());
+ tr->setTimeoutHandlerFactory(vmime::make_shared <testTimeoutHandlerFactory>());
+
+ VASSERT_NO_THROW("Connection", tr->connect());
+
+ vmime::mailbox exp(
+ vmime::emailAddress(
+ vmime::word("expéditeur", vmime::charsets::UTF_8),
+ vmime::word("test.vmime.org")
+ )
+ );
+
+ vmime::mailboxList recips;
+ recips.appendMailbox(vmime::make_shared <vmime::mailbox>("[email protected]"));
+ recips.appendMailbox(vmime::make_shared <vmime::mailbox>("[email protected]"));
+ recips.appendMailbox(vmime::make_shared <vmime::mailbox>(
+ vmime::emailAddress(
+ vmime::word("récepteur", vmime::charsets::UTF_8),
+ vmime::word("test.vmime.org")
+ )
+ ));
+
+ vmime::string data("Message data");
+ vmime::utility::inputStreamStringAdapter is(data);
+
+ tr->send(exp, recips, is, 0);
+ }
+
VMIME_TEST_SUITE_END
diff --git a/tests/net/smtp/SMTPTransportTestUtils.hpp b/tests/net/smtp/SMTPTransportTestUtils.hpp
index c1224216..cfd830cf 100644
--- a/tests/net/smtp/SMTPTransportTestUtils.hpp
+++ b/tests/net/smtp/SMTPTransportTestUtils.hpp
@@ -591,3 +591,185 @@ public:
};
typedef SMTPBigTestMessage <4194304> SMTPBigTestMessage4MB;
+
+
+
+/** SMTP test server for SMTPUTF8 extension.
+ */
+template <bool SUPPORTS_UTF8>
+class UTF8SMTPTestSocket : public lineBasedTestSocket
+{
+public:
+
+ UTF8SMTPTestSocket()
+ {
+ if (SUPPORTS_UTF8)
+ {
+ m_rcptLines.insert("RCPT TO:<[email protected]>");
+ m_rcptLines.insert("RCPT TO:<[email protected]>");
+ m_rcptLines.insert("RCPT TO:<ré[email protected]> SMTPUTF8");
+ }
+ else
+ {
+ m_rcptLines.insert("RCPT TO:<[email protected]>");
+ m_rcptLines.insert("RCPT TO:<[email protected]>");
+ m_rcptLines.insert("RCPT TO:<[email protected]>");
+ }
+
+ m_state = STATE_NOT_CONNECTED;
+ m_ehloSent = m_mailSent = m_rcptSent = m_dataSent = m_quitSent = false;
+ }
+
+ ~UTF8SMTPTestSocket()
+ {
+ }
+
+ 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")
+ {
+ if (SUPPORTS_UTF8)
+ {
+ localSend("250-test.vmime.org\r\n");
+ localSend("250 SMTPUTF8\r\n");
+ }
+ else
+ {
+ localSend("250 test.vmime.org\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("Client must send the EHLO command", m_ehloSent);
+ VASSERT("The MAIL command must be sent only one time", !m_mailSent);
+
+ if (SUPPORTS_UTF8)
+ {
+ VASSERT_EQ("MAIL", std::string("MAIL FROM:<expé[email protected]> SMTPUTF8"), line);
+ }
+ else
+ {
+ VASSERT_EQ("MAIL", std::string("MAIL FROM:<[email protected]>"), line);
+ }
+
+ localSend("250 OK\r\n");
+
+ m_mailSent = true;
+ }
+ else if (cmd == "RCPT")
+ {
+ std::set <vmime::string>::iterator it = m_rcptLines.find(line);
+
+ VASSERT(std::string("RCPT not found: '") + line + "'", it != m_rcptLines.end());
+
+ m_rcptLines.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_rcptLines.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_rcptLines;
+
+ std::string m_msgData;
+
+ bool m_ehloSent, m_mailSent, m_rcptSent, m_dataSent, m_quitSent;
+};
diff --git a/tests/testUtils.cpp b/tests/testUtils.cpp
index 0eb2bfd4..1e8e0c99 100644
--- a/tests/testUtils.cpp
+++ b/tests/testUtils.cpp
@@ -26,6 +26,11 @@
#include "vmime/utility/stringUtils.hpp"
#include <cstring>
+#include <iostream>
+
+
+// Enable to output socket send/receive on standard output
+#define DEBUG_SOCKET_IN_OUT 0
@@ -156,6 +161,11 @@ vmime::size_t testSocket::sendRawNonBlocking(const vmime::byte_t* buffer, const
void testSocket::localSend(const vmime::string& buffer)
{
m_inBuffer += buffer;
+
+#if DEBUG_SOCKET_IN_OUT
+ std::cout << "> " << vmime::utility::stringUtils::trim(buffer) << std::endl;
+#endif // DEBUG_SOCKET_IN_OUT
+
}
@@ -232,6 +242,10 @@ void lineBasedTestSocket::onDataReceived()
if (!line.empty() && line[line.length() - 1] == '\r')
line.erase(line.end() - 1, line.end());
+#if DEBUG_SOCKET_IN_OUT
+ std::cout << "< " << vmime::utility::stringUtils::trim(line) << std::endl;
+#endif // DEBUG_SOCKET_IN_OUT
+
m_lines.push_back(line);
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + eol + 1);
}