aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct1
-rw-r--r--src/net/smtp/SMTPConnection.cpp612
-rw-r--r--src/net/smtp/SMTPTransport.cpp526
-rw-r--r--vmime/net/smtp/SMTPConnection.hpp131
-rw-r--r--vmime/net/smtp/SMTPTransport.hpp32
5 files changed, 797 insertions, 505 deletions
diff --git a/SConstruct b/SConstruct
index 0200a1cc..aa477018 100644
--- a/SConstruct
+++ b/SConstruct
@@ -254,6 +254,7 @@ libvmime_messaging_proto_sources = [
[
'net/smtp/SMTPCommand.cpp', 'net/smtp/SMTPCommand.hpp',
'net/smtp/SMTPCommandSet.cpp', 'net/smtp/SMTPCommandSet.hpp',
+ 'net/smtp/SMTPConnection.cpp', 'net/smtp/SMTPConnection.hpp',
'net/smtp/SMTPResponse.cpp', 'net/smtp/SMTPResponse.hpp',
'net/smtp/SMTPServiceInfos.cpp', 'net/smtp/SMTPServiceInfos.hpp',
'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp',
diff --git a/src/net/smtp/SMTPConnection.cpp b/src/net/smtp/SMTPConnection.cpp
new file mode 100644
index 00000000..c5ec7b51
--- /dev/null
+++ b/src/net/smtp/SMTPConnection.cpp
@@ -0,0 +1,612 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/config.hpp"
+
+
+#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
+
+
+#include "vmime/net/smtp/SMTPConnection.hpp"
+#include "vmime/net/smtp/SMTPTransport.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"
+#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.acquire()->getInfos().getPropertyValue <type>(getSession(), \
+ dynamic_cast <const SMTPServiceInfos&>(m_transport.acquire()->getInfos()).getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (m_transport.acquire()->getInfos().hasProperty(getSession(), \
+ dynamic_cast <const SMTPServiceInfos&>(m_transport.acquire()->getInfos()).getProperties().prop))
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+
+SMTPConnection::SMTPConnection(ref <SMTPTransport> transport, ref <security::authenticator> auth)
+ : m_transport(transport), m_auth(auth), m_socket(NULL), m_timeoutHandler(NULL),
+ m_authenticated(false), m_secured(false), m_extendedSMTP(false)
+{
+}
+
+
+SMTPConnection::~SMTPConnection()
+{
+ try
+ {
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+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);
+
+ ref <SMTPTransport> transport = m_transport.acquire();
+
+ // 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);
+
+#if VMIME_HAVE_TLS_SUPPORT
+ if (transport->isSMTPS()) // dedicated port/SMTPS
+ {
+ ref <tls::TLSSession> tlsSession =
+ tls::TLSSession::create(transport->getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ m_socket = tlsSocket;
+
+ m_secured = true;
+ m_cntInfos = vmime::create <tls::TLSSecuredConnectionInfos>(address, port, tlsSession, tlsSocket);
+ }
+ else
+#endif // VMIME_HAVE_TLS_SUPPORT
+ {
+ m_cntInfos = vmime::create <defaultConnectionInfos>(address, port);
+ }
+
+ m_socket->connect(address, port);
+
+ // Connection
+ //
+ // eg: C: <connection to server>
+ // --- S: 220 smtp.domain.com Service ready
+
+ ref <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()));
+
+ ref <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);
+
+ 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.acquire());
+
+#if VMIME_HAVE_SASL_SUPPORT
+ // First, try SASL authentication
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
+ {
+ try
+ {
+ authenticateSASL();
+
+ m_authenticated = true;
+ return;
+ }
+ catch (exceptions::authentication_error& e)
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
+ {
+ // Can't fallback on normal authentication
+ internalDisconnect();
+ throw e;
+ }
+ else
+ {
+ // Ignore, will try normal authentication
+ }
+ }
+ catch (exception& e)
+ {
+ internalDisconnect();
+ throw e;
+ }
+ }
+#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 (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
+ 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 <ref <security::sasl::SASLMechanism> > mechList;
+
+ ref <security::sasl::SASLContext> saslContext =
+ vmime::create <security::sasl::SASLContext>();
+
+ 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
+ ref <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 = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
+ 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)
+ {
+ ref <security::sasl::SASLMechanism> mech = mechList[i];
+
+ ref <security::sasl::SASLSession> saslSession =
+ saslContext->createSession("smtp", getAuthenticator(), mech);
+
+ saslSession->init();
+
+ sendRequest(SMTPCommand::AUTH(mech->getName()));
+
+ for (bool cont = true ; cont ; )
+ {
+ ref <SMTPResponse> response = readResponse();
+
+ switch (response->getCode())
+ {
+ case 235:
+ {
+ m_socket = saslSession->getSecuredSocket(m_socket);
+ return;
+ }
+ case 334:
+ {
+ byte_t* challenge = 0;
+ long challengeLen = 0;
+
+ byte_t* resp = 0;
+ long respLen = 0;
+
+ try
+ {
+ // Extract challenge
+ saslContext->decodeB64(response->getText(), &challenge, &challengeLen);
+
+ // Prepare response
+ saslSession->evaluateChallenge
+ (challenge, challengeLen, &resp, &respLen);
+
+ // Send response
+ m_socket->send(saslContext->encodeB64(resp, respLen) + "\r\n");
+ }
+ catch (exceptions::sasl_exception& e)
+ {
+ if (challenge)
+ {
+ delete [] challenge;
+ challenge = NULL;
+ }
+
+ if (resp)
+ {
+ delete [] resp;
+ resp = NULL;
+ }
+
+ // Cancel SASL exchange
+ m_socket->sendRaw("*\r\n", 3);
+ }
+ 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());
+
+ ref <SMTPResponse> resp = readResponse();
+
+ if (resp->getCode() != 220)
+ throw exceptions::command_error("STARTTLS", resp->getText());
+
+ ref <tls::TLSSession> tlsSession =
+ tls::TLSSession::create(getTransport()->getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ tlsSocket->handshake(m_timeoutHandler);
+
+ m_socket = tlsSocket;
+
+ m_secured = true;
+ m_cntInfos = vmime::create <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()
+{
+ try
+ {
+ sendRequest(SMTPCommand::QUIT());
+ readResponse();
+ }
+ 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(ref <SMTPCommand> cmd)
+{
+ cmd->writeToSocket(m_socket);
+}
+
+
+ref <SMTPResponse> SMTPConnection::readResponse()
+{
+ ref <SMTPResponse> resp = SMTPResponse::readResponse
+ (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;
+}
+
+
+ref <connectionInfos> SMTPConnection::getConnectionInfos() const
+{
+ return m_cntInfos;
+}
+
+
+ref <SMTPTransport> SMTPConnection::getTransport()
+{
+ return m_transport.acquire();
+}
+
+
+ref <session> SMTPConnection::getSession()
+{
+ return m_transport.acquire()->getSession();
+}
+
+
+ref <socket> SMTPConnection::getSocket()
+{
+ return m_socket;
+}
+
+
+ref <timeoutHandler> SMTPConnection::getTimeoutHandler()
+{
+ return m_timeoutHandler;
+}
+
+
+ref <security::authenticator> SMTPConnection::getAuthenticator()
+{
+ return m_auth;
+}
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp
index 65c999bf..9e00ac4d 100644
--- a/src/net/smtp/SMTPTransport.cpp
+++ b/src/net/smtp/SMTPTransport.cpp
@@ -33,7 +33,6 @@
#include "vmime/net/smtp/SMTPCommandSet.hpp"
#include "vmime/exception.hpp"
-#include "vmime/platform.hpp"
#include "vmime/mailboxList.hpp"
#include "vmime/message.hpp"
@@ -44,26 +43,6 @@
#include "vmime/utility/outputStreamAdapter.hpp"
#include "vmime/utility/inputStreamStringAdapter.hpp"
-#include "vmime/net/defaultConnectionInfos.hpp"
-
-#if VMIME_HAVE_SASL_SUPPORT
- #include "vmime/security/sasl/SASLContext.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) \
- (getInfos().getPropertyValue <type>(getSession(), \
- dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().prop))
-#define HAS_PROPERTY(prop) \
- (getInfos().hasProperty(getSession(), \
- dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().prop))
-
namespace vmime {
namespace net {
@@ -71,9 +50,7 @@ namespace smtp {
SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth, const bool secured)
- : transport(sess, getInfosInstance(), auth), m_socket(NULL),
- m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL),
- m_isSMTPS(secured), m_secured(false), m_needReset(false)
+ : transport(sess, getInfosInstance(), auth), m_isSMTPS(secured), m_needReset(false)
{
}
@@ -84,8 +61,6 @@ SMTPTransport::~SMTPTransport()
{
if (isConnected())
disconnect();
- else if (m_socket)
- internalDisconnect();
}
catch (vmime::exception&)
{
@@ -100,452 +75,69 @@ const string SMTPTransport::getProtocolName() const
}
-void SMTPTransport::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);
-
- // Create the time-out handler
- if (getTimeoutHandlerFactory())
- m_timeoutHandler = getTimeoutHandlerFactory()->create();
-
- // Create and connect the socket
- m_socket = getSocketFactory()->create(m_timeoutHandler);
-
-#if VMIME_HAVE_TLS_SUPPORT
- if (m_isSMTPS) // dedicated port/SMTPS
- {
- ref <tls::TLSSession> tlsSession =
- tls::TLSSession::create(getCertificateVerifier());
-
- ref <tls::TLSSocket> tlsSocket =
- tlsSession->getSocket(m_socket);
-
- m_socket = tlsSocket;
-
- m_secured = true;
- m_cntInfos = vmime::create <tls::TLSSecuredConnectionInfos>(address, port, tlsSession, tlsSocket);
- }
- else
-#endif // VMIME_HAVE_TLS_SUPPORT
- {
- m_cntInfos = vmime::create <defaultConnectionInfos>(address, port);
- }
-
- m_socket->connect(address, port);
-
- // Connection
- //
- // eg: C: <connection to server>
- // --- S: 220 smtp.domain.com Service ready
-
- ref <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 (!m_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_authentified = true;
-}
-
-
-void SMTPTransport::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()));
-
- ref <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);
-
- 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;
- }
- }
-}
-
-
-void SMTPTransport::authenticate()
+bool SMTPTransport::isSMTPS() const
{
- if (!m_extendedSMTP)
- {
- internalDisconnect();
- throw exceptions::command_error("AUTH", "ESMTP not supported.");
- }
-
- getAuthenticator()->setService(thisRef().dynamicCast <service>());
-
-#if VMIME_HAVE_SASL_SUPPORT
- // First, try SASL authentication
- if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
- {
- try
- {
- authenticateSASL();
-
- m_authentified = true;
- return;
- }
- catch (exceptions::authentication_error& e)
- {
- if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
- {
- // Can't fallback on normal authentication
- internalDisconnect();
- throw e;
- }
- else
- {
- // Ignore, will try normal authentication
- }
- }
- catch (exception& e)
- {
- internalDisconnect();
- throw e;
- }
- }
-#endif // VMIME_HAVE_SASL_SUPPORT
-
- // No other authentication method is possible
- throw exceptions::authentication_error("All authentication methods failed");
+ return m_isSMTPS;
}
-#if VMIME_HAVE_SASL_SUPPORT
-
-void SMTPTransport::authenticateSASL()
+void SMTPTransport::connect()
{
- if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
- throw exceptions::authentication_error("No SASL authenticator available.");
-
- // Obtain SASL mechanisms supported by server from ESMTP extensions
- const std::vector <string> saslMechs =
- (m_extensions.find("AUTH") != m_extensions.end())
- ? m_extensions["AUTH"] : std::vector <string>();
-
- if (saslMechs.empty())
- throw exceptions::authentication_error("No SASL mechanism available.");
-
- std::vector <ref <security::sasl::SASLMechanism> > mechList;
-
- ref <security::sasl::SASLContext> saslContext =
- vmime::create <security::sasl::SASLContext>();
-
- 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
- ref <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 = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
- 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)
- {
- ref <security::sasl::SASLMechanism> mech = mechList[i];
-
- ref <security::sasl::SASLSession> saslSession =
- saslContext->createSession("smtp", getAuthenticator(), mech);
-
- saslSession->init();
-
- sendRequest(SMTPCommand::AUTH(mech->getName()));
-
- for (bool cont = true ; cont ; )
- {
- ref <SMTPResponse> response = readResponse();
-
- switch (response->getCode())
- {
- case 235:
- {
- m_socket = saslSession->getSecuredSocket(m_socket);
- return;
- }
- case 334:
- {
- byte_t* challenge = 0;
- long challengeLen = 0;
-
- byte_t* resp = 0;
- long respLen = 0;
-
- try
- {
- // Extract challenge
- saslContext->decodeB64(response->getText(), &challenge, &challengeLen);
-
- // Prepare response
- saslSession->evaluateChallenge
- (challenge, challengeLen, &resp, &respLen);
-
- // Send response
- m_socket->send(saslContext->encodeB64(resp, respLen) + "\r\n");
- }
- catch (exceptions::sasl_exception& e)
- {
- if (challenge)
- {
- delete [] challenge;
- challenge = NULL;
- }
-
- if (resp)
- {
- delete [] resp;
- resp = NULL;
- }
-
- // Cancel SASL exchange
- m_socket->sendRaw("*\r\n", 3);
- }
- 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 (isConnected())
+ throw exceptions::already_connected();
-#if VMIME_HAVE_TLS_SUPPORT
+ m_connection = vmime::create <SMTPConnection>
+ (thisRef().dynamicCast <SMTPTransport>(), getAuthenticator());
-void SMTPTransport::startTLS()
-{
try
{
- sendRequest(SMTPCommand::STARTTLS());
-
- ref <SMTPResponse> resp = readResponse();
-
- if (resp->getCode() != 220)
- throw exceptions::command_error("STARTTLS", resp->getText());
-
- ref <tls::TLSSession> tlsSession =
- tls::TLSSession::create(getCertificateVerifier());
-
- ref <tls::TLSSocket> tlsSocket =
- tlsSession->getSocket(m_socket);
-
- tlsSocket->handshake(m_timeoutHandler);
-
- m_socket = tlsSocket;
-
- m_secured = true;
- m_cntInfos = vmime::create <tls::TLSSecuredConnectionInfos>
- (m_cntInfos->getHost(), m_cntInfos->getPort(), tlsSession, tlsSocket);
- }
- catch (exceptions::command_error&)
- {
- // Non-fatal error
- throw;
+ m_connection->connect();
}
- catch (exception&)
+ catch (std::exception&)
{
- // Fatal error
- internalDisconnect();
+ m_connection = NULL;
throw;
}
}
-#endif // VMIME_HAVE_TLS_SUPPORT
-
bool SMTPTransport::isConnected() const
{
- return (m_socket && m_socket->isConnected() && m_authentified);
+ return m_connection && m_connection->isConnected();
}
bool SMTPTransport::isSecuredConnection() const
{
- return m_secured;
+ if (m_connection == NULL)
+ return false;
+
+ return m_connection->isSecuredConnection();
}
ref <connectionInfos> SMTPTransport::getConnectionInfos() const
{
- return m_cntInfos;
+ if (m_connection == NULL)
+ return NULL;
+
+ return m_connection->getConnectionInfos();
}
-void SMTPTransport::disconnect()
+ref <SMTPConnection> SMTPTransport::getConnection()
{
- if (!isConnected())
- throw exceptions::not_connected();
-
- internalDisconnect();
+ return m_connection;
}
-void SMTPTransport::internalDisconnect()
+void SMTPTransport::disconnect()
{
- try
- {
- sendRequest(SMTPCommand::QUIT());
- readResponse();
- }
- catch (exception&)
- {
- // Not important
- }
-
- m_socket->disconnect();
- m_socket = NULL;
-
- m_timeoutHandler = NULL;
-
- m_authentified = false;
- m_extendedSMTP = false;
+ if (!isConnected())
+ throw exceptions::not_connected();
- m_secured = false;
- m_cntInfos = NULL;
+ m_connection->disconnect();
+ m_connection = NULL;
}
@@ -554,9 +146,9 @@ void SMTPTransport::noop()
if (!isConnected())
throw exceptions::not_connected();
- sendRequest(SMTPCommand::NOOP());
+ m_connection->sendRequest(SMTPCommand::NOOP());
- ref <SMTPResponse> resp = readResponse();
+ ref <SMTPResponse> resp = m_connection->readResponse();
if (resp->getCode() != 250)
throw exceptions::command_error("NOOP", resp->getText());
@@ -579,8 +171,7 @@ void SMTPTransport::send
const bool needReset = m_needReset;
- const bool hasPipelining =
- m_extensions.find("PIPELINING") != m_extensions.end();
+ const bool hasPipelining = m_connection->hasExtension("PIPELINING");
ref <SMTPResponse> resp;
ref <SMTPCommandSet> commands = SMTPCommandSet::create(hasPipelining);
@@ -590,8 +181,7 @@ void SMTPTransport::send
commands->addCommand(SMTPCommand::RSET());
// Emit the "MAIL" command
- const bool hasSMTPUTF8 =
- m_extensions.find("SMTPUTF8") != m_extensions.end();
+ const bool hasSMTPUTF8 = m_connection->hasExtension("SMTPUTF8");
if (!sender.isEmpty())
commands->addCommand(SMTPCommand::MAIL(sender, hasSMTPUTF8));
@@ -614,51 +204,51 @@ void SMTPTransport::send
// Read response for "RSET" command
if (needReset)
{
- commands->writeToSocket(m_socket);
+ commands->writeToSocket(m_connection->getSocket());
- if ((resp = readResponse())->getCode() != 250)
+ if ((resp = m_connection->readResponse())->getCode() != 250)
{
- internalDisconnect();
+ disconnect();
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
}
}
// Read response for "MAIL" command
- commands->writeToSocket(m_socket);
+ commands->writeToSocket(m_connection->getSocket());
- if ((resp = readResponse())->getCode() != 250)
+ if ((resp = m_connection->readResponse())->getCode() != 250)
{
- internalDisconnect();
+ disconnect();
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
}
// Read responses for "RCPT TO" commands
for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i)
{
- commands->writeToSocket(m_socket);
+ commands->writeToSocket(m_connection->getSocket());
- resp = readResponse();
+ resp = m_connection->readResponse();
if (resp->getCode() != 250 &&
resp->getCode() != 251)
{
- internalDisconnect();
+ disconnect();
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
}
}
// Read response for "DATA" command
- commands->writeToSocket(m_socket);
+ commands->writeToSocket(m_connection->getSocket());
- if ((resp = readResponse())->getCode() != 354)
+ if ((resp = m_connection->readResponse())->getCode() != 354)
{
- internalDisconnect();
+ disconnect();
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
}
// Send the message data
// Stream copy with "\n." to "\n.." transformation
- utility::outputStreamSocketAdapter sos(*m_socket);
+ utility::outputStreamSocketAdapter sos(*m_connection->getSocket());
utility::dotFilteredOutputStream fos(sos);
utility::bufferedStreamCopy(is, fos, size, progress);
@@ -666,11 +256,11 @@ void SMTPTransport::send
fos.flush();
// Send end-of-data delimiter
- m_socket->sendRaw("\r\n.\r\n", 5);
+ m_connection->getSocket()->sendRaw("\r\n.\r\n", 5);
- if ((resp = readResponse())->getCode() != 250)
+ if ((resp = m_connection->readResponse())->getCode() != 250)
{
- internalDisconnect();
+ disconnect();
throw exceptions::command_error("DATA", resp->getText());
}
}
@@ -682,14 +272,11 @@ void SMTPTransport::send
{
// Generate the message with Internationalized Email support,
// if this is supported by the SMTP server
- const bool hasSMTPUTF8 =
- m_extensions.find("SMTPUTF8") != m_extensions.end();
-
std::ostringstream oss;
utility::outputStreamAdapter ossAdapter(oss);
generationContext ctx(generationContext::getDefaultContext());
- ctx.setInternationalizedEmailSupport(hasSMTPUTF8);
+ ctx.setInternationalizedEmailSupport(m_connection->hasExtension("SMTPUTF8"));
msg->generate(ctx, ossAdapter);
@@ -701,23 +288,6 @@ void SMTPTransport::send
}
-void SMTPTransport::sendRequest(ref <SMTPCommand> cmd)
-{
- cmd->writeToSocket(m_socket);
-}
-
-
-ref <SMTPResponse> SMTPTransport::readResponse()
-{
- ref <SMTPResponse> resp = SMTPResponse::readResponse
- (m_socket, m_timeoutHandler, m_responseState);
-
- m_responseState = resp->getCurrentState();
-
- return resp;
-}
-
-
// Service infos
diff --git a/vmime/net/smtp/SMTPConnection.hpp b/vmime/net/smtp/SMTPConnection.hpp
new file mode 100644
index 00000000..96469ab1
--- /dev/null
+++ b/vmime/net/smtp/SMTPConnection.hpp
@@ -0,0 +1,131 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#ifndef VMIME_NET_SMTP_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/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
+{
+ friend class vmime::creator;
+
+public:
+
+ SMTPConnection(ref <SMTPTransport> transport, ref <security::authenticator> auth);
+ virtual ~SMTPConnection();
+
+
+ virtual void connect();
+ virtual bool isConnected() const;
+ virtual void disconnect();
+
+ bool isSecuredConnection() const;
+ ref <connectionInfos> getConnectionInfos() const;
+
+ virtual ref <SMTPTransport> getTransport();
+ virtual ref <socket> getSocket();
+ virtual ref <timeoutHandler> getTimeoutHandler();
+ virtual ref <security::authenticator> getAuthenticator();
+ virtual ref <session> getSession();
+
+ void sendRequest(ref <SMTPCommand> cmd);
+ ref <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_ref <SMTPTransport> m_transport;
+
+ ref <security::authenticator> m_auth;
+ ref <socket> m_socket;
+ ref <timeoutHandler> m_timeoutHandler;
+
+ SMTPResponse::state m_responseState;
+
+ bool m_authenticated;
+ bool m_secured;
+
+ ref <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/net/smtp/SMTPTransport.hpp b/vmime/net/smtp/SMTPTransport.hpp
index 19b27bee..57ea43c3 100644
--- a/vmime/net/smtp/SMTPTransport.hpp
+++ b/vmime/net/smtp/SMTPTransport.hpp
@@ -36,7 +36,7 @@
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/smtp/SMTPServiceInfos.hpp"
-#include "vmime/net/smtp/SMTPResponse.hpp"
+#include "vmime/net/smtp/SMTPConnection.hpp"
namespace vmime {
@@ -85,39 +85,17 @@ public:
bool isSecuredConnection() const;
ref <connectionInfos> getConnectionInfos() const;
+ ref <SMTPConnection> getConnection();
-private:
-
- void sendRequest(ref <SMTPCommand> cmd);
- ref <SMTPResponse> readResponse();
-
- void internalDisconnect();
-
- void helo();
- void authenticate();
-#if VMIME_HAVE_SASL_SUPPORT
- void authenticateSASL();
-#endif // VMIME_HAVE_SASL_SUPPORT
+ bool isSMTPS() const;
-#if VMIME_HAVE_TLS_SUPPORT
- void startTLS();
-#endif // VMIME_HAVE_TLS_SUPPORT
-
- ref <socket> m_socket;
- bool m_authentified;
+private:
- bool m_extendedSMTP;
- std::map <string, std::vector <string> > m_extensions;
+ ref <SMTPConnection> m_connection;
- ref <timeoutHandler> m_timeoutHandler;
const bool m_isSMTPS;
- bool m_secured;
- ref <connectionInfos> m_cntInfos;
-
- SMTPResponse::state m_responseState;
-
bool m_needReset;
// Service infos