diff options
author | Vincent Richard <[email protected]> | 2013-02-12 15:58:27 +0000 |
---|---|---|
committer | Vincent Richard <[email protected]> | 2013-02-12 15:58:27 +0000 |
commit | 83c5ba96b9e1ae8fc1d4d413aff57d6522b9c184 (patch) | |
tree | c76b03893e920ed7d9745e136cc383ccf53188d3 /src | |
parent | Fixed invalid use of freed memory. (diff) | |
download | vmime-83c5ba96b9e1ae8fc1d4d413aff57d6522b9c184.tar.gz vmime-83c5ba96b9e1ae8fc1d4d413aff57d6522b9c184.zip |
Rewrote SMTP command sending. Better code for pipelining. Report full command text on MAIL/RCPT errors.
Diffstat (limited to 'src')
-rw-r--r-- | src/net/smtp/SMTPCommand.cpp | 155 | ||||
-rw-r--r-- | src/net/smtp/SMTPCommandSet.cpp | 144 | ||||
-rw-r--r-- | src/net/smtp/SMTPTransport.cpp | 154 |
3 files changed, 344 insertions, 109 deletions
diff --git a/src/net/smtp/SMTPCommand.cpp b/src/net/smtp/SMTPCommand.cpp new file mode 100644 index 00000000..13b1dc5d --- /dev/null +++ b/src/net/smtp/SMTPCommand.cpp @@ -0,0 +1,155 @@ +// +// 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 "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/smtp/SMTPCommand.hpp" + +#include "vmime/net/socket.hpp" + +#include "vmime/mailbox.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPCommand::SMTPCommand(const std::string& text) + : m_text(text) +{ +} + + +// static +ref <SMTPCommand> SMTPCommand::EHLO(const std::string& hostname) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "EHLO " << hostname; + + return createCommand(cmd.str()); +} + + +// static +ref <SMTPCommand> SMTPCommand::HELO(const std::string& hostname) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "HELO " << hostname; + + return createCommand(cmd.str()); +} + + +// static +ref <SMTPCommand> SMTPCommand::AUTH(const std::string& mechName) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName; + + return createCommand(cmd.str()); +} + + +// static +ref <SMTPCommand> SMTPCommand::STARTTLS() +{ + return createCommand("STARTTLS"); +} + + +// static +ref <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "MAIL FROM:<" << mbox.getEmail() << ">"; + + return createCommand(cmd.str()); +} + + +// static +ref <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "RCPT TO:<" << mbox.getEmail() << ">"; + + return createCommand(cmd.str()); +} + + +// static +ref <SMTPCommand> SMTPCommand::DATA() +{ + return createCommand("DATA"); +} + + +// static +ref <SMTPCommand> SMTPCommand::NOOP() +{ + return createCommand("NOOP"); +} + + +// static +ref <SMTPCommand> SMTPCommand::QUIT() +{ + return createCommand("QUIT"); +} + + +// static +ref <SMTPCommand> SMTPCommand::createCommand(const std::string& text) +{ + return vmime::create <SMTPCommand>(text); +} + + +const string SMTPCommand::getText() const +{ + return m_text; +} + + +void SMTPCommand::writeToSocket(ref <socket> sok) +{ + sok->send(m_text + "\r\n"); +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/src/net/smtp/SMTPCommandSet.cpp b/src/net/smtp/SMTPCommandSet.cpp new file mode 100644 index 00000000..8644ca48 --- /dev/null +++ b/src/net/smtp/SMTPCommandSet.cpp @@ -0,0 +1,144 @@ +// +// 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 "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/smtp/SMTPCommandSet.hpp" + +#include "vmime/net/socket.hpp" + +#include "vmime/mailbox.hpp" + +#include <stdexcept> + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPCommandSet::SMTPCommandSet(const bool pipeline) + : SMTPCommand(""), m_pipeline(pipeline), + m_started(false), m_lastCommandSent() +{ +} + + +// static +ref <SMTPCommandSet> SMTPCommandSet::create(const bool pipeline) +{ + return vmime::create <SMTPCommandSet>(pipeline); +} + + +void SMTPCommandSet::addCommand(ref <SMTPCommand> cmd) +{ + if (m_started) + { + throw std::runtime_error("Could not add command to pipeline: " + "one or more commands have already been sent to the server."); + } + + m_commands.push_back(cmd); +} + + +void SMTPCommandSet::writeToSocket(ref <socket> sok) +{ + if (m_pipeline) + { + if (!m_started) + { + // Send all commands at once + for (std::list <ref <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) + { + ref <SMTPCommand> cmd = *it; + cmd->writeToSocket(sok); + } + } + + if (!m_commands.empty()) + { + // Advance the pointer to last command sent + ref <SMTPCommand> cmd = m_commands.front(); + m_commands.pop_front(); + + m_lastCommandSent = cmd; + } + } + else + { + if (!m_commands.empty()) + { + // Send only one command + ref <SMTPCommand> cmd = m_commands.front(); + m_commands.pop_front(); + + cmd->writeToSocket(sok); + + m_lastCommandSent = cmd; + } + } + + m_started = true; +} + + +const string SMTPCommandSet::getText() const +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + for (std::list <ref <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) + { + cmd << (*it)->getText() << "\r\n"; + } + + return cmd.str(); +} + + +const bool SMTPCommandSet::isFinished() const +{ + return (m_pipeline && m_started) || (m_commands.size() == 0); +} + + +ref <SMTPCommand> SMTPCommandSet::getLastCommandSent() const +{ + return m_lastCommandSent; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp index c17b4a05..8a3362e2 100644 --- a/src/net/smtp/SMTPTransport.cpp +++ b/src/net/smtp/SMTPTransport.cpp @@ -29,6 +29,8 @@ #include "vmime/net/smtp/SMTPTransport.hpp" #include "vmime/net/smtp/SMTPResponse.hpp" +#include "vmime/net/smtp/SMTPCommand.hpp" +#include "vmime/net/smtp/SMTPCommandSet.hpp" #include "vmime/exception.hpp" #include "vmime/platform.hpp" @@ -68,7 +70,7 @@ namespace smtp { SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth, const bool secured) : transport(sess, getInfosInstance(), auth), m_socket(NULL), m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL), - m_isSMTPS(secured), m_secured(false), m_pipelineStarted(false) + m_isSMTPS(secured), m_secured(false) { } @@ -202,7 +204,7 @@ void SMTPTransport::helo() // S: 250-PIPELINING // S: 250 SIZE 2555555555 - sendRequest("EHLO " + platform::getHandler()->getHostName()); + sendRequest(SMTPCommand::EHLO(platform::getHandler()->getHostName())); ref <SMTPResponse> resp; @@ -213,7 +215,7 @@ void SMTPTransport::helo() // eg: C: HELO thismachine.ourdomain.com // S: 250 OK - sendRequest("HELO " + platform::getHandler()->getHostName()); + sendRequest(SMTPCommand::HELO(platform::getHandler()->getHostName())); if ((resp = readResponse())->getCode() != 250) { @@ -365,7 +367,7 @@ void SMTPTransport::authenticateSASL() saslSession->init(); - sendRequest("AUTH " + mech->getName()); + sendRequest(SMTPCommand::AUTH(mech->getName())); for (bool cont = true ; cont ; ) { @@ -396,7 +398,7 @@ void SMTPTransport::authenticateSASL() (challenge, challengeLen, &resp, &respLen); // Send response - sendRequest(saslContext->encodeB64(resp, respLen)); + m_socket->send(saslContext->encodeB64(resp, respLen) + "\r\n"); } catch (exceptions::sasl_exception& e) { @@ -413,7 +415,7 @@ void SMTPTransport::authenticateSASL() } // Cancel SASL exchange - sendRequest("*"); + m_socket->sendRaw("*\r\n", 3); } catch (...) { @@ -455,7 +457,7 @@ void SMTPTransport::startTLS() { try { - sendRequest("STARTTLS"); + sendRequest(SMTPCommand::STARTTLS()); ref <SMTPResponse> resp = readResponse(); @@ -523,7 +525,7 @@ void SMTPTransport::internalDisconnect() { try { - sendRequest("QUIT"); + sendRequest(SMTPCommand::QUIT()); readResponse(); } catch (exception&) @@ -549,7 +551,7 @@ void SMTPTransport::noop() if (!isConnected()) throw exceptions::not_connected(); - sendRequest("NOOP"); + sendRequest(SMTPCommand::NOOP()); ref <SMTPResponse> resp = readResponse(); @@ -572,87 +574,53 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients throw exceptions::no_expeditor(); - ref <SMTPResponse> resp; - - if (m_extensions.find("PIPELINING") != m_extensions.end()) - { - beginCommandPipeline(); - - // Emit the "MAIL" command - sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">"); - - // Emit a "RCPT TO" command for each recipient - for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) - { - const mailbox& mbox = *recipients.getMailboxAt(i); - - sendRequest("RCPT TO:<" + mbox.getEmail() + ">"); - } + const bool hasPipelining = + m_extensions.find("PIPELINING") != m_extensions.end(); - // Prepare sending of message data - sendRequest("DATA"); + ref <SMTPResponse> resp; + ref <SMTPCommandSet> commands = SMTPCommandSet::create(hasPipelining); - endCommandPipeline(); + // Emit the "MAIL" command + commands->addCommand(SMTPCommand::MAIL(expeditor)); - // Read response for "MAIL" command - if ((resp = readResponse())->getCode() != 250) - { - internalDisconnect(); - throw exceptions::command_error("MAIL", resp->getText()); - } + // Emit a "RCPT TO" command for each recipient + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) + { + const mailbox& mbox = *recipients.getMailboxAt(i); + commands->addCommand(SMTPCommand::RCPT(mbox)); + } - // Read responses for "RCPT TO" commands - for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) - { - const mailbox& mbox = *recipients.getMailboxAt(i); + // Prepare sending of message data + commands->addCommand(SMTPCommand::DATA()); - if ((resp = readResponse())->getCode() != 250) - { - internalDisconnect(); - throw exceptions::command_error("RCPT TO", resp->getText(), mbox.getEmail()); - } - } + // Read response for "MAIL" command + commands->writeToSocket(m_socket); - // Read response for "DATA" command - if ((resp = readResponse())->getCode() != 354) - { - internalDisconnect(); - throw exceptions::command_error("DATA", resp->getText()); - } + if ((resp = readResponse())->getCode() != 250) + { + internalDisconnect(); + throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText()); } - else + + // Read responses for "RCPT TO" commands + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { - // Emit the "MAIL" command - sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">"); + commands->writeToSocket(m_socket); if ((resp = readResponse())->getCode() != 250) { internalDisconnect(); - throw exceptions::command_error("MAIL", resp->getText()); - } - - // Emit a "RCPT TO" command for each recipient - for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) - { - const mailbox& mbox = *recipients.getMailboxAt(i); - - sendRequest("RCPT TO:<" + mbox.getEmail() + ">"); - - if ((resp = readResponse())->getCode() != 250) - { - internalDisconnect(); - throw exceptions::command_error("RCPT TO", resp->getText(), mbox.getEmail()); - } + throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText()); } + } - // Prepare sending of message data - sendRequest("DATA"); + // Read response for "DATA" command + commands->writeToSocket(m_socket); - if ((resp = readResponse())->getCode() != 354) - { - internalDisconnect(); - throw exceptions::command_error("DATA", resp->getText()); - } + if ((resp = readResponse())->getCode() != 354) + { + internalDisconnect(); + throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText()); } // Send the message data @@ -675,41 +643,9 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients } -void SMTPTransport::beginCommandPipeline() +void SMTPTransport::sendRequest(ref <SMTPCommand> cmd) { - m_pipeline.clear(); - m_pipelineStarted = true; -} - - -void SMTPTransport::endCommandPipeline() -{ - if (m_pipelineStarted) - { - m_socket->send(m_pipeline.str()); - - m_pipeline.clear(); - m_pipelineStarted = false; - } -} - - -void SMTPTransport::sendRequest(const string& buffer, const bool end) -{ - if (m_pipelineStarted) - { - m_pipeline << buffer; - - if (end) - m_pipeline << "\r\n"; - } - else - { - if (end) - m_socket->send(buffer + "\r\n"); - else - m_socket->send(buffer); - } + cmd->writeToSocket(m_socket); } |