diff options
-rw-r--r-- | SConstruct | 2 | ||||
-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 | ||||
-rw-r--r-- | vmime/net/smtp/SMTPCommand.hpp | 110 | ||||
-rw-r--r-- | vmime/net/smtp/SMTPCommandSet.hpp | 107 | ||||
-rw-r--r-- | vmime/net/smtp/SMTPTransport.hpp | 13 |
7 files changed, 567 insertions, 118 deletions
@@ -243,6 +243,8 @@ libvmime_messaging_proto_sources = [ [ 'smtp', [ + 'net/smtp/SMTPCommand.cpp', 'net/smtp/SMTPCommand.hpp', + 'net/smtp/SMTPCommandSet.cpp', 'net/smtp/SMTPCommandSet.hpp', 'net/smtp/SMTPResponse.cpp', 'net/smtp/SMTPResponse.hpp', 'net/smtp/SMTPServiceInfos.cpp', 'net/smtp/SMTPServiceInfos.hpp', 'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp', 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); } diff --git a/vmime/net/smtp/SMTPCommand.hpp b/vmime/net/smtp/SMTPCommand.hpp new file mode 100644 index 00000000..ccf445e5 --- /dev/null +++ b/vmime/net/smtp/SMTPCommand.hpp @@ -0,0 +1,110 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/object.hpp" +#include "vmime/base.hpp" + + +namespace vmime { + + +class mailbox; + + +namespace net { + + +class socket; +class timeoutHandler; + + +namespace smtp { + + +/** A SMTP command, as sent to server. + */ +class SMTPCommand : public object +{ + friend class vmime::creator; + +public: + + static ref <SMTPCommand> HELO(const std::string& hostname); + static ref <SMTPCommand> EHLO(const std::string& hostname); + static ref <SMTPCommand> AUTH(const std::string& mechName); + static ref <SMTPCommand> STARTTLS(); + static ref <SMTPCommand> MAIL(const mailbox& mbox); + static ref <SMTPCommand> RCPT(const mailbox& mbox); + static ref <SMTPCommand> DATA(); + static ref <SMTPCommand> NOOP(); + static ref <SMTPCommand> QUIT(); + + /** Creates a new SMTP command with the specified text. + * + * @param text command text + * @return a new SMTPCommand object + */ + static ref <SMTPCommand> createCommand(const std::string& text); + + /** Sends this command to the specified socket. + * + * @param sok socket to which the command will be written + */ + virtual void writeToSocket(ref <socket> sok); + + /** Returns the full text of the command, including command name + * and parameters (if any). + * + * @return command text (eg. "RCPT TO:<[email protected]>") + */ + virtual const string getText() const; + +protected: + + SMTPCommand(const std::string& text); + SMTPCommand(const SMTPCommand&); + +private: + + std::string m_text; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED diff --git a/vmime/net/smtp/SMTPCommandSet.hpp b/vmime/net/smtp/SMTPCommandSet.hpp new file mode 100644 index 00000000..5b52ba0a --- /dev/null +++ b/vmime/net/smtp/SMTPCommandSet.hpp @@ -0,0 +1,107 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPCOMMANDSET_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCOMMANDSET_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include <list> + +#include "vmime/net/smtp/SMTPCommand.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** A set of SMTP commands, which may be sent all at once + * to the server if pipelining is supported. + */ +class SMTPCommandSet : public SMTPCommand +{ + friend class vmime::creator; + +public: + + /** Creates a new set of SMTP commands. + * + * @param pipeline set to true if the server supports pipelining + * @return a new SMTPCommandSet object + */ + static ref <SMTPCommandSet> create(const bool pipeline); + + /** Adds a new command to this set. + * If one or more comments have already been sent to the server, + * an exception will be thrown. + * + * @param cmd command to add + */ + void addCommand(ref <SMTPCommand> cmd); + + /** Tests whether all commands have been sent. + * + * @return true if all commands have been sent, + * or false otherwise + */ + const bool isFinished() const; + + /** Returns the last command which has been sent. + * + * @return a pointer to a SMTPCommand, of NULL if no command + * has been sent yet + */ + ref <SMTPCommand> getLastCommandSent() const; + + + void writeToSocket(ref <socket> sok); + + const string getText() const; + +private: + + SMTPCommandSet(const bool pipeline); + SMTPCommandSet(const SMTPCommandSet&); + + + bool m_pipeline; + bool m_started; + std::list <ref <SMTPCommand> > m_commands; + ref <SMTPCommand> m_lastCommandSent; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCOMMANDSET_HPP_INCLUDED diff --git a/vmime/net/smtp/SMTPTransport.hpp b/vmime/net/smtp/SMTPTransport.hpp index 5c285ab5..50885203 100644 --- a/vmime/net/smtp/SMTPTransport.hpp +++ b/vmime/net/smtp/SMTPTransport.hpp @@ -44,6 +44,9 @@ namespace net { namespace smtp { +class SMTPCommand; + + /** SMTP transport service. */ @@ -72,7 +75,7 @@ public: private: - void sendRequest(const string& buffer, const bool end = true); + void sendRequest(ref <SMTPCommand> cmd); ref <SMTPResponse> readResponse(); void internalDisconnect(); @@ -102,14 +105,6 @@ private: SMTPResponse::state m_responseState; - // Pipelining - std::ostringstream m_pipeline; - bool m_pipelineStarted; - - void beginCommandPipeline(); - void endCommandPipeline(); - - // Service infos static SMTPServiceInfos sm_infos; }; |