diff options
Diffstat (limited to 'src/net/pop3/POP3Store.cpp')
-rw-r--r-- | src/net/pop3/POP3Store.cpp | 543 |
1 files changed, 29 insertions, 514 deletions
diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp index 0efeec5f..6ff404e2 100644 --- a/src/net/pop3/POP3Store.cpp +++ b/src/net/pop3/POP3Store.cpp @@ -33,42 +33,17 @@ #include "vmime/net/pop3/POP3Response.hpp" #include "vmime/exception.hpp" -#include "vmime/platform.hpp" -#include "vmime/messageId.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 #include <algorithm> -// Helpers for service properties -#define GET_PROPERTY(type, prop) \ - (getInfos().getPropertyValue <type>(getSession(), \ - dynamic_cast <const POP3ServiceInfos&>(getInfos()).getProperties().prop)) -#define HAS_PROPERTY(prop) \ - (getInfos().hasProperty(getSession(), \ - dynamic_cast <const POP3ServiceInfos&>(getInfos()).getProperties().prop)) - - namespace vmime { namespace net { namespace pop3 { POP3Store::POP3Store(ref <session> sess, ref <security::authenticator> auth, const bool secured) - : store(sess, getInfosInstance(), auth), m_socket(NULL), - m_authentified(false), m_timeoutHandler(NULL), - m_isPOP3S(secured), m_secured(false) + : store(sess, getInfosInstance(), auth), m_isPOP3S(secured) { } @@ -79,8 +54,6 @@ POP3Store::~POP3Store() { if (isConnected()) disconnect(); - else if (m_socket) - internalDisconnect(); } catch (vmime::exception&) { @@ -136,458 +109,54 @@ void POP3Store::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_isPOP3S) // dedicated port/POP3S - { - 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); + m_connection = vmime::create <POP3Connection> + (thisRef().dynamicCast <POP3Store>(), getAuthenticator()); - // Connection - // - // eg: C: <connection to server> - // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <[email protected]> - - ref <POP3Response> response = readResponse(); - - if (!response->isSuccess()) + try { - internalDisconnect(); - throw exceptions::connection_greeting_error(response->getFirstLine()); + m_connection->connect(); } - -#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_isPOP3S && tls) // only if not POP3S + catch (std::exception&) { - 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; - } + m_connection = NULL; + throw; } -#endif // VMIME_HAVE_TLS_SUPPORT - - // Start authentication process - authenticate(messageId(response->getText())); } -void POP3Store::authenticate(const messageId& randomMID) +bool POP3Store::isPOP3S() const { - 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 APOP/normal authentication - internalDisconnect(); - throw e; - } - else - { - // Ignore, will try APOP/normal authentication - } - } - catch (exception& e) - { - internalDisconnect(); - throw e; - } - } -#endif // VMIME_HAVE_SASL_SUPPORT - - // Secured authentication with APOP (if requested and if available) - // - // eg: C: APOP vincent <digest> - // --- S: +OK vincent is a valid mailbox - - const string username = getAuthenticator()->getUsername(); - const string password = getAuthenticator()->getPassword(); - - ref <POP3Response> response; - - if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP)) - { - if (randomMID.getLeft().length() != 0 && - randomMID.getRight().length() != 0) - { - // <digest> is the result of MD5 applied to "<message-id>password" - ref <security::digest::messageDigest> md5 = - security::digest::messageDigestFactory::getInstance()->create("md5"); - - md5->update(randomMID.generate() + password); - md5->finalize(); - - sendRequest(POP3Command::APOP(username, md5->getHexDigest())); - response = readResponse(); - - if (response->isSuccess()) - { - m_authentified = true; - return; - } - else - { - // Some servers close the connection after an unsuccessful APOP - // command, so the fallback may not always work... - // - // S: +OK Qpopper (version 4.0.5) at xxx starting. <30396.1126730747@xxx> - // C: APOP plop c5e0a87d088ec71d60e32692d4c5bdf4 - // S: -ERR [AUTH] Password supplied for "plop" is incorrect. - // S: +OK Pop server at xxx signing off. - // [Connection closed by foreign host.] - - if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK)) - { - // Can't fallback on basic authentication - internalDisconnect(); - throw exceptions::authentication_error(response->getFirstLine()); - } - - // Ensure connection is valid (cf. note above) - try - { - sendRequest(POP3Command::NOOP()); - readResponse(); - } - catch (exceptions::socket_exception&) - { - internalDisconnect(); - throw exceptions::authentication_error(response->getFirstLine()); - } - } - } - else - { - // APOP not supported - if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK)) - { - // Can't fallback on basic authentication - internalDisconnect(); - throw exceptions::authentication_error("APOP not supported"); - } - } - } - - // Basic authentication - // - // eg: C: USER vincent - // --- S: +OK vincent is a valid mailbox - // - // C: PASS couic - // S: +OK vincent's maildrop has 2 messages (320 octets) - sendRequest(POP3Command::USER(username)); - response = readResponse(); - - if (!response->isSuccess()) - { - internalDisconnect(); - throw exceptions::authentication_error(response->getFirstLine()); - } - - sendRequest(POP3Command::PASS(password)); - response = readResponse(); - - if (!response->isSuccess()) - { - internalDisconnect(); - throw exceptions::authentication_error(response->getFirstLine()); - } - - m_authentified = true; + return m_isPOP3S; } -#if VMIME_HAVE_SASL_SUPPORT - -void POP3Store::authenticateSASL() +bool POP3Store::isConnected() const { - if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()) - throw exceptions::authentication_error("No SASL authenticator available."); - - std::vector <string> capa = getCapabilities(); - std::vector <string> saslMechs; - - for (unsigned int i = 0 ; i < capa.size() ; ++i) - { - const string& x = capa[i]; - - // C: CAPA - // S: +OK List of capabilities follows - // S: LOGIN-DELAY 0 - // S: PIPELINING - // S: UIDL - // S: ... - // S: SASL DIGEST-MD5 CRAM-MD5 <----- - // S: EXPIRE NEVER - // S: ... - - if (x.length() > 5 && - (x[0] == 'S' || x[0] == 's') && - (x[1] == 'A' || x[1] == 'a') && - (x[2] == 'S' || x[2] == 's') && - (x[3] == 'L' || x[3] == 'l') && - (x[4] == ' ' || x[4] == '\t')) - { - const string list(x.begin() + 5, x.end()); - - std::istringstream iss(list); - string mech; - - while (iss >> mech) - saslMechs.push_back(mech); - } - } - - 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("pop3", getAuthenticator(), mech); - - saslSession->init(); - - sendRequest(POP3Command::AUTH(mech->getName())); - - for (bool cont = true ; cont ; ) - { - ref <POP3Response> response = readResponse(); - - switch (response->getCode()) - { - case POP3Response::CODE_OK: - { - m_socket = saslSession->getSecuredSocket(m_socket); - return; - } - case POP3Response::CODE_READY: - { - 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."); + return m_connection && m_connection->isConnected(); } -#endif // VMIME_HAVE_SASL_SUPPORT - -#if VMIME_HAVE_TLS_SUPPORT - -void POP3Store::startTLS() +bool POP3Store::isSecuredConnection() const { - try - { - sendRequest(POP3Command::STLS()); + if (m_connection == NULL) + return false; - ref <POP3Response> response = readResponse(); - - if (!response->isSuccess()) - throw exceptions::command_error("STLS", response->getFirstLine()); - - 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; - } + return m_connection->isSecuredConnection(); } -#endif // VMIME_HAVE_TLS_SUPPORT - -bool POP3Store::isConnected() const +ref <connectionInfos> POP3Store::getConnectionInfos() const { - return (m_socket && m_socket->isConnected() && m_authentified); -} - + if (m_connection == NULL) + return NULL; -bool POP3Store::isSecuredConnection() const -{ - return m_secured; + return m_connection->getConnectionInfos(); } -ref <connectionInfos> POP3Store::getConnectionInfos() const +ref <POP3Connection> POP3Store::getConnection() { - return m_cntInfos; + return m_connection; } @@ -596,12 +165,6 @@ void POP3Store::disconnect() if (!isConnected()) throw exceptions::not_connected(); - internalDisconnect(); -} - - -void POP3Store::internalDisconnect() -{ for (std::list <POP3Folder*>::iterator it = m_folders.begin() ; it != m_folders.end() ; ++it) { @@ -610,74 +173,26 @@ void POP3Store::internalDisconnect() m_folders.clear(); - try - { - sendRequest(POP3Command::QUIT()); - readResponse(); - } - catch (exception&) - { - // Not important - } - - m_socket->disconnect(); - m_socket = NULL; - m_timeoutHandler = NULL; - - m_authentified = false; - - m_secured = false; - m_cntInfos = NULL; + m_connection->disconnect(); + m_connection = NULL; } void POP3Store::noop() { - sendRequest(POP3Command::NOOP()); + if (!m_connection) + throw exceptions::not_connected(); + + POP3Command::NOOP()->send(m_connection); - ref <POP3Response> response = - readResponse(); + ref <POP3Response> response = POP3Response::readResponse(m_connection); if (!response->isSuccess()) throw exceptions::command_error("NOOP", response->getFirstLine()); } -const std::vector <string> POP3Store::getCapabilities() -{ - sendRequest(POP3Command::CAPA()); - - ref <POP3Response> response = - POP3Response::readMultilineResponse(m_socket, m_timeoutHandler); - - std::vector <string> res; - - if (response->isSuccess()) - { - for (size_t i = 0, n = response->getLineCount() ; i < n ; ++i) - res.push_back(response->getLineAt(i)); - } - - return res; -} - - -void POP3Store::sendRequest(ref <POP3Command> cmd) -{ - cmd->writeToSocket(m_socket); -} - - -ref <POP3Response> POP3Store::readResponse() -{ - ref <POP3Response> resp = - readResponse(); - - return resp; -} - - void POP3Store::registerFolder(POP3Folder* folder) { m_folders.push_back(folder); |