Fixed #186: use SMTPUTF8 only when needed.
This commit is contained in:
parent
eb5d9db693
commit
1592cccb61
@ -153,6 +153,9 @@ shared_ptr <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox, const bool utf8)
|
||||
|
||||
cmd << ">";
|
||||
|
||||
if (utf8)
|
||||
cmd << " SMTPUTF8";
|
||||
|
||||
return createCommand(cmd.str());
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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>("recipient1@test.vmime.org"));
|
||||
recips.appendMailbox(vmime::make_shared <vmime::mailbox>("recipient2@test.vmime.org"));
|
||||
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>("recipient1@test.vmime.org"));
|
||||
recips.appendMailbox(vmime::make_shared <vmime::mailbox>("recipient2@test.vmime.org"));
|
||||
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
|
||||
|
||||
|
@ -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:<recipient1@test.vmime.org>");
|
||||
m_rcptLines.insert("RCPT TO:<recipient2@test.vmime.org>");
|
||||
m_rcptLines.insert("RCPT TO:<récepteur@test.vmime.org> SMTPUTF8");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rcptLines.insert("RCPT TO:<recipient1@test.vmime.org>");
|
||||
m_rcptLines.insert("RCPT TO:<recipient2@test.vmime.org>");
|
||||
m_rcptLines.insert("RCPT TO:<=?utf-8?Q?r=C3=A9cepteur?=@test.vmime.org>");
|
||||
}
|
||||
|
||||
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éditeur@test.vmime.org> SMTPUTF8"), line);
|
||||
}
|
||||
else
|
||||
{
|
||||
VASSERT_EQ("MAIL", std::string("MAIL FROM:<=?utf-8?Q?exp=C3=A9diteur?=@test.vmime.org>"), 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;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user