diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/src/vmime/net/smtp | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/src/vmime/net/smtp')
19 files changed, 3782 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp new file mode 100644 index 0000000..3b242d1 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp @@ -0,0 +1,180 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/SMTPChunkingOutputStreamAdapter.hpp" + +#include "vmime/net/smtp/SMTPConnection.hpp" +#include "vmime/net/smtp/SMTPTransport.hpp" + +#include <algorithm> + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPChunkingOutputStreamAdapter::SMTPChunkingOutputStreamAdapter( + const shared_ptr <SMTPConnection>& conn, + const size_t size, + utility::progressListener* progress +) + : m_connection(conn), + m_bufferSize(0), + m_chunkCount(0), + m_totalSize(size), + m_totalSent(0), + m_progress(progress) { + + if (progress) { + progress->start(size); + } +} + + +void SMTPChunkingOutputStreamAdapter::sendChunk( + const byte_t* const data, + const size_t count, + const bool last +) { + + if (count == 0 && !last) { + // Nothing to send + return; + } + + // Send this chunk + m_connection->sendRequest(SMTPCommand::BDAT(count, last)); + m_connection->getSocket()->sendRaw(data, count); + + ++m_chunkCount; + + if (m_progress) { + + m_totalSent += count; + m_totalSize = std::max(m_totalSize, m_totalSent); + + m_progress->progress(m_totalSent, m_totalSize); + } + + if (m_connection->getTracer()) { + m_connection->getTracer()->traceSendBytes(count); + } + + // If PIPELINING is not supported, read one response for this BDAT command + if (!m_connection->hasExtension("PIPELINING")) { + + shared_ptr <SMTPResponse> resp = m_connection->readResponse(); + + if (resp->getCode() != 250) { + m_connection->getTransport()->disconnect(); + throw exceptions::command_error("BDAT", resp->getText()); + } + + // If PIPELINING is supported, read one response for each chunk (ie. number + // of BDAT commands issued) after the last chunk has been sent + } else if (last) { + + bool invalidReply = false; + shared_ptr <SMTPResponse> resp; + + for (unsigned int i = 0 ; i < m_chunkCount ; ++i) { + + resp = m_connection->readResponse(); + + if (resp->getCode() != 250) { + invalidReply = true; + } + } + + if (invalidReply) { + m_connection->getTransport()->disconnect(); + throw exceptions::command_error("BDAT", resp->getText()); + } + } +} + + +void SMTPChunkingOutputStreamAdapter::writeImpl( + const byte_t* const data, + const size_t count +) { + + const byte_t* curData = data; + size_t curCount = count; + + while (curCount != 0) { + + // Fill the buffer + const size_t remaining = sizeof(m_buffer) - m_bufferSize; + const size_t bytesToCopy = std::min(remaining, curCount); + + std::copy(curData, curData + bytesToCopy, m_buffer + m_bufferSize); + + m_bufferSize += bytesToCopy; + curData += bytesToCopy; + curCount -= bytesToCopy; + + // If the buffer is full, send this chunk + if (m_bufferSize >= sizeof(m_buffer)) { + + sendChunk(m_buffer, m_bufferSize, /* last */ false); + m_bufferSize = 0; + } + } +} + + +void SMTPChunkingOutputStreamAdapter::flush() { + + sendChunk(m_buffer, m_bufferSize, /* last */ true); + m_bufferSize = 0; + + if (m_progress) { + m_progress->stop(m_totalSize); + } + + if (m_connection->getTracer()) { + m_connection->getTracer()->traceSendBytes(m_bufferSize); + } +} + + +size_t SMTPChunkingOutputStreamAdapter::getBlockSize() { + + return sizeof(m_buffer); +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp new file mode 100644 index 0000000..4ef466a --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp @@ -0,0 +1,94 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTPCHUNKINGOUTPUTSTREAMADAPTER_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCHUNKINGOUTPUTSTREAMADAPTER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/utility/outputStream.hpp" +#include "vmime/utility/progressListener.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +class SMTPConnection; + + +/** An output stream adapter used with ESMTP CHUNKING extension. + */ +class VMIME_EXPORT SMTPChunkingOutputStreamAdapter : public utility::outputStream { + +public: + + SMTPChunkingOutputStreamAdapter( + const shared_ptr <SMTPConnection>& conn, + const size_t size, + utility::progressListener* progress + ); + + void flush(); + + size_t getBlockSize(); + +protected: + + void writeImpl(const byte_t* const data, const size_t count); + +private: + + SMTPChunkingOutputStreamAdapter(const SMTPChunkingOutputStreamAdapter&); + + + void sendChunk(const byte_t* const data, const size_t count, const bool last); + + + shared_ptr <SMTPConnection> m_connection; + + byte_t m_buffer[262144]; // 256 KB + size_t m_bufferSize; + + unsigned int m_chunkCount; + + size_t m_totalSize; + size_t m_totalSent; + utility::progressListener* m_progress; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCHUNKINGOUTPUTSTREAMADAPTER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommand.cpp b/vmime-master/src/vmime/net/smtp/SMTPCommand.cpp new file mode 100644 index 0000000..5e533d1 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommand.cpp @@ -0,0 +1,258 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/net/tracer.hpp" + +#include "vmime/mailbox.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPCommand::SMTPCommand(const string& text, const string& traceText) + : m_text(text), + m_traceText(traceText) +{ +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::EHLO(const string& hostname) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "EHLO " << hostname; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::HELO(const string& hostname) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "HELO " << hostname; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::AUTH(const string& mechName) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::AUTH(const string& mechName, const std::string& initialResponse) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName << " " << initialResponse; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::STARTTLS() { + + return createCommand("STARTTLS"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, + const std::string& dsnRet, const std::string& dsnEnvelopId) { + + return MAIL(mbox, utf8, 0, dsnRet, dsnEnvelopId); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, const size_t size, + const std::string& dsnRet, const std::string& dsnEnvelopId) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "MAIL FROM:<"; + + if (utf8) { + + cmd << mbox.getEmail().toText().getConvertedText(vmime::charsets::UTF_8); + + } else { + + vmime::utility::outputStreamAdapter cmd2(cmd); + mbox.getEmail().generate(cmd2); + } + + cmd << ">"; + + if (!dsnRet.empty()) { + cmd << " " << dsn::RET << "=" << dsnRet; + } + if (!dsnEnvelopId.empty()) { + cmd << " " << dsn::ENVID << "=<" << dsnEnvelopId << ">"; + } + + if (utf8) { + cmd << " SMTPUTF8"; + } + + if (size != 0) { + cmd << " SIZE=" << size; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox, const bool utf8, + const string& dsnNotify) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "RCPT TO:<"; + + if (utf8) { + + cmd << mbox.getEmail().toText().getConvertedText(vmime::charsets::UTF_8); + + } else { + + vmime::utility::outputStreamAdapter cmd2(cmd); + mbox.getEmail().generate(cmd2); + } + + cmd << ">"; + + if (!dsnNotify.empty()) { + cmd << " " << dsn::NOTIFY << "=" << dsnNotify; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::RSET() { + + return createCommand("RSET"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::DATA() { + + return createCommand("DATA"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::BDAT(const size_t chunkSize, const bool last) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "BDAT " << chunkSize; + + if (last) { + cmd << " LAST"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::NOOP() { + + return createCommand("NOOP"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::QUIT() { + + return createCommand("QUIT"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::createCommand(const string& text, const string& traceText) { + + if (traceText.empty()) { + return shared_ptr <SMTPCommand>(new SMTPCommand(text, text)); + } else { + return shared_ptr <SMTPCommand>(new SMTPCommand(text, traceText)); + } +} + + +const string SMTPCommand::getText() const { + + return m_text; +} + + +const string SMTPCommand::getTraceText() const { + + return m_traceText; +} + + +void SMTPCommand::writeToSocket(const shared_ptr <socket>& sok, shared_ptr <tracer> tr) { + + sok->send(m_text + "\r\n"); + + if (tr) { + tr->traceSend(m_traceText); + } +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommand.hpp b/vmime-master/src/vmime/net/smtp/SMTPCommand.hpp new file mode 100644 index 0000000..9a32d1e --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommand.hpp @@ -0,0 +1,125 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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; +class tracer; + + +namespace smtp { + + +/** A SMTP command, as sent to server. + */ +class VMIME_EXPORT SMTPCommand : public object { + +public: + + static shared_ptr <SMTPCommand> HELO(const string& hostname); + static shared_ptr <SMTPCommand> EHLO(const string& hostname); + static shared_ptr <SMTPCommand> AUTH(const string& mechName); + static shared_ptr <SMTPCommand> AUTH(const string& mechName, const std::string& initialResponse); + static shared_ptr <SMTPCommand> STARTTLS(); + static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, + const std::string& dsnRet, const std::string& dsnEnvelopId); + static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const size_t size, + const std::string& dsnRet, const std::string& dsnEnvelopId); + static shared_ptr <SMTPCommand> RCPT(const mailbox& mbox, const bool utf8, + const std::string& dsnNotify); + static shared_ptr <SMTPCommand> RSET(); + static shared_ptr <SMTPCommand> DATA(); + static shared_ptr <SMTPCommand> BDAT(const size_t chunkSize, const bool last); + static shared_ptr <SMTPCommand> NOOP(); + static shared_ptr <SMTPCommand> QUIT(); + + /** Creates a new SMTP command with the specified text. + * + * @param text command text + * @return a new SMTPCommand object + */ + static shared_ptr <SMTPCommand> createCommand(const string& text, const string& traceText = ""); + + /** Sends this command to the specified socket. + * + * @param sok socket to which the command will be written + * @param tr tracer + */ + virtual void writeToSocket(const shared_ptr <socket>& sok, shared_ptr <tracer> tr); + + /** Returns the full text of the command, including command name + * and parameters (if any). + * + * @return command text (eg. "RCPT TO:<vincent@kisli.com>") + */ + virtual const string getText() const; + + /** Returns the full text of the command, suitable for outputing + * to the tracer. + * + * @return trace text (eg. "LOGIN myusername ***") + */ + virtual const string getTraceText() const; + +protected: + + SMTPCommand(const string& text, const string& traceText); + SMTPCommand(const SMTPCommand&); + +private: + + string m_text; + string m_traceText; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommandSet.cpp b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.cpp new file mode 100644 index 0000000..eab7086 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.cpp @@ -0,0 +1,160 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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 +shared_ptr <SMTPCommandSet> SMTPCommandSet::create(const bool pipeline) { + + return shared_ptr <SMTPCommandSet>(new SMTPCommandSet(pipeline)); +} + + +void SMTPCommandSet::addCommand(const shared_ptr <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(const shared_ptr <socket>& sok, const shared_ptr <tracer>& tr) { + + if (m_pipeline) { + + if (!m_started) { + + // Send all commands at once + for (std::list <shared_ptr <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) { + + shared_ptr <SMTPCommand> cmd = *it; + cmd->writeToSocket(sok, tr); + } + } + + if (!m_commands.empty()) { + + // Advance the pointer to last command sent + shared_ptr <SMTPCommand> cmd = m_commands.front(); + m_commands.pop_front(); + + m_lastCommandSent = cmd; + } + + } else { + + if (!m_commands.empty()) { + + // Send only one command + shared_ptr <SMTPCommand> cmd = m_commands.front(); + m_commands.pop_front(); + + cmd->writeToSocket(sok, tr); + + m_lastCommandSent = cmd; + } + } + + m_started = true; +} + + +const string SMTPCommandSet::getText() const { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + for (std::list <shared_ptr <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) { + + cmd << (*it)->getText() << "\r\n"; + } + + return cmd.str(); +} + + +const string SMTPCommandSet::getTraceText() const { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + for (std::list <shared_ptr <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) { + + cmd << (*it)->getTraceText() << "\r\n"; + } + + return cmd.str(); +} + + +bool SMTPCommandSet::isFinished() const { + + return (m_pipeline && m_started) || (m_commands.size() == 0 && m_started); +} + + +shared_ptr <SMTPCommand> SMTPCommandSet::getLastCommandSent() const { + + return m_lastCommandSent; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommandSet.hpp b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.hpp new file mode 100644 index 0000000..2fd977c --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.hpp @@ -0,0 +1,106 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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 VMIME_EXPORT SMTPCommandSet : public SMTPCommand { + +public: + + /** Creates a new set of SMTP commands. + * + * @param pipeline set to true if the server supports pipelining + * @return a new SMTPCommandSet object + */ + static shared_ptr <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(const shared_ptr <SMTPCommand>& cmd); + + /** Tests whether all commands have been sent. + * + * @return true if all commands have been sent, + * or false otherwise + */ + 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 + */ + shared_ptr <SMTPCommand> getLastCommandSent() const; + + + void writeToSocket(const shared_ptr <socket>& sok, const shared_ptr <tracer>& tr); + + const string getText() const; + const string getTraceText() const; + +private: + + SMTPCommandSet(const bool pipeline); + SMTPCommandSet(const SMTPCommandSet&); + + + bool m_pipeline; + bool m_started; + std::list <shared_ptr <SMTPCommand> > m_commands; + shared_ptr <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-master/src/vmime/net/smtp/SMTPConnection.cpp b/vmime-master/src/vmime/net/smtp/SMTPConnection.cpp new file mode 100644 index 0000000..07d0376 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPConnection.cpp @@ -0,0 +1,714 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/SMTPConnection.hpp" +#include "vmime/net/smtp/SMTPTransport.hpp" +#include "vmime/net/smtp/SMTPExceptions.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include "vmime/security/digest/messageDigestFactory.hpp" + +#include "vmime/net/defaultConnectionInfos.hpp" + +#if VMIME_HAVE_SASL_SUPPORT + #include "vmime/security/sasl/SASLContext.hpp" +#else + #include "vmime/utility/encoder/b64Encoder.hpp" + #include "vmime/utility/inputStreamStringAdapter.hpp" + #include "vmime/utility/outputStreamStringAdapter.hpp" +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/TLSSession.hpp" + #include "vmime/net/tls/TLSSecuredConnectionInfos.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + + +// Helpers for service properties +#define GET_PROPERTY(type, prop) \ + (m_transport.lock()->getInfos().getPropertyValue <type>(getSession(), \ + dynamic_cast <const SMTPServiceInfos&>(m_transport.lock()->getInfos()).getProperties().prop)) +#define HAS_PROPERTY(prop) \ + (m_transport.lock()->getInfos().hasProperty(getSession(), \ + dynamic_cast <const SMTPServiceInfos&>(m_transport.lock()->getInfos()).getProperties().prop)) + + +namespace vmime { +namespace net { +namespace smtp { + + + +SMTPConnection::SMTPConnection( + const shared_ptr <SMTPTransport>& transport, + const shared_ptr <security::authenticator>& auth +) + : m_transport(transport), + m_auth(auth), + m_socket(null), + m_timeoutHandler(null), + m_authenticated(false), + m_secured(false), + m_extendedSMTP(false) { + + static int connectionId = 0; + + if (transport->getTracerFactory()) { + m_tracer = transport->getTracerFactory()->create(transport, ++connectionId); + } +} + + +SMTPConnection::~SMTPConnection() { + + try { + + if (isConnected()) { + disconnect(); + } else if (m_socket) { + internalDisconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +void SMTPConnection::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + const string address = GET_PROPERTY(string, PROPERTY_SERVER_ADDRESS); + const port_t port = GET_PROPERTY(port_t, PROPERTY_SERVER_PORT); + + shared_ptr <SMTPTransport> transport = m_transport.lock(); + + // Create the time-out handler + if (transport->getTimeoutHandlerFactory()) { + m_timeoutHandler = transport->getTimeoutHandlerFactory()->create(); + } + + // Create and connect the socket + m_socket = transport->getSocketFactory()->create(m_timeoutHandler); + m_socket->setTracer(m_tracer); + +#if VMIME_HAVE_TLS_SUPPORT + if (transport->isSMTPS()) { // dedicated port/SMTPS + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create( + transport->getCertificateVerifier(), + transport->getSession()->getTLSProperties() + ); + + shared_ptr <tls::TLSSocket> tlsSocket = tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>(address, port, tlsSession, tlsSocket); + + } else +#endif // VMIME_HAVE_TLS_SUPPORT + { + m_cntInfos = make_shared <defaultConnectionInfos>(address, port); + } + + m_socket->connect(address, port); + + // Connection + // + // eg: C: <connection to server> + // --- S: 220 smtp.domain.com Service ready + + shared_ptr <SMTPResponse> resp; + + if ((resp = readResponse())->getCode() != 220) { + internalDisconnect(); + throw exceptions::connection_greeting_error(resp->getText()); + } + + // Identification + helo(); + +#if VMIME_HAVE_TLS_SUPPORT + // Setup secured connection, if requested + const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); + const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); + + if (!transport->isSMTPS() && tls) { // only if not SMTPS + + try { + + startTLS(); + + // Non-fatal error + } catch (exceptions::command_error&) { + + if (tlsRequired) { + throw; + } else { + // TLS is not required, so don't bother + } + + // Fatal error + } catch (...) { + + throw; + } + + // Must reissue a EHLO command [RFC-2487, 5.2] + helo(); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + // Authentication + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH)) { + authenticate(); + } else { + m_authenticated = true; + } +} + + +void SMTPConnection::helo() { + + // First, try Extended SMTP (ESMTP) + // + // eg: C: EHLO thismachine.ourdomain.com + // S: 250-smtp.theserver.com + // S: 250-AUTH CRAM-MD5 DIGEST-MD5 + // S: 250-PIPELINING + // S: 250 SIZE 2555555555 + + sendRequest(SMTPCommand::EHLO(platform::getHandler()->getHostName())); + + shared_ptr <SMTPResponse> resp; + + if ((resp = readResponse())->getCode() != 250) { + + // Next, try "Basic" SMTP + // + // eg: C: HELO thismachine.ourdomain.com + // S: 250 OK + + sendRequest(SMTPCommand::HELO(platform::getHandler()->getHostName())); + + if ((resp = readResponse())->getCode() != 250) { + internalDisconnect(); + throw exceptions::connection_greeting_error(resp->getLastLine().getText()); + } + + m_extendedSMTP = false; + m_extensions.clear(); + + } else { + + m_extendedSMTP = true; + m_extensions.clear(); + + // Get supported extensions from SMTP response + // One extension per line, format is: EXT PARAM1 PARAM2... + for (size_t i = 1, n = resp->getLineCount() ; i < n ; ++i) { + + const string line = resp->getLineAt(i).getText(); + + std::istringstream iss(line); + iss.imbue(std::locale::classic()); + + string ext; + iss >> ext; + + std::vector <string> params; + string param; + + // Special case: some servers send "AUTH=MECH [MECH MECH...]" + if (ext.length() >= 5 && utility::stringUtils::toUpper(ext.substr(0, 5)) == "AUTH=") { + + params.push_back(utility::stringUtils::toUpper(ext.substr(5))); + ext = "AUTH"; + } + + while (iss >> param) { + params.push_back(utility::stringUtils::toUpper(param)); + } + + m_extensions[ext] = params; + } + } +} + + +bool SMTPConnection::hasExtension( + const std::string& extName, + std::vector <string>* params +) const { + + std::map <string, std::vector <string> >::const_iterator + it = m_extensions.find(extName); + + if (it != m_extensions.end()) { + + if (params) { + *params = (*it).second; + } + + return true; + + } else { + + return false; + } +} + + +void SMTPConnection::authenticate() { + + if (!m_extendedSMTP) { + internalDisconnect(); + throw exceptions::command_error("AUTH", "ESMTP not supported."); + } + + getAuthenticator()->setService(m_transport.lock()); + +#if VMIME_HAVE_SASL_SUPPORT + // Try SASL authentication + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL)) { + + try { + + authenticateSASL(); + + m_authenticated = true; + return; + + } catch (exception&) { + + internalDisconnect(); + throw; + } + } +#else // no SASL + + // allow AUTH PLAIN over TLS - it is a popular and simple mechanism + if (m_secured) { + + std::vector <string> authMechs; + hasExtension("AUTH", &authMechs); + + if (authMechs.empty()) { + throw exceptions::authentication_error("No AUTH mechanism available."); + } + + const string plain("PLAIN"); + + if (std::find(authMechs.begin(), authMechs.end(), plain) != authMechs.end()) { + + const string username = getAuthenticator()->getUsername(); + const string password = getAuthenticator()->getPassword(); + const string authToken = username + '\0' + username + '\0' + password; + + utility::inputStreamStringAdapter in(authToken); + string authTokenBase64; + utility::outputStreamStringAdapter out(authTokenBase64); + + vmime::utility::encoder::b64Encoder encoder; + encoder.encode(in, out); + + sendRequest(SMTPCommand::AUTH(plain, authTokenBase64)); + + shared_ptr <SMTPResponse> response = readResponse(); + + const int code = response ? response->getCode() : -1; + + if (code == 235) { + m_authenticated = true; + return; + } + } + } + +#endif // VMIME_HAVE_SASL_SUPPORT + + // No other authentication method is possible + throw exceptions::authentication_error("All authentication methods failed"); +} + + + +#if VMIME_HAVE_SASL_SUPPORT + +void SMTPConnection::authenticateSASL() { + + if (!dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())) { + throw exceptions::authentication_error("No SASL authenticator available."); + } + + // Obtain SASL mechanisms supported by server from ESMTP extensions + std::vector <string> saslMechs; + hasExtension("AUTH", &saslMechs); + + if (saslMechs.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + std::vector <shared_ptr <security::sasl::SASLMechanism> > mechList; + + shared_ptr <security::sasl::SASLContext> saslContext = + security::sasl::SASLContext::create(); + + for (unsigned int i = 0 ; i < saslMechs.size() ; ++i) { + + try { + + mechList.push_back + (saslContext->createMechanism(saslMechs[i])); + + } catch (exceptions::no_such_mechanism&) { + + // Ignore mechanism + } + } + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try to suggest a mechanism among all those supported + shared_ptr <security::sasl::SASLMechanism> suggestedMech = + saslContext->suggestMechanism(mechList); + + if (!suggestedMech) { + throw exceptions::authentication_error("Unable to suggest SASL mechanism."); + } + + // Allow application to choose which mechanisms to use + mechList = dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())-> + getAcceptableMechanisms(mechList, suggestedMech); + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try each mechanism in the list in turn + for (unsigned int i = 0 ; i < mechList.size() ; ++i) { + + shared_ptr <security::sasl::SASLMechanism> mech = mechList[i]; + + shared_ptr <security::sasl::SASLSession> saslSession = + saslContext->createSession("smtp", getAuthenticator(), mech); + + saslSession->init(); + + if (saslSession->getMechanism()->hasInitialResponse()) { + + byte_t* initialResp = 0; + size_t initialRespLen = 0; + + saslSession->evaluateChallenge(NULL, 0, &initialResp, &initialRespLen); + + string encodedInitialResp(saslContext->encodeB64(initialResp, initialRespLen)); + delete [] initialResp; + + if (encodedInitialResp.empty()) { + sendRequest(SMTPCommand::AUTH(mech->getName(), "=")); + } else { + sendRequest(SMTPCommand::AUTH(mech->getName(), encodedInitialResp)); + } + + } else { + + sendRequest(SMTPCommand::AUTH(mech->getName())); + } + + for (bool cont = true ; cont ; ) { + + shared_ptr <SMTPResponse> response = readResponse(); + + switch (response->getCode()) { + + case 235: { + + m_socket = saslSession->getSecuredSocket(m_socket); + return; + } + case 334: { + + byte_t* challenge = 0; + size_t challengeLen = 0; + + byte_t* resp = 0; + size_t respLen = 0; + + try { + + // Extract challenge + saslContext->decodeB64(response->getText(), &challenge, &challengeLen); + + // Prepare response + saslSession->evaluateChallenge(challenge, challengeLen, &resp, &respLen); + + // Send response + const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n"; + m_socket->sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length()); + + if (m_tracer) { + m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange"); + } + + } catch (exceptions::sasl_exception& e) { + + if (challenge) { + delete [] challenge; + challenge = NULL; + } + + if (resp) { + delete [] resp; + resp = NULL; + } + + // Cancel SASL exchange + m_socket->send("*\r\n"); + + if (m_tracer) { + m_tracer->traceSend("*"); + } + + } catch (...) { + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + + throw; + } + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + + break; + } + default: + + cont = false; + break; + } + } + } + + throw exceptions::authentication_error("Could not authenticate using SASL: all mechanisms failed."); +} + +#endif // VMIME_HAVE_SASL_SUPPORT + + +#if VMIME_HAVE_TLS_SUPPORT + +void SMTPConnection::startTLS() { + + try { + + sendRequest(SMTPCommand::STARTTLS()); + + shared_ptr <SMTPResponse> resp = readResponse(); + + if (resp->getCode() != 220) { + + throw SMTPCommandError( + "STARTTLS", resp->getText(), resp->getCode(), resp->getEnhancedCode() + ); + } + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create( + getTransport()->getCertificateVerifier(), + getTransport()->getSession()->getTLSProperties() + ); + + shared_ptr <tls::TLSSocket> tlsSocket = tlsSession->getSocket(m_socket); + + tlsSocket->handshake(); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>( + m_cntInfos->getHost(), m_cntInfos->getPort(), tlsSession, tlsSocket + ); + + } catch (exceptions::command_error&) { + + // Non-fatal error + throw; + + } catch (exception&) { + + // Fatal error + internalDisconnect(); + throw; + } +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + +void SMTPConnection::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + internalDisconnect(); +} + + +void SMTPConnection::internalDisconnect() { + + if (isConnected()) { + + try { + + sendRequest(SMTPCommand::QUIT()); + + // Do not wait for server response. This is contrary to the RFC, but + // some servers never send a response to a QUIT command. + + } catch (exception&) { + + // Not important + } + } + + m_socket->disconnect(); + m_socket = null; + + m_timeoutHandler = null; + + m_authenticated = false; + m_extendedSMTP = false; + + m_secured = false; + m_cntInfos = null; +} + + +void SMTPConnection::sendRequest(const shared_ptr <SMTPCommand>& cmd) { + + cmd->writeToSocket(m_socket, m_tracer); +} + + +shared_ptr <SMTPResponse> SMTPConnection::readResponse() { + + shared_ptr <SMTPResponse> resp = SMTPResponse::readResponse( + m_tracer, m_socket, m_timeoutHandler, m_responseState + ); + + m_responseState = resp->getCurrentState(); + + return resp; +} + + +bool SMTPConnection::isConnected() const { + + return m_socket && m_socket->isConnected() && m_authenticated; +} + + +bool SMTPConnection::isSecuredConnection() const { + + return m_secured; +} + + +shared_ptr <connectionInfos> SMTPConnection::getConnectionInfos() const { + + return m_cntInfos; +} + + +shared_ptr <SMTPTransport> SMTPConnection::getTransport() { + + return m_transport.lock(); +} + + +shared_ptr <session> SMTPConnection::getSession() { + + return m_transport.lock()->getSession(); +} + + +shared_ptr <socket> SMTPConnection::getSocket() { + + return m_socket; +} + + +shared_ptr <tracer> SMTPConnection::getTracer() { + + return m_tracer; +} + + +shared_ptr <timeoutHandler> SMTPConnection::getTimeoutHandler() { + + return m_timeoutHandler; +} + + +shared_ptr <security::authenticator> SMTPConnection::getAuthenticator() { + + return m_auth; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPConnection.hpp b/vmime-master/src/vmime/net/smtp/SMTPConnection.hpp new file mode 100644 index 0000000..d8a2375 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPConnection.hpp @@ -0,0 +1,136 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTPCONNECTION_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCONNECTION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/messageId.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/session.hpp" +#include "vmime/net/connectionInfos.hpp" +#include "vmime/net/tracer.hpp" + +#include "vmime/net/smtp/SMTPCommand.hpp" +#include "vmime/net/smtp/SMTPResponse.hpp" + +#include "vmime/security/authenticator.hpp" + + +namespace vmime { +namespace net { + + +class socket; +class timeoutHandler; + + +namespace smtp { + + +class SMTPTransport; + + +/** Manage connection to a SMTP server. + */ +class VMIME_EXPORT SMTPConnection : public object { + +public: + + SMTPConnection( + const shared_ptr <SMTPTransport>& transport, + const shared_ptr <security::authenticator>& auth + ); + + ~SMTPConnection(); + + + virtual void connect(); + virtual bool isConnected() const; + virtual void disconnect(); + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + + virtual shared_ptr <SMTPTransport> getTransport(); + virtual shared_ptr <socket> getSocket(); + virtual shared_ptr <timeoutHandler> getTimeoutHandler(); + virtual shared_ptr <security::authenticator> getAuthenticator(); + virtual shared_ptr <session> getSession(); + virtual shared_ptr <tracer> getTracer(); + + void sendRequest(const shared_ptr <SMTPCommand>& cmd); + shared_ptr <SMTPResponse> readResponse(); + + bool hasExtension(const std::string& extName, std::vector <string>* params = NULL) const; + +private: + + void internalDisconnect(); + + void helo(); + void authenticate(); +#if VMIME_HAVE_SASL_SUPPORT + void authenticateSASL(); +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT + + + weak_ptr <SMTPTransport> m_transport; + + shared_ptr <security::authenticator> m_auth; + shared_ptr <socket> m_socket; + shared_ptr <timeoutHandler> m_timeoutHandler; + shared_ptr <tracer> m_tracer; + + SMTPResponse::state m_responseState; + + bool m_authenticated; + bool m_secured; + + shared_ptr <connectionInfos> m_cntInfos; + + bool m_extendedSMTP; + std::map <string, std::vector <string> > m_extensions; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCONNECTION_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPExceptions.cpp b/vmime-master/src/vmime/net/smtp/SMTPExceptions.cpp new file mode 100644 index 0000000..e3b4652 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPExceptions.cpp @@ -0,0 +1,212 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/SMTPExceptions.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +// +// SMTPCommandError +// + +SMTPCommandError::SMTPCommandError( + const string& command, + const string& response, + const string& desc, + const int statusCode, + const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other +) + : command_error(command, response, desc, other), + m_status(statusCode), + m_exStatus(extendedStatusCode) { + +} + + +SMTPCommandError::SMTPCommandError( + const string& command, + const string& response, + const int statusCode, + const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other +) + : command_error(command, response, "", other), + m_status(statusCode), + m_exStatus(extendedStatusCode) { + +} + + +SMTPCommandError::~SMTPCommandError() throw() { + +} + + +int SMTPCommandError::statusCode() const { + + return m_status; +} + + +const SMTPResponse::enhancedStatusCode SMTPCommandError::extendedStatusCode() const { + + return m_exStatus; +} + + +exception* SMTPCommandError::clone() const { + + return new SMTPCommandError(*this); +} + + +const char* SMTPCommandError::name() const throw() { + + return "SMTPCommandError"; +} + + +// +// SMTPMessageSizeExceedsMaxLimitsException +// + +SMTPMessageSizeExceedsMaxLimitsException::SMTPMessageSizeExceedsMaxLimitsException(const exception& other) + : net_exception("Message size exceeds maximum server limits (permanent error).", other) { + +} + + +SMTPMessageSizeExceedsMaxLimitsException::~SMTPMessageSizeExceedsMaxLimitsException() throw() { + +} + + +exception* SMTPMessageSizeExceedsMaxLimitsException::clone() const { + + return new SMTPMessageSizeExceedsMaxLimitsException(*this); +} + + +const char* SMTPMessageSizeExceedsMaxLimitsException::name() const throw() { + + return "SMTPMessageSizeExceedsMaxLimitsException"; +} + + +// +// SMTPMessageSizeExceedsCurLimitsException +// + +SMTPMessageSizeExceedsCurLimitsException::SMTPMessageSizeExceedsCurLimitsException(const exception& other) + : net_exception("Message size exceeds current server limits (temporary storage error).", other) { + +} + + +SMTPMessageSizeExceedsCurLimitsException::~SMTPMessageSizeExceedsCurLimitsException() throw() { + +} + + +exception* SMTPMessageSizeExceedsCurLimitsException::clone() const { + + return new SMTPMessageSizeExceedsCurLimitsException(*this); +} + + +const char* SMTPMessageSizeExceedsCurLimitsException::name() const throw() { + + return "SMTPMessageSizeExceedsCurLimitsException"; +} + + +// +// SMTPExtensionNotSupportedException +// + +SMTPExtensionNotSupportedException::SMTPExtensionNotSupportedException(const string &what, const exception& other) + : net_exception(what.empty() ? "A required extension is not supported by the SMTP server." : what, other) { + +} + + +SMTPExtensionNotSupportedException::~SMTPExtensionNotSupportedException() throw() { + +} + + +exception* SMTPExtensionNotSupportedException::clone() const { + + return new SMTPExtensionNotSupportedException(*this); +} + + +const char* SMTPExtensionNotSupportedException::name() const throw() { + + return "SMTPExtensionNotSupportedException"; +} + + +// +// SMTPDSNExtensionNotSupportedException +// + +SMTPDSNExtensionNotSupportedException::SMTPDSNExtensionNotSupportedException(const exception& other) + : SMTPExtensionNotSupportedException("RFC-1891 DSN extension is not supported by the SMTP server.", other) { + +} + + +SMTPDSNExtensionNotSupportedException::~SMTPDSNExtensionNotSupportedException() throw() { + +} + + +exception* SMTPDSNExtensionNotSupportedException::clone() const { + return new SMTPDSNExtensionNotSupportedException(*this); +} + + +const char* SMTPDSNExtensionNotSupportedException::name() const throw() { + + return "SMTPDSNExtensionNotSupportedException"; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPExceptions.hpp b/vmime-master/src/vmime/net/smtp/SMTPExceptions.hpp new file mode 100644 index 0000000..488ee2e --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPExceptions.hpp @@ -0,0 +1,159 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTPEXCEPTIONS_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPEXCEPTIONS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/exception.hpp" +#include "vmime/base.hpp" + +#include "vmime/net/smtp/SMTPResponse.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** SMTP Command error: a SMTP command failed. + */ +class VMIME_EXPORT SMTPCommandError : public exceptions::command_error { + +public: + + SMTPCommandError( + const string& command, + const string& response, + const string& desc, + const int statusCode, + const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other = NO_EXCEPTION + ); + + SMTPCommandError( + const string& command, + const string& response, + const int statusCode, const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other = NO_EXCEPTION + ); + + ~SMTPCommandError() throw(); + + /** Returns the SMTP status code for this error. + * + * @return status code (protocol-dependent) + */ + int statusCode() const; + + /** Returns the extended status code (following RFC-3463) for this + * error, if available. + * + * @return status code + */ + const SMTPResponse::enhancedStatusCode extendedStatusCode() const; + + + exception* clone() const; + const char* name() const throw(); + +private: + + int m_status; + SMTPResponse::enhancedStatusCode m_exStatus; +}; + + +/** SMTP error: message size exceeds maximum server limits. + * This is a permanent error. + */ +class VMIME_EXPORT SMTPMessageSizeExceedsMaxLimitsException : public exceptions::net_exception { + +public: + + SMTPMessageSizeExceedsMaxLimitsException(const exception& other = NO_EXCEPTION); + ~SMTPMessageSizeExceedsMaxLimitsException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +/** SMTP error: message size exceeds current server limits. + * This is a temporary error (you may retry later). + */ +class VMIME_EXPORT SMTPMessageSizeExceedsCurLimitsException : public exceptions::net_exception { + +public: + + SMTPMessageSizeExceedsCurLimitsException(const exception& other = NO_EXCEPTION); + ~SMTPMessageSizeExceedsCurLimitsException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +/** SMTP error: a required extension is not supported by the server. + */ +class VMIME_EXPORT SMTPExtensionNotSupportedException : public exceptions::net_exception { + +public: + + SMTPExtensionNotSupportedException(const string& what, const exception& other = NO_EXCEPTION); + ~SMTPExtensionNotSupportedException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +/** SMTP error: RFC-1891 DSN extension is not supported by the server. + */ +class VMIME_EXPORT SMTPDSNExtensionNotSupportedException : public SMTPExtensionNotSupportedException { + +public: + + SMTPDSNExtensionNotSupportedException(const exception& other = NO_EXCEPTION); + ~SMTPDSNExtensionNotSupportedException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPEXCEPTIONS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPResponse.cpp b/vmime-master/src/vmime/net/smtp/SMTPResponse.cpp new file mode 100644 index 0000000..2234705 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPResponse.cpp @@ -0,0 +1,366 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/SMTPResponse.hpp" + +#include "vmime/platform.hpp" +#include "vmime/utility/stringUtils.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/tracer.hpp" + +#include <cctype> + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPResponse::SMTPResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st +) + : m_socket(sok), + m_timeoutHandler(toh), + m_tracer(tr), + m_responseBuffer(st.responseBuffer), + m_responseContinues(false) { + +} + + +SMTPResponse::SMTPResponse(const SMTPResponse&) + : vmime::object() { + + // Not used +} + + +int SMTPResponse::getCode() const { + + const int firstCode = m_lines[0].getCode(); + + for (unsigned int i = 1 ; i < m_lines.size() ; ++i) { + + // All response codes returned must be equal + // or else this in an error... + if (m_lines[i].getCode() != firstCode) { + return 0; + } + } + + return firstCode; +} + + +const SMTPResponse::enhancedStatusCode SMTPResponse::getEnhancedCode() const { + + return m_lines[m_lines.size() - 1].getEnhancedCode(); +} + + +const string SMTPResponse::getText() const { + + string text = m_lines[0].getText(); + + for (unsigned int i = 1 ; i < m_lines.size() ; ++i) { + + text += '\n'; + text += m_lines[i].getText(); + } + + return text; +} + + +// static +shared_ptr <SMTPResponse> SMTPResponse::readResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st +) { + + shared_ptr <SMTPResponse> resp = + shared_ptr <SMTPResponse>(new SMTPResponse(tr, sok, toh, st)); + + resp->readResponse(); + + return resp; +} + + +void SMTPResponse::readResponse() { + + responseLine line = getNextResponse(); + m_lines.push_back(line); + + while (m_responseContinues) { + line = getNextResponse(); + m_lines.push_back(line); + } +} + + +const string SMTPResponse::readResponseLine() { + + string currentBuffer = m_responseBuffer; + + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + while (true) { + + // Get a line from the response buffer + const size_t lineEnd = currentBuffer.find_first_of('\n'); + + if (lineEnd != string::npos) { + + size_t actualLineEnd = lineEnd; + + if (actualLineEnd != 0 && currentBuffer[actualLineEnd - 1] == '\r') { // CRLF case + actualLineEnd--; + } + + const string line(currentBuffer.begin(), currentBuffer.begin() + actualLineEnd); + + currentBuffer.erase(currentBuffer.begin(), currentBuffer.begin() + lineEnd + 1); + m_responseBuffer = currentBuffer; + + if (m_tracer) { + m_tracer->traceReceive(line); + } + + return line; + } + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) { + + if (!m_timeoutHandler->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + + m_timeoutHandler->resetTimeOut(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) { // buffer is empty + m_socket->waitForRead(); + continue; + } + + currentBuffer += receiveBuffer; + } +} + + +const SMTPResponse::responseLine SMTPResponse::getNextResponse() { + + string line = readResponseLine(); + + const int code = extractResponseCode(line); + string text; + + m_responseContinues = (line.length() >= 4 && line[3] == '-'); + + if (line.length() > 4) { + text = utility::stringUtils::trim(line.substr(4)); + } else { + text = ""; + } + + return responseLine(code, text, extractEnhancedCode(text)); +} + + +// static +int SMTPResponse::extractResponseCode(const string& response) { + + int code = 0; + + if (response.length() >= 3) { + + code = (response[0] - '0') * 100 + + (response[1] - '0') * 10 + + (response[2] - '0'); + } + + return code; +} + + +// static +const SMTPResponse::enhancedStatusCode SMTPResponse::extractEnhancedCode(const string& responseText) { + + enhancedStatusCode enhCode; + + std::istringstream iss(responseText); + iss.imbue(std::locale::classic()); + + if (std::isdigit(iss.peek())) { + + iss >> enhCode.klass; + + if (iss.get() == '.' && std::isdigit(iss.peek())) { + + iss >> enhCode.subject; + + if (iss.get() == '.' && std::isdigit(iss.peek())) { + + iss >> enhCode.detail; + return enhCode; + } + } + } + + return enhancedStatusCode(); // no enhanced code found +} + + +const SMTPResponse::responseLine SMTPResponse::getLineAt(const size_t pos) const { + + return m_lines[pos]; +} + + +size_t SMTPResponse::getLineCount() const { + + return m_lines.size(); +} + + +const SMTPResponse::responseLine SMTPResponse::getLastLine() const { + + return m_lines[m_lines.size() - 1]; +} + + +const SMTPResponse::state SMTPResponse::getCurrentState() const { + + state st; + st.responseBuffer = m_responseBuffer; + + return st; +} + + + +// SMTPResponse::responseLine + +SMTPResponse::responseLine::responseLine( + const int code, + const string& text, + const enhancedStatusCode& enhCode +) + : m_code(code), + m_text(text), + m_enhCode(enhCode) { + +} + + +void SMTPResponse::responseLine::setCode(const int code) { + + m_code = code; +} + + +int SMTPResponse::responseLine::getCode() const { + + return m_code; +} + + +void SMTPResponse::responseLine::setEnhancedCode(const enhancedStatusCode& enhCode) { + + m_enhCode = enhCode; +} + + +const SMTPResponse::enhancedStatusCode SMTPResponse::responseLine::getEnhancedCode() const { + + return m_enhCode; +} + + +void SMTPResponse::responseLine::setText(const string& text) { + + m_text = text; +} + + +const string SMTPResponse::responseLine::getText() const { + + return m_text; +} + + + +// SMTPResponse::enhancedStatusCode + + +SMTPResponse::enhancedStatusCode::enhancedStatusCode() + : klass(0), + subject(0), + detail(0) { + +} + + +SMTPResponse::enhancedStatusCode::enhancedStatusCode(const enhancedStatusCode& enhCode) + : klass(enhCode.klass), + subject(enhCode.subject), + detail(enhCode.detail) { + +} + + +std::ostream& operator<<(std::ostream& os, const SMTPResponse::enhancedStatusCode& code) { + + os << code.klass << '.' << code.subject << '.' << code.detail; + return os; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPResponse.hpp b/vmime-master/src/vmime/net/smtp/SMTPResponse.hpp new file mode 100644 index 0000000..2eec7f4 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPResponse.hpp @@ -0,0 +1,200 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTPRESPONSE_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPRESPONSE_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 { +namespace net { + + +class socket; +class timeoutHandler; +class tracer; + + +namespace smtp { + + +/** A SMTP response, as sent by the server. + */ +class VMIME_EXPORT SMTPResponse : public object { + +public: + + /** Current state of response parser. */ + struct state { + + string responseBuffer; + }; + + /** Enhanced status code (as per RFC-3463). */ + struct enhancedStatusCode { + + enhancedStatusCode(); + enhancedStatusCode(const enhancedStatusCode& enhCode); + + unsigned short klass; /**< Success/failure. */ + unsigned short subject; /**< Source of anomaly. */ + unsigned short detail; /**< Precise error condition. */ + }; + + /** An element of a SMTP response. */ + class responseLine { + + public: + + responseLine(const int code, const string& text, const enhancedStatusCode& enhCode); + + void setCode(const int code); + int getCode() const; + + void setEnhancedCode(const enhancedStatusCode& enhCode); + const enhancedStatusCode getEnhancedCode() const; + + void setText(const string& text); + const string getText() const; + + private: + + int m_code; + string m_text; + enhancedStatusCode m_enhCode; + }; + + /** Receive and parse a new SMTP response from the + * specified socket. + * + * @param tr tracer + * @param sok socket from which to read + * @param toh time-out handler + * @param st previous state of response parser for the specified socket + * @return SMTP response + * @throws exceptions::operation_timed_out if no data + * has been received within the granted time + */ + static shared_ptr <SMTPResponse> readResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st + ); + + /** Return the SMTP response code. + * + * @return response code + */ + int getCode() const; + + /** Return the SMTP enhanced status code, if available. + * + * @return enhanced status code + */ + const enhancedStatusCode getEnhancedCode() const; + + /** Return the SMTP response text. + * The text of each line is concatenated. + * + * @return response text + */ + const string getText() const; + + /** Return the response line at the specified position. + * + * @param pos line index + * @return line at the specified index + */ + const responseLine getLineAt(const size_t pos) const; + + /** Return the number of lines in the response. + * + * @return number of lines in the response + */ + size_t getLineCount() const; + + /** Return the last line in the response. + * + * @return last response line + */ + const responseLine getLastLine() const; + + /** Returns the current state of the response parser. + * + * @return current parser state + */ + const state getCurrentState() const; + +private: + + SMTPResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st + ); + + SMTPResponse(const SMTPResponse&); + + void readResponse(); + + const string readResponseLine(); + const responseLine getNextResponse(); + + static int extractResponseCode(const string& response); + static const enhancedStatusCode extractEnhancedCode(const string& responseText); + + + std::vector <responseLine> m_lines; + + shared_ptr <socket> m_socket; + shared_ptr <timeoutHandler> m_timeoutHandler; + shared_ptr <tracer> m_tracer; + + string m_responseBuffer; + bool m_responseContinues; +}; + + +VMIME_EXPORT std::ostream& operator<<(std::ostream& os, const SMTPResponse::enhancedStatusCode& code); + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPRESPONSE_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPSTransport.cpp b/vmime-master/src/vmime/net/smtp/SMTPSTransport.cpp new file mode 100644 index 0000000..5158aa9 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPSTransport.cpp @@ -0,0 +1,82 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/SMTPSTransport.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPSTransport::SMTPSTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth +) + : SMTPTransport(sess, auth, true) { + +} + + +SMTPSTransport::~SMTPSTransport() { + +} + + +const string SMTPSTransport::getProtocolName() const { + + return "smtps"; +} + + + +// Service infos + +SMTPServiceInfos SMTPSTransport::sm_infos(true); + + +const serviceInfos& SMTPSTransport::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& SMTPSTransport::getInfos() const { + + return sm_infos; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPSTransport.hpp b/vmime-master/src/vmime/net/smtp/SMTPSTransport.hpp new file mode 100644 index 0000000..f19b3c5 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPSTransport.hpp @@ -0,0 +1,74 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTPSSTORE_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPSSTORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/smtp/SMTPTransport.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** SMTPS transport service. + */ +class VMIME_EXPORT SMTPSTransport : public SMTPTransport { + +public: + + SMTPSTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth + ); + + ~SMTPSTransport(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + +private: + + static SMTPServiceInfos sm_infos; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPSSTORE_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.cpp b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.cpp new file mode 100644 index 0000000..a2ee3d5 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.cpp @@ -0,0 +1,144 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/SMTPServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPServiceInfos::SMTPServiceInfos(const bool smtps) + : m_smtps(smtps) { + +} + + +const string SMTPServiceInfos::getPropertyPrefix() const { + + if (m_smtps) { + return "transport.smtps."; + } else { + return "transport.smtp."; + } +} + + +const SMTPServiceInfos::props& SMTPServiceInfos::getProperties() const { + + static props smtpProps = { + // SMTP-specific options + property("options.need-authentication", serviceInfos::property::TYPE_BOOLEAN, "false"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "false"), +#endif // VMIME_HAVE_SASL_SUPPORT + + property("options.pipelining", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.chunking", serviceInfos::property::TYPE_BOOLEAN, "true"), + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "25"), + }; + + static props smtpsProps = { + // SMTP-specific options + property("options.need-authentication", serviceInfos::property::TYPE_BOOLEAN, "false"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "false"), +#endif // VMIME_HAVE_SASL_SUPPORT + + property("options.pipelining", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.chunking", serviceInfos::property::TYPE_BOOLEAN, "true"), + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "465"), + }; + + return m_smtps ? smtpsProps : smtpProps; +} + + +const std::vector <serviceInfos::property> SMTPServiceInfos::getAvailableProperties() const { + + std::vector <property> list; + const props& p = getProperties(); + + // SMTP-specific options + list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH); +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + list.push_back(p.PROPERTY_AUTH_USERNAME); + list.push_back(p.PROPERTY_AUTH_PASSWORD); + +#if VMIME_HAVE_TLS_SUPPORT + if (!m_smtps) { + list.push_back(p.PROPERTY_CONNECTION_TLS); + list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + list.push_back(p.PROPERTY_SERVER_ADDRESS); + list.push_back(p.PROPERTY_SERVER_PORT); + + return list; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.hpp b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.hpp new file mode 100644 index 0000000..005a0a2 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.hpp @@ -0,0 +1,93 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTPSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** Information about SMTP service. + */ +class VMIME_EXPORT SMTPServiceInfos : public serviceInfos { + +public: + + SMTPServiceInfos(const bool smtps); + + struct props { + // SMTP-specific options + serviceInfos::property PROPERTY_OPTIONS_NEEDAUTH; +#if VMIME_HAVE_SASL_SUPPORT + serviceInfos::property PROPERTY_OPTIONS_SASL; + serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; +#endif // VMIME_HAVE_SASL_SUPPORT + + serviceInfos::property PROPERTY_OPTIONS_PIPELINING; + serviceInfos::property PROPERTY_OPTIONS_CHUNKING; + + // Common properties + serviceInfos::property PROPERTY_AUTH_USERNAME; + serviceInfos::property PROPERTY_AUTH_PASSWORD; + +#if VMIME_HAVE_TLS_SUPPORT + serviceInfos::property PROPERTY_CONNECTION_TLS; + serviceInfos::property PROPERTY_CONNECTION_TLS_REQUIRED; +#endif // VMIME_HAVE_TLS_SUPPORT + + serviceInfos::property PROPERTY_SERVER_ADDRESS; + serviceInfos::property PROPERTY_SERVER_PORT; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector <serviceInfos::property> getAvailableProperties() const; + +private: + + const bool m_smtps; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPTransport.cpp b/vmime-master/src/vmime/net/smtp/SMTPTransport.cpp new file mode 100644 index 0000000..561bd59 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPTransport.cpp @@ -0,0 +1,502 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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/SMTPTransport.hpp" +#include "vmime/net/smtp/SMTPResponse.hpp" +#include "vmime/net/smtp/SMTPCommand.hpp" +#include "vmime/net/smtp/SMTPCommandSet.hpp" +#include "vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp" +#include "vmime/net/smtp/SMTPExceptions.hpp" + +#include "vmime/exception.hpp" +#include "vmime/mailboxList.hpp" +#include "vmime/message.hpp" + +#include "vmime/utility/filteredStream.hpp" +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/outputStreamSocketAdapter.hpp" +#include "vmime/utility/streamUtils.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPTransport::SMTPTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured +) + : transport(sess, getInfosInstance(), auth), + m_isSMTPS(secured), + m_needReset(false) { + +} + + +SMTPTransport::~SMTPTransport() { + + try { + + if (isConnected()) { + disconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +const string SMTPTransport::getProtocolName() const { + + return "smtp"; +} + + +bool SMTPTransport::isSMTPS() const { + + return m_isSMTPS; +} + + +void SMTPTransport::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + m_connection = make_shared <SMTPConnection>( + dynamicCast <SMTPTransport>(shared_from_this()), getAuthenticator() + ); + + m_connection->connect(); +} + + +bool SMTPTransport::isConnected() const { + + return m_connection && m_connection->isConnected(); +} + + +bool SMTPTransport::isSecuredConnection() const { + + if (!m_connection) { + return false; + } + + return m_connection->isSecuredConnection(); +} + + +shared_ptr <connectionInfos> SMTPTransport::getConnectionInfos() const { + + if (!m_connection) { + return null; + } + + return m_connection->getConnectionInfos(); +} + + +shared_ptr <SMTPConnection> SMTPTransport::getConnection() { + + return m_connection; +} + + +void SMTPTransport::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + m_connection->disconnect(); + m_connection = null; +} + + +void SMTPTransport::noop() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + m_connection->sendRequest(SMTPCommand::NOOP()); + + shared_ptr <SMTPResponse> resp = m_connection->readResponse(); + + if (resp->getCode() != 250) { + throw SMTPCommandError("NOOP", resp->getText(), resp->getCode(), resp->getEnhancedCode()); + } +} + + +// 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, + const size_t size, + const dsnAttributes& dsnAttrs +) { + + // If no recipient/expeditor was found, throw an exception + if (recipients.isEmpty()) { + throw exceptions::no_recipient(); + } else if (expeditor.isEmpty()) { + throw exceptions::no_expeditor(); + } + + // If DSN extension is used, ensure it is supported by the server + if (!dsnAttrs.isEmpty() && !m_connection->hasExtension("DSN")) { + throw SMTPDSNExtensionNotSupportedException(); + } + + + const bool needReset = m_needReset; + const bool hasPipelining = m_connection->hasExtension("PIPELINING") && + getInfos().getPropertyValue <bool>(getSession(), + dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().PROPERTY_OPTIONS_PIPELINING); + + shared_ptr <SMTPResponse> resp; + shared_ptr <SMTPCommandSet> commands = SMTPCommandSet::create(hasPipelining); + + // Emit a "RSET" command if we previously sent a message on this connection + if (needReset) { + commands->addCommand(SMTPCommand::RSET()); + } + + // Check whether we need SMTPUTF8 + const bool hasSMTPUTF8 = m_connection->hasExtension("SMTPUTF8"); + bool needSMTPUTF8 = false; + + if (!sender.isEmpty()) { + needSMTPUTF8 = needSMTPUTF8 || mailboxNeedsUTF8(sender); + } else { + needSMTPUTF8 = needSMTPUTF8 || mailboxNeedsUTF8(expeditor); + } + + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { + + const mailbox& mbox = *recipients.getMailboxAt(i); + needSMTPUTF8 = needSMTPUTF8 || mailboxNeedsUTF8(mbox); + } + + // Emit the "MAIL" command + const bool hasSize = m_connection->hasExtension("SIZE"); + + if (!sender.isEmpty()) { + + commands->addCommand( + SMTPCommand::MAIL( + sender, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0, + dsnAttrs.getNotificationConditions(), + dsnAttrs.getEnvelopId() + ) + ); + + } else { + + commands->addCommand( + SMTPCommand::MAIL( + expeditor, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0, + dsnAttrs.getNotificationConditions(), + dsnAttrs.getEnvelopId() + ) + ); + } + + // Now, we will need to reset next time + m_needReset = true; + + // 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, hasSMTPUTF8 && needSMTPUTF8, + dsnAttrs.getNotificationConditions())); + } + + // Prepare sending of message data + if (sendDATACommand) { + commands->addCommand(SMTPCommand::DATA()); + } + + // Read response for "RSET" command + if (needReset) { + + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + resp = m_connection->readResponse(); + + if (resp->getCode() != 250 && + resp->getCode() != 200) { // RFC-876: << In reply to a RSET and/or a NOOP command, + // some servers reply "200" >> + + disconnect(); + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } + + // Read response for "MAIL" command + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + if ((resp = m_connection->readResponse())->getCode() != 250) { + + // SIZE extension: insufficient system storage + if (resp->getCode() == 452) { + + throw SMTPMessageSizeExceedsCurLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // SIZE extension: message size exceeds fixed maximum message size + } else if (resp->getCode() == 552) { + + throw SMTPMessageSizeExceedsMaxLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // Other error + } else { + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } + + // Read responses for "RCPT TO" commands + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { + + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + resp = m_connection->readResponse(); + + if (resp->getCode() != 250 && + resp->getCode() != 251) { + + // SIZE extension: insufficient system storage + if (resp->getCode() == 452) { + + throw SMTPMessageSizeExceedsCurLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // SIZE extension: message size exceeds fixed maximum message size + } else if (resp->getCode() == 552) { + + throw SMTPMessageSizeExceedsMaxLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // Other error + } else { + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } + } + + // Read response for "DATA" command + if (sendDATACommand) { + + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + if ((resp = m_connection->readResponse())->getCode() != 354) { + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } +} + + +void SMTPTransport::send( + const mailbox& expeditor, + const mailboxList& recipients, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress, + const mailbox& sender, + const dsnAttributes& dsnAttrs +) { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + // Send message envelope + sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ true, size, + dsnAttrs); + + // Send the message data + // Stream copy with "\n." to "\n.." transformation + utility::outputStreamSocketAdapter sos(*m_connection->getSocket()); + utility::dotFilteredOutputStream fos(sos); + + utility::bufferedStreamCopy(is, fos, size, progress); + + fos.flush(); + + // Send end-of-data delimiter + m_connection->getSocket()->send("\r\n.\r\n"); + + if (m_connection->getTracer()) { + m_connection->getTracer()->traceSendBytes(size); + m_connection->getTracer()->traceSend("."); + } + + shared_ptr <SMTPResponse> resp; + + if ((resp = m_connection->readResponse())->getCode() != 250) { + throw SMTPCommandError("DATA", resp->getText(), resp->getCode(), resp->getEnhancedCode()); + } +} + + +void SMTPTransport::send( + const shared_ptr <vmime::message>& msg, + const mailbox& expeditor, + const mailboxList& recipients, + utility::progressListener* progress, + const mailbox& sender, + const dsnAttributes& dsnAttrs +) { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + // Generate the message with Internationalized Email support, + // if this is supported by the SMTP server + generationContext ctx(generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(m_connection->hasExtension("SMTPUTF8")); + + // If CHUNKING is not supported, generate the message to a temporary + // buffer then use the send() method which takes an inputStream + if (!m_connection->hasExtension("CHUNKING") || + !getInfos().getPropertyValue <bool>(getSession(), + dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().PROPERTY_OPTIONS_CHUNKING)) { + + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ctx, ossAdapter); + + const string& str(oss.str()); + + utility::inputStreamStringAdapter isAdapter(str); + + send(expeditor, recipients, isAdapter, str.length(), progress, sender, dsnAttrs); + return; + } + + // Send message envelope + const size_t msgSize = msg->getGeneratedSize(ctx); + + sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ false, msgSize, dsnAttrs); + + // Send the message by chunks + SMTPChunkingOutputStreamAdapter chunkStream(m_connection, msgSize, progress); + + msg->generate(ctx, chunkStream); + + chunkStream.flush(); +} + + + +// Service infos + +SMTPServiceInfos SMTPTransport::sm_infos(false); + + +const serviceInfos& SMTPTransport::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& SMTPTransport::getInfos() const { + + return sm_infos; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPTransport.hpp b/vmime-master/src/vmime/net/smtp/SMTPTransport.hpp new file mode 100644 index 0000000..cd7c712 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPTransport.hpp @@ -0,0 +1,144 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTPTRANSPORT_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/transport.hpp" +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" + +#include "vmime/net/smtp/SMTPServiceInfos.hpp" +#include "vmime/net/smtp/SMTPConnection.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +class SMTPCommand; + + +/** SMTP transport service. + */ +class VMIME_EXPORT SMTPTransport : public transport { + +public: + + SMTPTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured = false + ); + + ~SMTPTransport(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + + void connect(); + bool isConnected() const; + void disconnect(); + + void noop(); + + void send( + const mailbox& expeditor, + const mailboxList& recipients, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress = NULL, + const mailbox& sender = mailbox(), + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + void send( + const shared_ptr <vmime::message>& msg, + const mailbox& expeditor, + const mailboxList& recipients, + utility::progressListener* progress = NULL, + const mailbox& sender = mailbox(), + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + shared_ptr <SMTPConnection> getConnection(); + + bool isSMTPS() const; + +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. + * + * @param expeditor expeditor mailbox + * @param recipients list of recipient mailboxes + * @param sender envelope sender (if empty, expeditor will be used) + * @param sendDATACommand if true, the DATA command will be sent + * @param size message size, in bytes (or 0, if not known) + * @param dsnAttributes attributes for Delivery Status Notification (if needed) + */ + void sendEnvelope( + const mailbox& expeditor, + const mailboxList& recipients, + const mailbox& sender, + bool sendDATACommand, + const size_t size, + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + + shared_ptr <SMTPConnection> m_connection; + + + const bool m_isSMTPS; + + bool m_needReset; + + // Service infos + static SMTPServiceInfos sm_infos; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/smtp.hpp b/vmime-master/src/vmime/net/smtp/smtp.hpp new file mode 100644 index 0000000..4c0b17d --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/smtp.hpp @@ -0,0 +1,33 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_SMTP_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTP_HPP_INCLUDED + + +#include "vmime/net/smtp/SMTPTransport.hpp" +#include "vmime/net/smtp/SMTPSTransport.hpp" +#include "vmime/net/smtp/SMTPExceptions.hpp" + + +#endif // VMIME_NET_SMTP_SMTP_HPP_INCLUDED |