diff options
Diffstat (limited to 'src/messaging/smtp')
-rw-r--r-- | src/messaging/smtp/SMTPTransport.cpp | 503 |
1 files changed, 0 insertions, 503 deletions
diff --git a/src/messaging/smtp/SMTPTransport.cpp b/src/messaging/smtp/SMTPTransport.cpp deleted file mode 100644 index 5613a47d..00000000 --- a/src/messaging/smtp/SMTPTransport.cpp +++ /dev/null @@ -1,503 +0,0 @@ -// -// VMime library (http://www.vmime.org) -// Copyright (C) 2002-2005 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// - -#include "vmime/messaging/smtp/SMTPTransport.hpp" - -#include "vmime/exception.hpp" -#include "vmime/platformDependant.hpp" -#include "vmime/encoderB64.hpp" -#include "vmime/mailboxList.hpp" - -#include "vmime/messaging/authHelper.hpp" - -#include "vmime/utility/filteredStream.hpp" - - -// Helpers for service properties -#define GET_PROPERTY(type, prop) \ - (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop)) -#define HAS_PROPERTY(prop) \ - (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop)) - - -namespace vmime { -namespace messaging { -namespace smtp { - - -SMTPTransport::SMTPTransport(ref <session> sess, ref <authenticator> auth) - : transport(sess, getInfosInstance(), auth), m_socket(NULL), - m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL) -{ -} - - -SMTPTransport::~SMTPTransport() -{ - if (isConnected()) - disconnect(); - else if (m_socket) - internalDisconnect(); -} - - -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 (HAS_PROPERTY(PROPERTY_TIMEOUT_FACTORY)) - { - timeoutHandlerFactory* tof = platformDependant::getHandler()-> - getTimeoutHandlerFactory(GET_PROPERTY(string, PROPERTY_TIMEOUT_FACTORY)); - - m_timeoutHandler = tof->create(); - } - - // Create and connect the socket - socketFactory* sf = platformDependant::getHandler()-> - getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY)); - - m_socket = sf->create(); - m_socket->connect(address, port); - - // Connection - // - // eg: C: <connection to server> - // --- S: 220 smtp.domain.com Service ready - - string response; - readResponse(response); - - if (responseCode(response) != 220) - { - internalDisconnect(); - throw exceptions::connection_greeting_error(response); - } - - // Identification - // First, try Extended SMTP (ESMTP) - // - // eg: C: EHLO thismachine.ourdomain.com - // S: 250 OK - - sendRequest("EHLO " + platformDependant::getHandler()->getHostName()); - readResponse(response); - - if (responseCode(response) != 250) - { - // Next, try "Basic" SMTP - // - // eg: C: HELO thismachine.ourdomain.com - // S: 250 OK - - sendRequest("HELO " + platformDependant::getHandler()->getHostName()); - readResponse(response); - - if (responseCode(response) != 250) - { - internalDisconnect(); - throw exceptions::connection_greeting_error(response); - } - - m_extendedSMTP = false; - } - else - { - m_extendedSMTP = true; - } - - // Authentication - if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH)) - { - if (!m_extendedSMTP) - { - internalDisconnect(); - throw exceptions::command_error("AUTH", "ESMTP not supported."); - } - - const authenticationInfos auth = getAuthenticator()->requestAuthInfos(); - bool authentified = false; - - enum AuthMethods - { - First = 0, - CRAM_MD5 = First, - // TODO: more authentication methods... - End - }; - - for (int currentMethod = First ; !authentified ; ++currentMethod) - { - switch (currentMethod) - { - case CRAM_MD5: - { - sendRequest("AUTH CRAM-MD5"); - readResponse(response); - - if (responseCode(response) == 334) - { - encoderB64 base64; - - string challengeB64 = responseText(response); - string challenge, challengeHex; - - { - utility::inputStreamStringAdapter in(challengeB64); - utility::outputStreamStringAdapter out(challenge); - - base64.decode(in, out); - } - - hmac_md5(challenge, auth.getPassword(), challengeHex); - - string decoded = auth.getUsername() + " " + challengeHex; - string encoded; - - { - utility::inputStreamStringAdapter in(decoded); - utility::outputStreamStringAdapter out(encoded); - - base64.encode(in, out); - } - - sendRequest(encoded); - readResponse(response); - - if (responseCode(response) == 235) - { - authentified = true; - } - else - { - internalDisconnect(); - throw exceptions::authentication_error(response); - } - } - - break; - } - case End: - { - // All authentication methods have been tried and - // the server does not understand any. - throw exceptions::authentication_error(response); - } - - } - } - } - - m_authentified = true; -} - - -const bool SMTPTransport::isConnected() const -{ - return (m_socket && m_socket->isConnected() && m_authentified); -} - - -void SMTPTransport::disconnect() -{ - if (!isConnected()) - throw exceptions::not_connected(); - - internalDisconnect(); -} - - -void SMTPTransport::internalDisconnect() -{ - sendRequest("QUIT"); - - m_socket->disconnect(); - m_socket = NULL; - - m_timeoutHandler = NULL; - - m_authentified = false; - m_extendedSMTP = false; -} - - -void SMTPTransport::noop() -{ - m_socket->send("NOOP"); - - string response; - readResponse(response); - - if (responseCode(response) != 250) - throw exceptions::command_error("NOOP", response); -} - - -void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients, - utility::inputStream& is, const utility::stream::size_type size, - utility::progressionListener* progress) -{ - // 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 - string response; - - sendRequest("MAIL FROM: <" + expeditor.getEmail() + ">"); - readResponse(response); - - if (responseCode(response) != 250) - { - internalDisconnect(); - throw exceptions::command_error("MAIL", response); - } - - // 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() + ">"); - readResponse(response); - - if (responseCode(response) != 250) - { - internalDisconnect(); - throw exceptions::command_error("RCPT TO", response); - } - } - - // Send the message data - sendRequest("DATA"); - readResponse(response); - - if (responseCode(response) != 354) - { - internalDisconnect(); - throw exceptions::command_error("DATA", response); - } - - // Stream copy with "\n." to "\n.." transformation - utility::outputStreamSocketAdapter sos(*m_socket); - utility::dotFilteredOutputStream fos(sos); - - utility::bufferedStreamCopy(is, fos, size, progress); - - // Send end-of-data delimiter - m_socket->sendRaw("\r\n.\r\n", 5); - readResponse(response); - - if (responseCode(response) != 250) - { - internalDisconnect(); - throw exceptions::command_error("DATA", response); - } -} - - -void SMTPTransport::sendRequest(const string& buffer, const bool end) -{ - m_socket->send(buffer); - if (end) m_socket->send("\r\n"); -} - - -const int SMTPTransport::responseCode(const string& response) -{ - int code = 0; - - if (response.length() >= 3) - { - code = (response[0] - '0') * 100 - + (response[1] - '0') * 10 - + (response[2] - '0'); - } - - return (code); -} - - -const string SMTPTransport::responseText(const string& response) -{ - string text; - - std::istringstream iss(response); - std::string line; - - while (std::getline(iss, line)) - { - if (line.length() >= 4) - text += line.substr(4); - else - text += line; - - text += "\n"; - } - - return (text); -} - - -void SMTPTransport::readResponse(string& buffer) -{ - bool foundTerminator = false; - - buffer.clear(); - - for ( ; !foundTerminator ; ) - { - // Check whether the time-out delay is elapsed - if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) - { - if (!m_timeoutHandler->handleTimeOut()) - throw exceptions::operation_timed_out(); - } - - // Receive data from the socket - string receiveBuffer; - m_socket->receive(receiveBuffer); - - if (receiveBuffer.empty()) // buffer is empty - { - platformDependant::getHandler()->wait(); - continue; - } - - // We have received data: reset the time-out counter - if (m_timeoutHandler) - m_timeoutHandler->resetTimeOut(); - - // Append the data to the response buffer - buffer += receiveBuffer; - - // Check for terminator string (and strip it if present) - if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n') - { - string::size_type p = buffer.length() - 2; - bool end = false; - - for ( ; !end ; --p) - { - if (p == 0 || buffer[p] == '\n') - { - end = true; - - if (p + 4 < buffer.length()) - foundTerminator = true; - } - } - } - } - - // Remove [CR]LF at the end of the response - if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n') - { - if (buffer[buffer.length() - 2] == '\r') - buffer.resize(buffer.length() - 2); - else - buffer.resize(buffer.length() - 1); - } -} - - - -// Service infos - -SMTPTransport::_infos SMTPTransport::sm_infos; - - -const serviceInfos& SMTPTransport::getInfosInstance() -{ - return (sm_infos); -} - - -const serviceInfos& SMTPTransport::getInfos() const -{ - return (sm_infos); -} - - -const string SMTPTransport::_infos::getPropertyPrefix() const -{ - return "transport.smtp."; -} - - -const SMTPTransport::_infos::props& SMTPTransport::_infos::getProperties() const -{ - static props p = - { - // SMTP-specific options - property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"), - - // Common properties - property(serviceInfos::property::AUTH_USERNAME), - property(serviceInfos::property::AUTH_PASSWORD), - - property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), - property(serviceInfos::property::SERVER_PORT, "25"), - property(serviceInfos::property::SERVER_SOCKETFACTORY), - - property(serviceInfos::property::TIMEOUT_FACTORY) - }; - - return p; -} - - -const std::vector <serviceInfos::property> SMTPTransport::_infos::getAvailableProperties() const -{ - std::vector <property> list; - const props& p = getProperties(); - - // SMTP-specific options - list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH); - - // Common properties - list.push_back(p.PROPERTY_AUTH_USERNAME); - list.push_back(p.PROPERTY_AUTH_PASSWORD); - - list.push_back(p.PROPERTY_SERVER_ADDRESS); - list.push_back(p.PROPERTY_SERVER_PORT); - list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY); - - list.push_back(p.PROPERTY_TIMEOUT_FACTORY); - - return (list); -} - - -} // smtp -} // messaging -} // vmime |