Moved SMTP connection-related things to SMTPConnection object.
This commit is contained in:
parent
26eecc6b35
commit
36773bd834
@ -254,6 +254,7 @@ libvmime_messaging_proto_sources = [
|
|||||||
[
|
[
|
||||||
'net/smtp/SMTPCommand.cpp', 'net/smtp/SMTPCommand.hpp',
|
'net/smtp/SMTPCommand.cpp', 'net/smtp/SMTPCommand.hpp',
|
||||||
'net/smtp/SMTPCommandSet.cpp', 'net/smtp/SMTPCommandSet.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/SMTPResponse.cpp', 'net/smtp/SMTPResponse.hpp',
|
||||||
'net/smtp/SMTPServiceInfos.cpp', 'net/smtp/SMTPServiceInfos.hpp',
|
'net/smtp/SMTPServiceInfos.cpp', 'net/smtp/SMTPServiceInfos.hpp',
|
||||||
'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp',
|
'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp',
|
||||||
|
612
src/net/smtp/SMTPConnection.cpp
Normal file
612
src/net/smtp/SMTPConnection.cpp
Normal file
@ -0,0 +1,612 @@
|
|||||||
|
//
|
||||||
|
// VMime library (http://www.vmime.org)
|
||||||
|
// Copyright (C) 2002-2013 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/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
|
@ -33,7 +33,6 @@
|
|||||||
#include "vmime/net/smtp/SMTPCommandSet.hpp"
|
#include "vmime/net/smtp/SMTPCommandSet.hpp"
|
||||||
|
|
||||||
#include "vmime/exception.hpp"
|
#include "vmime/exception.hpp"
|
||||||
#include "vmime/platform.hpp"
|
|
||||||
#include "vmime/mailboxList.hpp"
|
#include "vmime/mailboxList.hpp"
|
||||||
#include "vmime/message.hpp"
|
#include "vmime/message.hpp"
|
||||||
|
|
||||||
@ -44,26 +43,6 @@
|
|||||||
#include "vmime/utility/outputStreamAdapter.hpp"
|
#include "vmime/utility/outputStreamAdapter.hpp"
|
||||||
#include "vmime/utility/inputStreamStringAdapter.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 vmime {
|
||||||
namespace net {
|
namespace net {
|
||||||
@ -71,9 +50,7 @@ namespace smtp {
|
|||||||
|
|
||||||
|
|
||||||
SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth, const bool secured)
|
SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth, const bool secured)
|
||||||
: transport(sess, getInfosInstance(), auth), m_socket(NULL),
|
: transport(sess, getInfosInstance(), auth), m_isSMTPS(secured), m_needReset(false)
|
||||||
m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL),
|
|
||||||
m_isSMTPS(secured), m_secured(false), m_needReset(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,8 +61,6 @@ SMTPTransport::~SMTPTransport()
|
|||||||
{
|
{
|
||||||
if (isConnected())
|
if (isConnected())
|
||||||
disconnect();
|
disconnect();
|
||||||
else if (m_socket)
|
|
||||||
internalDisconnect();
|
|
||||||
}
|
}
|
||||||
catch (vmime::exception&)
|
catch (vmime::exception&)
|
||||||
{
|
{
|
||||||
@ -100,418 +75,59 @@ const string SMTPTransport::getProtocolName() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SMTPTransport::isSMTPS() const
|
||||||
|
{
|
||||||
|
return m_isSMTPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SMTPTransport::connect()
|
void SMTPTransport::connect()
|
||||||
{
|
{
|
||||||
if (isConnected())
|
if (isConnected())
|
||||||
throw exceptions::already_connected();
|
throw exceptions::already_connected();
|
||||||
|
|
||||||
const string address = GET_PROPERTY(string, PROPERTY_SERVER_ADDRESS);
|
m_connection = vmime::create <SMTPConnection>
|
||||||
const port_t port = GET_PROPERTY(port_t, PROPERTY_SERVER_PORT);
|
(thisRef().dynamicCast <SMTPTransport>(), getAuthenticator());
|
||||||
|
|
||||||
// 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()
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if VMIME_HAVE_SASL_SUPPORT
|
|
||||||
|
|
||||||
void SMTPTransport::authenticateSASL()
|
|
||||||
{
|
|
||||||
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
|
try
|
||||||
{
|
{
|
||||||
// Extract challenge
|
m_connection->connect();
|
||||||
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)
|
catch (std::exception&)
|
||||||
{
|
{
|
||||||
if (challenge)
|
m_connection = NULL;
|
||||||
{
|
|
||||||
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 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;
|
|
||||||
}
|
|
||||||
catch (exception&)
|
|
||||||
{
|
|
||||||
// Fatal error
|
|
||||||
internalDisconnect();
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // VMIME_HAVE_TLS_SUPPORT
|
|
||||||
|
|
||||||
|
|
||||||
bool SMTPTransport::isConnected() const
|
bool SMTPTransport::isConnected() const
|
||||||
{
|
{
|
||||||
return (m_socket && m_socket->isConnected() && m_authentified);
|
return m_connection && m_connection->isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SMTPTransport::isSecuredConnection() const
|
bool SMTPTransport::isSecuredConnection() const
|
||||||
{
|
{
|
||||||
return m_secured;
|
if (m_connection == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_connection->isSecuredConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ref <connectionInfos> SMTPTransport::getConnectionInfos() const
|
ref <connectionInfos> SMTPTransport::getConnectionInfos() const
|
||||||
{
|
{
|
||||||
return m_cntInfos;
|
if (m_connection == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return m_connection->getConnectionInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ref <SMTPConnection> SMTPTransport::getConnection()
|
||||||
|
{
|
||||||
|
return m_connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -520,32 +136,8 @@ void SMTPTransport::disconnect()
|
|||||||
if (!isConnected())
|
if (!isConnected())
|
||||||
throw exceptions::not_connected();
|
throw exceptions::not_connected();
|
||||||
|
|
||||||
internalDisconnect();
|
m_connection->disconnect();
|
||||||
}
|
m_connection = NULL;
|
||||||
|
|
||||||
|
|
||||||
void SMTPTransport::internalDisconnect()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sendRequest(SMTPCommand::QUIT());
|
|
||||||
readResponse();
|
|
||||||
}
|
|
||||||
catch (exception&)
|
|
||||||
{
|
|
||||||
// Not important
|
|
||||||
}
|
|
||||||
|
|
||||||
m_socket->disconnect();
|
|
||||||
m_socket = NULL;
|
|
||||||
|
|
||||||
m_timeoutHandler = NULL;
|
|
||||||
|
|
||||||
m_authentified = false;
|
|
||||||
m_extendedSMTP = false;
|
|
||||||
|
|
||||||
m_secured = false;
|
|
||||||
m_cntInfos = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -554,9 +146,9 @@ void SMTPTransport::noop()
|
|||||||
if (!isConnected())
|
if (!isConnected())
|
||||||
throw exceptions::not_connected();
|
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)
|
if (resp->getCode() != 250)
|
||||||
throw exceptions::command_error("NOOP", resp->getText());
|
throw exceptions::command_error("NOOP", resp->getText());
|
||||||
@ -579,8 +171,7 @@ void SMTPTransport::send
|
|||||||
|
|
||||||
|
|
||||||
const bool needReset = m_needReset;
|
const bool needReset = m_needReset;
|
||||||
const bool hasPipelining =
|
const bool hasPipelining = m_connection->hasExtension("PIPELINING");
|
||||||
m_extensions.find("PIPELINING") != m_extensions.end();
|
|
||||||
|
|
||||||
ref <SMTPResponse> resp;
|
ref <SMTPResponse> resp;
|
||||||
ref <SMTPCommandSet> commands = SMTPCommandSet::create(hasPipelining);
|
ref <SMTPCommandSet> commands = SMTPCommandSet::create(hasPipelining);
|
||||||
@ -590,8 +181,7 @@ void SMTPTransport::send
|
|||||||
commands->addCommand(SMTPCommand::RSET());
|
commands->addCommand(SMTPCommand::RSET());
|
||||||
|
|
||||||
// Emit the "MAIL" command
|
// Emit the "MAIL" command
|
||||||
const bool hasSMTPUTF8 =
|
const bool hasSMTPUTF8 = m_connection->hasExtension("SMTPUTF8");
|
||||||
m_extensions.find("SMTPUTF8") != m_extensions.end();
|
|
||||||
|
|
||||||
if (!sender.isEmpty())
|
if (!sender.isEmpty())
|
||||||
commands->addCommand(SMTPCommand::MAIL(sender, hasSMTPUTF8));
|
commands->addCommand(SMTPCommand::MAIL(sender, hasSMTPUTF8));
|
||||||
@ -614,51 +204,51 @@ void SMTPTransport::send
|
|||||||
// Read response for "RSET" command
|
// Read response for "RSET" command
|
||||||
if (needReset)
|
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());
|
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read response for "MAIL" command
|
// 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());
|
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read responses for "RCPT TO" commands
|
// Read responses for "RCPT TO" commands
|
||||||
for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i)
|
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 &&
|
if (resp->getCode() != 250 &&
|
||||||
resp->getCode() != 251)
|
resp->getCode() != 251)
|
||||||
{
|
{
|
||||||
internalDisconnect();
|
disconnect();
|
||||||
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
|
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read response for "DATA" command
|
// 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());
|
throw exceptions::command_error(commands->getLastCommandSent()->getText(), resp->getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the message data
|
// Send the message data
|
||||||
// Stream copy with "\n." to "\n.." transformation
|
// Stream copy with "\n." to "\n.." transformation
|
||||||
utility::outputStreamSocketAdapter sos(*m_socket);
|
utility::outputStreamSocketAdapter sos(*m_connection->getSocket());
|
||||||
utility::dotFilteredOutputStream fos(sos);
|
utility::dotFilteredOutputStream fos(sos);
|
||||||
|
|
||||||
utility::bufferedStreamCopy(is, fos, size, progress);
|
utility::bufferedStreamCopy(is, fos, size, progress);
|
||||||
@ -666,11 +256,11 @@ void SMTPTransport::send
|
|||||||
fos.flush();
|
fos.flush();
|
||||||
|
|
||||||
// Send end-of-data delimiter
|
// 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());
|
throw exceptions::command_error("DATA", resp->getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -682,14 +272,11 @@ void SMTPTransport::send
|
|||||||
{
|
{
|
||||||
// Generate the message with Internationalized Email support,
|
// Generate the message with Internationalized Email support,
|
||||||
// if this is supported by the SMTP server
|
// if this is supported by the SMTP server
|
||||||
const bool hasSMTPUTF8 =
|
|
||||||
m_extensions.find("SMTPUTF8") != m_extensions.end();
|
|
||||||
|
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
utility::outputStreamAdapter ossAdapter(oss);
|
utility::outputStreamAdapter ossAdapter(oss);
|
||||||
|
|
||||||
generationContext ctx(generationContext::getDefaultContext());
|
generationContext ctx(generationContext::getDefaultContext());
|
||||||
ctx.setInternationalizedEmailSupport(hasSMTPUTF8);
|
ctx.setInternationalizedEmailSupport(m_connection->hasExtension("SMTPUTF8"));
|
||||||
|
|
||||||
msg->generate(ctx, ossAdapter);
|
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
|
// Service infos
|
||||||
|
|
||||||
|
131
vmime/net/smtp/SMTPConnection.hpp
Normal file
131
vmime/net/smtp/SMTPConnection.hpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
//
|
||||||
|
// VMime library (http://www.vmime.org)
|
||||||
|
// Copyright (C) 2002-2013 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/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
|
@ -36,7 +36,7 @@
|
|||||||
#include "vmime/net/timeoutHandler.hpp"
|
#include "vmime/net/timeoutHandler.hpp"
|
||||||
|
|
||||||
#include "vmime/net/smtp/SMTPServiceInfos.hpp"
|
#include "vmime/net/smtp/SMTPServiceInfos.hpp"
|
||||||
#include "vmime/net/smtp/SMTPResponse.hpp"
|
#include "vmime/net/smtp/SMTPConnection.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace vmime {
|
namespace vmime {
|
||||||
@ -85,39 +85,17 @@ public:
|
|||||||
|
|
||||||
bool isSecuredConnection() const;
|
bool isSecuredConnection() const;
|
||||||
ref <connectionInfos> getConnectionInfos() const;
|
ref <connectionInfos> getConnectionInfos() const;
|
||||||
|
ref <SMTPConnection> getConnection();
|
||||||
|
|
||||||
|
bool isSMTPS() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void sendRequest(ref <SMTPCommand> cmd);
|
ref <SMTPConnection> m_connection;
|
||||||
ref <SMTPResponse> readResponse();
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
ref <socket> m_socket;
|
|
||||||
bool m_authentified;
|
|
||||||
|
|
||||||
bool m_extendedSMTP;
|
|
||||||
std::map <string, std::vector <string> > m_extensions;
|
|
||||||
|
|
||||||
ref <timeoutHandler> m_timeoutHandler;
|
|
||||||
|
|
||||||
const bool m_isSMTPS;
|
const bool m_isSMTPS;
|
||||||
|
|
||||||
bool m_secured;
|
|
||||||
ref <connectionInfos> m_cntInfos;
|
|
||||||
|
|
||||||
SMTPResponse::state m_responseState;
|
|
||||||
|
|
||||||
bool m_needReset;
|
bool m_needReset;
|
||||||
|
|
||||||
// Service infos
|
// Service infos
|
||||||
|
Loading…
Reference in New Issue
Block a user