⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smtptransport.cpp

📁 MIME解析的代码
💻 CPP
字号:
//// VMime library (http://www.vmime.org)// Copyright (C) 2002-2008 Vincent Richard <vincent@vincent-richard.net>//// 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 2 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/net/smtp/SMTPTransport.hpp"#include "vmime/net/smtp/SMTPResponse.hpp"#include "vmime/exception.hpp"#include "vmime/platform.hpp"#include "vmime/mailboxList.hpp"#include "vmime/utility/filteredStream.hpp"#include "vmime/utility/stringUtils.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 {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){}SMTPTransport::~SMTPTransport(){	try	{		if (isConnected())			disconnect();		else if (m_socket)			internalDisconnect();	}	catch (vmime::exception&)	{		// Ignore	}}const string SMTPTransport::getProtocolName() const{	return "smtp";}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();#if VMIME_HAVE_TLS_SUPPORT	if (m_isSMTPS)  // dedicated port/SMTPS	{		ref <tls::TLSSession> tlsSession =			vmime::create <tls::TLSSession>(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("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("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 (int 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_SUPPORTvoid 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("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;				int challengeLen = 0;				byte_t* resp = 0;				int respLen = 0;				try				{					// Extract challenge					saslContext->decodeB64(response->getText(), &challenge, &challengeLen);					// Prepare response					saslSession->evaluateChallenge						(challenge, challengeLen, &resp, &respLen);					// Send response					sendRequest(saslContext->encodeB64(resp, respLen));				}				catch (exceptions::sasl_exception& e)				{					if (challenge)					{						delete [] challenge;						challenge = NULL;					}					if (resp)					{						delete [] resp;						resp = NULL;					}					// Cancel SASL exchange					sendRequest("*");				}				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_SUPPORTvoid SMTPTransport::startTLS(){	try	{		sendRequest("STARTTLS");		ref <SMTPResponse> resp = readResponse();		if (resp->getCode() != 220)			throw exceptions::command_error("STARTTLS", resp->getText());		ref <tls::TLSSession> tlsSession =			vmime::create <tls::TLSSession>(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_SUPPORTbool SMTPTransport::isConnected() const{	return (m_socket && m_socket->isConnected() && m_authentified);}bool SMTPTransport::isSecuredConnection() const{	return m_secured;}ref <connectionInfos> SMTPTransport::getConnectionInfos() const{	return m_cntInfos;}void SMTPTransport::disconnect(){	if (!isConnected())		throw exceptions::not_connected();	internalDisconnect();}void SMTPTransport::internalDisconnect(){	try	{		sendRequest("QUIT");	}	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;}void SMTPTransport::noop(){	if (!isConnected())		throw exceptions::not_connected();	sendRequest("NOOP");	ref <SMTPResponse> resp = readResponse();	if (resp->getCode() != 250)		throw exceptions::command_error("NOOP", resp->getText());}void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients,                         utility::inputStream& is, const utility::stream::size_type size,                         utility::progressListener* progress){	if (!isConnected())		throw exceptions::not_connected();	// If no recipient/expeditor was found, throw an exception	if (recipients.isEmpty())		throw exceptions::no_recipient();	else if (expeditor.isEmpty())		throw exceptions::no_expeditor();	// Emit the "MAIL" command	ref <SMTPResponse> resp;	sendRequest("MAIL FROM: <" + expeditor.getEmail() + ">");	if ((resp = readResponse())->getCode() != 250)	{		internalDisconnect();		throw exceptions::command_error("MAIL", resp->getText());	}	// Emit a "RCPT TO" command for each recipient	for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)	{		const mailbox& mbox = *recipients.getMailboxAt(i);		sendRequest("RCPT TO: <" + mbox.getEmail() + ">");		if ((resp = readResponse())->getCode() != 250)		{			internalDisconnect();			throw exceptions::command_error("RCPT TO", resp->getText());		}	}	// Send the message data	sendRequest("DATA");	if ((resp = readResponse())->getCode() != 354)	{		internalDisconnect();		throw exceptions::command_error("DATA", resp->getText());	}	// Stream copy with "\n." to "\n.." transformation	utility::outputStreamSocketAdapter sos(*m_socket);	utility::dotFilteredOutputStream fos(sos);	utility::bufferedStreamCopy(is, fos, size, progress);	fos.flush();	// Send end-of-data delimiter	m_socket->sendRaw("\r\n.\r\n", 5);	if ((resp = readResponse())->getCode() != 250)	{		internalDisconnect();		throw exceptions::command_error("DATA", resp->getText());	}}void SMTPTransport::sendRequest(const string& buffer, const bool end){	m_socket->send(buffer);	if (end) m_socket->send("\r\n");}ref <SMTPResponse> SMTPTransport::readResponse(){	return SMTPResponse::readResponse(m_socket, m_timeoutHandler);}// Service infosSMTPServiceInfos SMTPTransport::sm_infos(false);const serviceInfos& SMTPTransport::getInfosInstance(){	return sm_infos;}const serviceInfos& SMTPTransport::getInfos() const{	return sm_infos;}} // smtp} // net} // vmime

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -