diff --git a/SConstruct b/SConstruct index d295a3ab..0200a1cc 100644 --- a/SConstruct +++ b/SConstruct @@ -239,6 +239,7 @@ libvmime_messaging_proto_sources = [ 'pop3', [ 'net/pop3/POP3Command.cpp', 'net/pop3/POP3Command.hpp', + 'net/pop3/POP3Connection.cpp', 'net/pop3/POP3Connection.hpp', 'net/pop3/POP3ServiceInfos.cpp', 'net/pop3/POP3ServiceInfos.hpp', 'net/pop3/POP3Store.cpp', 'net/pop3/POP3Store.hpp', 'net/pop3/POP3SStore.cpp', 'net/pop3/POP3SStore.hpp', diff --git a/src/net/pop3/POP3Command.cpp b/src/net/pop3/POP3Command.cpp index 4aef1e08..72909a49 100644 --- a/src/net/pop3/POP3Command.cpp +++ b/src/net/pop3/POP3Command.cpp @@ -28,6 +28,7 @@ #include "vmime/net/pop3/POP3Command.hpp" +#include "vmime/net/pop3/POP3Connection.hpp" #include "vmime/net/socket.hpp" @@ -214,9 +215,9 @@ const string POP3Command::getText() const } -void POP3Command::writeToSocket(ref sok) +void POP3Command::send(ref conn) { - sok->send(m_text + "\r\n"); + conn->getSocket()->send(m_text + "\r\n"); } diff --git a/src/net/pop3/POP3Connection.cpp b/src/net/pop3/POP3Connection.cpp new file mode 100644 index 00000000..96717620 --- /dev/null +++ b/src/net/pop3/POP3Connection.cpp @@ -0,0 +1,642 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Connection.hpp" +#include "vmime/net/pop3/POP3Store.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_store.acquire()->getInfos().getPropertyValue (getSession(), \ + dynamic_cast (m_store.acquire()->getInfos()).getProperties().prop)) +#define HAS_PROPERTY(prop) \ + (m_store.acquire()->getInfos().hasProperty(getSession(), \ + dynamic_cast (m_store.acquire()->getInfos()).getProperties().prop)) + + +namespace vmime { +namespace net { +namespace pop3 { + + + +POP3Connection::POP3Connection(ref store, ref auth) + : m_store(store), m_auth(auth), m_socket(NULL), m_timeoutHandler(NULL), + m_authenticated(false), m_secured(false) +{ +} + + +POP3Connection::~POP3Connection() +{ + try + { + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); + } + catch (vmime::exception&) + { + // Ignore + } +} + + +void POP3Connection::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 store = m_store.acquire(); + + // Create the time-out handler + if (store->getTimeoutHandlerFactory()) + m_timeoutHandler = store->getTimeoutHandlerFactory()->create(); + + // Create and connect the socket + m_socket = store->getSocketFactory()->create(m_timeoutHandler); + +#if VMIME_HAVE_TLS_SUPPORT + if (store->isPOP3S()) // dedicated port/POP3S + { + ref tlsSession = + tls::TLSSession::create(store->getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = vmime::create (address, port, tlsSession, tlsSocket); + } + else +#endif // VMIME_HAVE_TLS_SUPPORT + { + m_cntInfos = vmime::create (address, port); + } + + m_socket->connect(address, port); + + // Connection + // + // eg: C: + // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <36938848.1056800841.634@somewhere.com> + + ref response = POP3Response::readResponse + (thisRef().dynamicCast ()); + + if (!response->isSuccess()) + { + internalDisconnect(); + throw exceptions::connection_greeting_error(response->getFirstLine()); + } + +#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 (!store->isPOP3S() && tls) // only if not POP3S + { + 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; + } + } +#endif // VMIME_HAVE_TLS_SUPPORT + + // Start authentication process + authenticate(messageId(response->getText())); +} + + +void POP3Connection::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void POP3Connection::internalDisconnect() +{ + try + { + POP3Command::QUIT()->send(thisRef().dynamicCast ()); + POP3Response::readResponse(thisRef().dynamicCast ()); + } + catch (exception&) + { + // Not important + } + + m_socket->disconnect(); + m_socket = NULL; + + m_timeoutHandler = NULL; + + m_authenticated = false; + m_secured = false; + + m_cntInfos = NULL; +} + + +void POP3Connection::authenticate(const messageId& randomMID) +{ + getAuthenticator()->setService(thisRef().dynamicCast ()); + +#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 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 + // --- S: +OK vincent is a valid mailbox + + const string username = getAuthenticator()->getUsername(); + const string password = getAuthenticator()->getPassword(); + + ref conn = thisRef().dynamicCast (); + ref response; + + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP)) + { + if (randomMID.getLeft().length() != 0 && + randomMID.getRight().length() != 0) + { + // is the result of MD5 applied to "password" + ref md5 = + security::digest::messageDigestFactory::getInstance()->create("md5"); + + md5->update(randomMID.generate() + password); + md5->finalize(); + + POP3Command::APOP(username, md5->getHexDigest())->send(conn); + response = POP3Response::readResponse(conn); + + if (response->isSuccess()) + { + m_authenticated = 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 + { + POP3Command::NOOP()->send(conn); + POP3Response::readResponse(conn); + } + 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) + POP3Command::USER(username)->send(conn); + response = POP3Response::readResponse(conn); + + if (!response->isSuccess()) + { + internalDisconnect(); + throw exceptions::authentication_error(response->getFirstLine()); + } + + POP3Command::PASS(password)->send(conn); + response = POP3Response::readResponse(conn); + + if (!response->isSuccess()) + { + internalDisconnect(); + throw exceptions::authentication_error(response->getFirstLine()); + } + + m_authenticated = true; +} + + +#if VMIME_HAVE_SASL_SUPPORT + +void POP3Connection::authenticateSASL() +{ + if (!getAuthenticator().dynamicCast ()) + throw exceptions::authentication_error("No SASL authenticator available."); + + std::vector capa = getCapabilities(); + std::vector 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 > mechList; + + ref saslContext = + vmime::create (); + + 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 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 ()-> + 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 mech = mechList[i]; + + ref saslSession = + saslContext->createSession("pop3", getAuthenticator(), mech); + + saslSession->init(); + + POP3Command::AUTH(mech->getName())->send(thisRef().dynamicCast ()); + + for (bool cont = true ; cont ; ) + { + ref response = + POP3Response::readResponse(thisRef().dynamicCast ()); + + 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."); +} + +#endif // VMIME_HAVE_SASL_SUPPORT + + +#if VMIME_HAVE_TLS_SUPPORT + +void POP3Connection::startTLS() +{ + try + { + POP3Command::STLS()->send(thisRef().dynamicCast ()); + + ref response = + POP3Response::readResponse(thisRef().dynamicCast ()); + + if (!response->isSuccess()) + throw exceptions::command_error("STLS", response->getFirstLine()); + + ref tlsSession = + tls::TLSSession::create(m_store.acquire()->getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + tlsSocket->handshake(m_timeoutHandler); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = vmime::create + (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 + + +const std::vector POP3Connection::getCapabilities() +{ + POP3Command::CAPA()->send(thisRef().dynamicCast ()); + + ref response = + POP3Response::readMultilineResponse(thisRef().dynamicCast ()); + + std::vector res; + + if (response->isSuccess()) + { + for (size_t i = 0, n = response->getLineCount() ; i < n ; ++i) + res.push_back(response->getLineAt(i)); + } + + return res; +} + + +bool POP3Connection::isConnected() const +{ + return m_socket && m_socket->isConnected() && m_authenticated; +} + + +bool POP3Connection::isSecuredConnection() const +{ + return m_secured; +} + + +ref POP3Connection::getConnectionInfos() const +{ + return m_cntInfos; +} + + +ref POP3Connection::getStore() +{ + return m_store.acquire(); +} + + +ref POP3Connection::getSession() +{ + return m_store.acquire()->getSession(); +} + + +ref POP3Connection::getSocket() +{ + return m_socket; +} + + +ref POP3Connection::getTimeoutHandler() +{ + return m_timeoutHandler; +} + + +ref POP3Connection::getAuthenticator() +{ + return m_auth; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp index 46152f0e..7aa850b1 100644 --- a/src/net/pop3/POP3Folder.cpp +++ b/src/net/pop3/POP3Folder.cpp @@ -131,10 +131,9 @@ void POP3Folder::open(const int mode, bool failIfModeIsNotAvailable) } else if (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX") { - store->sendRequest(POP3Command::STAT()); + POP3Command::STAT()->send(store->getConnection()); - ref response = - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + ref response = POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("STAT", response->getFirstLine()); @@ -166,8 +165,8 @@ void POP3Folder::close(const bool expunge) if (!expunge) { - store->sendRequest(POP3Command::RSET()); - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + POP3Command::RSET()->send(store->getConnection()); + POP3Response::readResponse(store->getConnection()); } m_open = false; @@ -363,11 +362,11 @@ void POP3Folder::fetchMessages(std::vector >& msg, const int opti if (options & FETCH_SIZE) { // Send the "LIST" command - store->sendRequest(POP3Command::LIST()); + POP3Command::LIST()->send(store->getConnection()); // Get the response ref response = - POP3Response::readMultilineResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readMultilineResponse(store->getConnection()); if (response->isSuccess()) { @@ -403,11 +402,11 @@ void POP3Folder::fetchMessages(std::vector >& msg, const int opti if (options & FETCH_UID) { // Send the "UIDL" command - store->sendRequest(POP3Command::UIDL()); + POP3Command::UIDL()->send(store->getConnection()); // Get the response ref response = - POP3Response::readMultilineResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readMultilineResponse(store->getConnection()); if (response->isSuccess()) { @@ -452,11 +451,11 @@ void POP3Folder::fetchMessage(ref msg, const int options) if (options & FETCH_SIZE) { // Send the "LIST" command - store->sendRequest(POP3Command::LIST(msg->getNumber())); + POP3Command::LIST(msg->getNumber())->send(store->getConnection()); // Get the response ref response = - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readResponse(store->getConnection()); if (response->isSuccess()) { @@ -485,11 +484,11 @@ void POP3Folder::fetchMessage(ref msg, const int options) if (options & FETCH_UID) { // Send the "UIDL" command - store->sendRequest(POP3Command::UIDL(msg->getNumber())); + POP3Command::UIDL(msg->getNumber())->send(store->getConnection()); // Get the response ref response = - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readResponse(store->getConnection()); if (response->isSuccess()) { @@ -569,10 +568,10 @@ void POP3Folder::deleteMessage(const int num) else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); - store->sendRequest(POP3Command::DELE(num)); + POP3Command::DELE(num)->send(store->getConnection()); ref response = - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("DELE", response->getFirstLine()); @@ -615,10 +614,10 @@ void POP3Folder::deleteMessages(const int from, const int to) for (int i = from ; i <= to2 ; ++i) { - store->sendRequest(POP3Command::DELE(i)); + POP3Command::DELE(i)->send(store->getConnection()); ref response = - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("DELE", response->getFirstLine()); @@ -663,10 +662,10 @@ void POP3Folder::deleteMessages(const std::vector & nums) for (std::vector ::const_iterator it = nums.begin() ; it != nums.end() ; ++it) { - store->sendRequest(POP3Command::DELE(*it)); + POP3Command::DELE(*it)->send(store->getConnection()); ref response = - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("DELE", response->getFirstLine()); @@ -760,10 +759,10 @@ void POP3Folder::status(int& count, int& unseen) else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); - store->sendRequest(POP3Command::STAT()); + POP3Command::STAT()->send(store->getConnection()); ref response = - POP3Response::readResponse(store->m_socket, store->m_timeoutHandler); + POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("STAT", response->getFirstLine()); diff --git a/src/net/pop3/POP3Message.cpp b/src/net/pop3/POP3Message.cpp index b7494a71..d2bb9881 100644 --- a/src/net/pop3/POP3Message.cpp +++ b/src/net/pop3/POP3Message.cpp @@ -142,12 +142,12 @@ void POP3Message::extract(utility::outputStream& os, // Emit the "RETR" command ref store = folder.constCast ()->m_store.acquire(); - store->sendRequest(POP3Command::RETR(m_num)); + POP3Command::RETR(m_num)->send(store->getConnection()); try { POP3Response::readLargeResponse - (store->m_socket, store->m_timeoutHandler, os, progress, m_size); + (store->getConnection(), os, progress, m_size); } catch (exceptions::command_error& e) { @@ -198,14 +198,14 @@ void POP3Message::fetch(ref msgFolder, const int options) // Emit the "TOP" command ref store = folder->m_store.acquire(); - store->sendRequest(POP3Command::TOP(m_num, 0)); + POP3Command::TOP(m_num, 0)->send(store->getConnection()); try { string buffer; utility::outputStreamStringAdapter bufferStream(buffer); - POP3Response::readLargeResponse(store->m_socket, store->m_timeoutHandler, + POP3Response::readLargeResponse(store->getConnection(), bufferStream, /* progress */ NULL, /* predictedSize */ 0); m_header = vmime::create
(); diff --git a/src/net/pop3/POP3Response.cpp b/src/net/pop3/POP3Response.cpp index d61e1e6d..975cd642 100644 --- a/src/net/pop3/POP3Response.cpp +++ b/src/net/pop3/POP3Response.cpp @@ -28,6 +28,7 @@ #include "vmime/net/pop3/POP3Response.hpp" +#include "vmime/net/pop3/POP3Connection.hpp" #include "vmime/platform.hpp" @@ -52,10 +53,10 @@ POP3Response::POP3Response(ref sok, ref toh) // static -ref POP3Response::readResponse - (ref sok, ref toh) +ref POP3Response::readResponse(ref conn) { - ref resp = vmime::create (sok, toh); + ref resp = vmime::create + (conn->getSocket(), conn->getTimeoutHandler()); string buffer; resp->readResponseImpl(buffer, /* multiLine */ false); @@ -69,10 +70,10 @@ ref POP3Response::readResponse // static -ref POP3Response::readMultilineResponse - (ref sok, ref toh) +ref POP3Response::readMultilineResponse(ref conn) { - ref resp = vmime::create (sok, toh); + ref resp = vmime::create + (conn->getSocket(), conn->getTimeoutHandler()); string buffer; resp->readResponseImpl(buffer, /* multiLine */ true); @@ -96,10 +97,11 @@ ref POP3Response::readMultilineResponse // static ref POP3Response::readLargeResponse - (ref sok, ref toh, - utility::outputStream& os, utility::progressListener* progress, const long predictedSize) + (ref conn, utility::outputStream& os, + utility::progressListener* progress, const long predictedSize) { - ref resp = vmime::create (sok, toh); + ref resp = vmime::create + (conn->getSocket(), conn->getTimeoutHandler()); string firstLine; resp->readResponseImpl(firstLine, os, progress, predictedSize); 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 -// Helpers for service properties -#define GET_PROPERTY(type, prop) \ - (getInfos().getPropertyValue (getSession(), \ - dynamic_cast (getInfos()).getProperties().prop)) -#define HAS_PROPERTY(prop) \ - (getInfos().hasProperty(getSession(), \ - dynamic_cast (getInfos()).getProperties().prop)) - - namespace vmime { namespace net { namespace pop3 { POP3Store::POP3Store(ref sess, ref 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); + m_connection = vmime::create + (thisRef().dynamicCast (), 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_isPOP3S) // dedicated port/POP3S - { - ref tlsSession = - tls::TLSSession::create(getCertificateVerifier()); - - ref tlsSocket = - tlsSession->getSocket(m_socket); - - m_socket = tlsSocket; - - m_secured = true; - m_cntInfos = vmime::create (address, port, tlsSession, tlsSocket); - } - else -#endif // VMIME_HAVE_TLS_SUPPORT - { - m_cntInfos = vmime::create (address, port); - } - - m_socket->connect(address, port); - - // Connection - // - // eg: C: - // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <36938848.1056800841.634@somewhere.com> - - ref response = readResponse(); - - if (!response->isSuccess()) - { - internalDisconnect(); - throw exceptions::connection_greeting_error(response->getFirstLine()); - } - -#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 - { - 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; - } - } -#endif // VMIME_HAVE_TLS_SUPPORT - - // Start authentication process - authenticate(messageId(response->getText())); -} - - -void POP3Store::authenticate(const messageId& randomMID) -{ - getAuthenticator()->setService(thisRef().dynamicCast ()); - -#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 - // --- S: +OK vincent is a valid mailbox - - const string username = getAuthenticator()->getUsername(); - const string password = getAuthenticator()->getPassword(); - - ref response; - - if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP)) - { - if (randomMID.getLeft().length() != 0 && - randomMID.getRight().length() != 0) - { - // is the result of MD5 applied to "password" - ref 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; -} - - -#if VMIME_HAVE_SASL_SUPPORT - -void POP3Store::authenticateSASL() -{ - if (!getAuthenticator().dynamicCast ()) - throw exceptions::authentication_error("No SASL authenticator available."); - - std::vector capa = getCapabilities(); - std::vector 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 > mechList; - - ref saslContext = - vmime::create (); - - 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 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 ()-> - 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 mech = mechList[i]; - - ref saslSession = - saslContext->createSession("pop3", getAuthenticator(), mech); - - saslSession->init(); - - sendRequest(POP3Command::AUTH(mech->getName())); - - for (bool cont = true ; cont ; ) - { - ref 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."); -} - -#endif // VMIME_HAVE_SASL_SUPPORT - - -#if VMIME_HAVE_TLS_SUPPORT - -void POP3Store::startTLS() -{ try { - sendRequest(POP3Command::STLS()); - - ref response = readResponse(); - - if (!response->isSuccess()) - throw exceptions::command_error("STLS", response->getFirstLine()); - - ref tlsSession = - tls::TLSSession::create(getCertificateVerifier()); - - ref tlsSocket = - tlsSession->getSocket(m_socket); - - tlsSocket->handshake(m_timeoutHandler); - - m_socket = tlsSocket; - - m_secured = true; - m_cntInfos = vmime::create - (m_cntInfos->getHost(), m_cntInfos->getPort(), tlsSession, tlsSocket); + m_connection->connect(); } - catch (exceptions::command_error&) + catch (std::exception&) { - // Non-fatal error - throw; - } - catch (exception&) - { - // Fatal error - internalDisconnect(); + m_connection = NULL; throw; } } -#endif // VMIME_HAVE_TLS_SUPPORT + +bool POP3Store::isPOP3S() const +{ + return m_isPOP3S; +} bool POP3Store::isConnected() const { - return (m_socket && m_socket->isConnected() && m_authentified); + return m_connection && m_connection->isConnected(); } bool POP3Store::isSecuredConnection() const { - return m_secured; + if (m_connection == NULL) + return false; + + return m_connection->isSecuredConnection(); } ref POP3Store::getConnectionInfos() const { - return m_cntInfos; + if (m_connection == NULL) + return NULL; + + return m_connection->getConnectionInfos(); +} + + +ref POP3Store::getConnection() +{ + return m_connection; } @@ -596,12 +165,6 @@ void POP3Store::disconnect() if (!isConnected()) throw exceptions::not_connected(); - internalDisconnect(); -} - - -void POP3Store::internalDisconnect() -{ for (std::list ::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(); - ref response = - readResponse(); + POP3Command::NOOP()->send(m_connection); + + ref response = POP3Response::readResponse(m_connection); if (!response->isSuccess()) throw exceptions::command_error("NOOP", response->getFirstLine()); } -const std::vector POP3Store::getCapabilities() -{ - sendRequest(POP3Command::CAPA()); - - ref response = - POP3Response::readMultilineResponse(m_socket, m_timeoutHandler); - - std::vector 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 cmd) -{ - cmd->writeToSocket(m_socket); -} - - -ref POP3Store::readResponse() -{ - ref resp = - readResponse(); - - return resp; -} - - void POP3Store::registerFolder(POP3Folder* folder) { m_folders.push_back(folder); diff --git a/tests/net/pop3/POP3CommandTest.cpp b/tests/net/pop3/POP3CommandTest.cpp index 35a3629d..14c92be4 100644 --- a/tests/net/pop3/POP3CommandTest.cpp +++ b/tests/net/pop3/POP3CommandTest.cpp @@ -23,6 +23,8 @@ #include "tests/testUtils.hpp" +#include "tests/net/pop3/POP3TestUtils.hpp" + #include "vmime/net/pop3/POP3Command.hpp" @@ -212,7 +214,10 @@ VMIME_TEST_SUITE_BEGIN(POP3CommandTest) vmime::ref cmd = POP3Command::createCommand("MY_COMMAND param1 param2"); vmime::ref sok = vmime::create (); - cmd->writeToSocket(sok); + vmime::ref conn = vmime::create + (sok.dynamicCast (), vmime::null); + + cmd->send(conn); vmime::string response; sok->localReceive(response); diff --git a/tests/net/pop3/POP3ResponseTest.cpp b/tests/net/pop3/POP3ResponseTest.cpp index 1541ce8b..f1fbcd54 100644 --- a/tests/net/pop3/POP3ResponseTest.cpp +++ b/tests/net/pop3/POP3ResponseTest.cpp @@ -23,6 +23,8 @@ #include "tests/testUtils.hpp" +#include "tests/net/pop3/POP3TestUtils.hpp" + #include "vmime/net/pop3/POP3Response.hpp" @@ -48,10 +50,13 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("+OK Response Text\r\n"); vmime::ref resp = - POP3Response::readResponse(socket, toh); + POP3Response::readResponse(conn); VASSERT_EQ("Code", POP3Response::CODE_OK, resp->getCode()); VASSERT_TRUE("Success", resp->isSuccess()); @@ -65,10 +70,13 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("-ERR Response Text\r\n"); vmime::ref resp = - POP3Response::readResponse(socket, toh); + POP3Response::readResponse(conn); VASSERT_EQ("Code", POP3Response::CODE_ERR, resp->getCode()); VASSERT_FALSE("Success", resp->isSuccess()); @@ -82,10 +90,13 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("+ challenge_string\r\n"); vmime::ref resp = - POP3Response::readResponse(socket, toh); + POP3Response::readResponse(conn); VASSERT_EQ("Code", POP3Response::CODE_READY, resp->getCode()); VASSERT_FALSE("Success", resp->isSuccess()); @@ -99,10 +110,13 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("Invalid Response Text\r\n"); vmime::ref resp = - POP3Response::readResponse(socket, toh); + POP3Response::readResponse(conn); VASSERT_EQ("Code", POP3Response::CODE_ERR, resp->getCode()); VASSERT_FALSE("Success", resp->isSuccess()); @@ -116,10 +130,13 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("+OK Response terminated by LF\n"); vmime::ref resp = - POP3Response::readResponse(socket, toh); + POP3Response::readResponse(conn); VASSERT_EQ("Code", POP3Response::CODE_OK, resp->getCode()); VASSERT_TRUE("Success", resp->isSuccess()); @@ -133,13 +150,16 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("+OK Response Text\r\n"); socket->localSend("Line 1\r\n"); socket->localSend("Line 2\r\n"); socket->localSend(".\r\n"); vmime::ref resp = - POP3Response::readMultilineResponse(socket, toh); + POP3Response::readMultilineResponse(conn); VASSERT_EQ("Code", POP3Response::CODE_OK, resp->getCode()); VASSERT_TRUE("Success", resp->isSuccess()); @@ -155,13 +175,16 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("+OK Response Text\n"); socket->localSend("Line 1\n"); socket->localSend("Line 2\n"); socket->localSend(".\n"); vmime::ref resp = - POP3Response::readMultilineResponse(socket, toh); + POP3Response::readMultilineResponse(conn); VASSERT_EQ("Code", POP3Response::CODE_OK, resp->getCode()); VASSERT_TRUE("Success", resp->isSuccess()); @@ -182,6 +205,9 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("+OK Large Response Follows\n"); socket->localSend(data.str()); socket->localSend("\r\n.\r\n"); @@ -190,7 +216,7 @@ VMIME_TEST_SUITE_BEGIN(POP3ResponseTest) vmime::utility::outputStreamStringAdapter receivedDataStream(receivedData); vmime::ref resp = - POP3Response::readLargeResponse(socket, toh, receivedDataStream, NULL, 0); + POP3Response::readLargeResponse(conn, receivedDataStream, NULL, 0); VASSERT_EQ("Code", POP3Response::CODE_OK, resp->getCode()); VASSERT_TRUE("Success", resp->isSuccess()); diff --git a/tests/net/pop3/POP3TestUtils.hpp b/tests/net/pop3/POP3TestUtils.hpp new file mode 100644 index 00000000..26b6601a --- /dev/null +++ b/tests/net/pop3/POP3TestUtils.hpp @@ -0,0 +1,53 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// 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/net/pop3/POP3Connection.hpp" +#include "vmime/net/pop3/POP3Store.hpp" + + +class POP3ConnectionTest : public vmime::net::pop3::POP3Connection +{ +public: + + POP3ConnectionTest(vmime::ref socket, + vmime::ref timeoutHandler) + : POP3Connection(NULL, NULL), + m_socket(socket), m_timeoutHandler(timeoutHandler) + { + } + + vmime::ref getSocket() + { + return m_socket; + } + + vmime::ref getTimeoutHandler() + { + return m_timeoutHandler; + } + +private: + + vmime::ref m_socket; + vmime::ref m_timeoutHandler; +}; diff --git a/tests/net/pop3/POP3UtilsTest.cpp b/tests/net/pop3/POP3UtilsTest.cpp index ac92056a..9d443ddb 100644 --- a/tests/net/pop3/POP3UtilsTest.cpp +++ b/tests/net/pop3/POP3UtilsTest.cpp @@ -23,6 +23,8 @@ #include "tests/testUtils.hpp" +#include "tests/net/pop3/POP3TestUtils.hpp" + #include "vmime/net/pop3/POP3Utils.hpp" #include "vmime/net/pop3/POP3Response.hpp" @@ -42,6 +44,9 @@ VMIME_TEST_SUITE_BEGIN(POP3UtilsTest) vmime::ref socket = vmime::create (); vmime::ref toh = vmime::create (); + vmime::ref conn = vmime::create + (socket.dynamicCast (), toh); + socket->localSend("+OK Response Text\r\n"); socket->localSend("1 abcdef\r\n"); socket->localSend("23 ghijkl\r\n"); @@ -51,7 +56,7 @@ VMIME_TEST_SUITE_BEGIN(POP3UtilsTest) socket->localSend(".\r\n"); vmime::ref resp = - POP3Response::readMultilineResponse(socket, toh); + POP3Response::readMultilineResponse(conn); std::map result; POP3Utils::parseMultiListOrUidlResponse(resp, result); diff --git a/vmime/net/pop3/POP3Command.hpp b/vmime/net/pop3/POP3Command.hpp index 68731661..ef2b3ac4 100644 --- a/vmime/net/pop3/POP3Command.hpp +++ b/vmime/net/pop3/POP3Command.hpp @@ -42,15 +42,12 @@ class mailbox; namespace net { - - -class socket; -class timeoutHandler; - - namespace pop3 { +class POP3Connection; + + /** A POP3 command that will be sent to the server. */ class VMIME_EXPORT POP3Command : public object @@ -84,11 +81,11 @@ public: */ static ref createCommand(const string& text); - /** Sends this command to the specified socket. + /** Sends this command over the specified connection. * - * @param sok socket to which the command will be written + * @param conn connection onto which the command will be sent */ - virtual void writeToSocket(ref sok); + virtual void send(ref conn); /** Returns the full text of the command, including command name * and parameters (if any). diff --git a/vmime/net/pop3/POP3Connection.hpp b/vmime/net/pop3/POP3Connection.hpp new file mode 100644 index 00000000..8a900cb2 --- /dev/null +++ b/vmime/net/pop3/POP3Connection.hpp @@ -0,0 +1,122 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// 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_POP3_POP3CONNECTION_HPP_INCLUDED +#define VMIME_NET_POP3_POP3CONNECTION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#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/pop3/POP3Command.hpp" +#include "vmime/net/pop3/POP3Response.hpp" + +#include "vmime/security/authenticator.hpp" + + +namespace vmime { +namespace net { + + +class socket; +class timeoutHandler; + + +namespace pop3 { + + +class POP3Store; + + +/** Manage connection to a POP3 server. + */ +class VMIME_EXPORT POP3Connection : public object +{ + friend class vmime::creator; + +public: + + POP3Connection(ref store, ref auth); + virtual ~POP3Connection(); + + + virtual void connect(); + virtual bool isConnected() const; + virtual void disconnect(); + + bool isSecuredConnection() const; + ref getConnectionInfos() const; + + virtual ref getStore(); + virtual ref getSocket(); + virtual ref getTimeoutHandler(); + virtual ref getAuthenticator(); + virtual ref getSession(); + +private: + + void authenticate(const messageId& randomMID); +#if VMIME_HAVE_SASL_SUPPORT + void authenticateSASL(); +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT + + const std::vector getCapabilities(); + + void internalDisconnect(); + + + weak_ref m_store; + + ref m_auth; + ref m_socket; + ref m_timeoutHandler; + + bool m_authenticated; + bool m_secured; + + ref m_cntInfos; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3CONNECTION_HPP_INCLUDED diff --git a/vmime/net/pop3/POP3Response.hpp b/vmime/net/pop3/POP3Response.hpp index c2997a27..efe72d05 100644 --- a/vmime/net/pop3/POP3Response.hpp +++ b/vmime/net/pop3/POP3Response.hpp @@ -50,6 +50,9 @@ class timeoutHandler; namespace pop3 { +class POP3Connection; + + /** A POP3 response, as sent by the server. */ class VMIME_EXPORT POP3Response : public object @@ -68,34 +71,29 @@ public: /** Receive and parse a POP3 response from the - * specified socket. + * specified connection. * - * @param sok socket from which to read - * @param toh time-out handler (can be NULL) + * @param conn connection from which to read * @return POP3 response * @throws exceptions::operation_timed_out if no data * has been received within the granted time */ - static ref readResponse - (ref sok, ref toh); + static ref readResponse(ref conn); /** Receive and parse a multiline POP3 response from - * the specified socket. + * the specified connection. * - * @param sok socket from which to read - * @param toh time-out handler (can be NULL) + * @param conn connection from which to read * @return POP3 response * @throws exceptions::operation_timed_out if no data * has been received within the granted time */ - static ref readMultilineResponse - (ref sok, ref toh); + static ref readMultilineResponse(ref conn); /** Receive and parse a large POP3 response (eg. message data) - * from the specified socket. + * from the specified connection. * - * @param sok socket from which to read - * @param toh time-out handler (can be NULL) + * @param conn connection from which to read * @param os output stream to which response data will be written * @param progress progress listener (can be NULL) * @param predictedSize estimated size of response data (in bytes) @@ -104,8 +102,7 @@ public: * has been received within the granted time */ static ref readLargeResponse - (ref sok, ref toh, - utility::outputStream& os, + (ref conn, utility::outputStream& os, utility::progressListener* progress, const long predictedSize); diff --git a/vmime/net/pop3/POP3Store.hpp b/vmime/net/pop3/POP3Store.hpp index 681a295e..17dfdbb0 100644 --- a/vmime/net/pop3/POP3Store.hpp +++ b/vmime/net/pop3/POP3Store.hpp @@ -31,13 +31,10 @@ #if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 -#include "vmime/messageId.hpp" - #include "vmime/net/store.hpp" -#include "vmime/net/socket.hpp" -#include "vmime/net/timeoutHandler.hpp" #include "vmime/net/pop3/POP3ServiceInfos.hpp" +#include "vmime/net/pop3/POP3Connection.hpp" #include "vmime/utility/stream.hpp" @@ -86,24 +83,13 @@ public: bool isSecuredConnection() const; ref getConnectionInfos() const; + ref getConnection(); + + bool isPOP3S() const; private: - void authenticate(const messageId& randomMID); -#if VMIME_HAVE_SASL_SUPPORT - void authenticateSASL(); -#endif // VMIME_HAVE_SASL_SUPPORT - -#if VMIME_HAVE_TLS_SUPPORT - void startTLS(); -#endif // VMIME_HAVE_TLS_SUPPORT - - const std::vector getCapabilities(); - - void sendRequest(ref cmd); - ref readResponse(); - - void internalDisconnect(); + ref m_connection; void registerFolder(POP3Folder* folder); @@ -112,16 +98,8 @@ private: std::list m_folders; - ref m_socket; - bool m_authentified; - - ref m_timeoutHandler; - const bool m_isPOP3S; - bool m_secured; - ref m_cntInfos; - // Service infos static POP3ServiceInfos sm_infos;