aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2013-06-12 12:02:40 +0000
committerVincent Richard <[email protected]>2013-06-12 12:02:40 +0000
commit7ab35173bce99c6c9a5f189cdd83f93fcb3e9086 (patch)
tree3fb077645ba770932adaa1f629b7c2f0931a6cf0
parentMoved POP3 client command related things to POP3Command class. (diff)
downloadvmime-7ab35173bce99c6c9a5f189cdd83f93fcb3e9086.tar.gz
vmime-7ab35173bce99c6c9a5f189cdd83f93fcb3e9086.zip
Moved POP3 connection-related things to POP3Connection object.
-rw-r--r--SConstruct1
-rw-r--r--src/net/pop3/POP3Command.cpp5
-rw-r--r--src/net/pop3/POP3Connection.cpp642
-rw-r--r--src/net/pop3/POP3Folder.cpp41
-rw-r--r--src/net/pop3/POP3Message.cpp8
-rw-r--r--src/net/pop3/POP3Response.cpp20
-rw-r--r--src/net/pop3/POP3Store.cpp543
-rw-r--r--tests/net/pop3/POP3CommandTest.cpp7
-rw-r--r--tests/net/pop3/POP3ResponseTest.cpp42
-rw-r--r--tests/net/pop3/POP3TestUtils.hpp53
-rw-r--r--tests/net/pop3/POP3UtilsTest.cpp7
-rw-r--r--vmime/net/pop3/POP3Command.hpp13
-rw-r--r--vmime/net/pop3/POP3Connection.hpp122
-rw-r--r--vmime/net/pop3/POP3Response.hpp27
-rw-r--r--vmime/net/pop3/POP3Store.hpp32
15 files changed, 953 insertions, 610 deletions
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 <socket> sok)
+void POP3Command::send(ref <POP3Connection> 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 <[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 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 <type>(getSession(), \
+ dynamic_cast <const POP3ServiceInfos&>(m_store.acquire()->getInfos()).getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (m_store.acquire()->getInfos().hasProperty(getSession(), \
+ dynamic_cast <const POP3ServiceInfos&>(m_store.acquire()->getInfos()).getProperties().prop))
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+
+POP3Connection::POP3Connection(ref <POP3Store> store, ref <security::authenticator> 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 <POP3Store> 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 <tls::TLSSession> tlsSession =
+ tls::TLSSession::create(store->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: +OK MailSite POP3 Server 5.3.4.0 Ready <[email protected]>
+
+ ref <POP3Response> response = POP3Response::readResponse
+ (thisRef().dynamicCast <POP3Connection>());
+
+ 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 <POP3Connection>());
+ POP3Response::readResponse(thisRef().dynamicCast <POP3Connection>());
+ }
+ 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 <service>());
+
+#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 <digest>
+ // --- S: +OK vincent is a valid mailbox
+
+ const string username = getAuthenticator()->getUsername();
+ const string password = getAuthenticator()->getPassword();
+
+ ref <POP3Connection> conn = thisRef().dynamicCast <POP3Connection>();
+ 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();
+
+ 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 <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();
+
+ POP3Command::AUTH(mech->getName())->send(thisRef().dynamicCast <POP3Connection>());
+
+ for (bool cont = true ; cont ; )
+ {
+ ref <POP3Response> response =
+ POP3Response::readResponse(thisRef().dynamicCast <POP3Connection>());
+
+ 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 <POP3Connection>());
+
+ ref <POP3Response> response =
+ POP3Response::readResponse(thisRef().dynamicCast <POP3Connection>());
+
+ if (!response->isSuccess())
+ throw exceptions::command_error("STLS", response->getFirstLine());
+
+ ref <tls::TLSSession> tlsSession =
+ tls::TLSSession::create(m_store.acquire()->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
+
+
+const std::vector <string> POP3Connection::getCapabilities()
+{
+ POP3Command::CAPA()->send(thisRef().dynamicCast <POP3Connection>());
+
+ ref <POP3Response> response =
+ POP3Response::readMultilineResponse(thisRef().dynamicCast <POP3Connection>());
+
+ 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;
+}
+
+
+bool POP3Connection::isConnected() const
+{
+ return m_socket && m_socket->isConnected() && m_authenticated;
+}
+
+
+bool POP3Connection::isSecuredConnection() const
+{
+ return m_secured;
+}
+
+
+ref <connectionInfos> POP3Connection::getConnectionInfos() const
+{
+ return m_cntInfos;
+}
+
+
+ref <POP3Store> POP3Connection::getStore()
+{
+ return m_store.acquire();
+}
+
+
+ref <session> POP3Connection::getSession()
+{
+ return m_store.acquire()->getSession();
+}
+
+
+ref <socket> POP3Connection::getSocket()
+{
+ return m_socket;
+}
+
+
+ref <timeoutHandler> POP3Connection::getTimeoutHandler()
+{
+ return m_timeoutHandler;
+}
+
+
+ref <security::authenticator> 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 <POP3Response> response =
- POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
+ ref <POP3Response> 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 <ref <message> >& 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 <POP3Response> 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 <ref <message> >& 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 <POP3Response> response =
- POP3Response::readMultilineResponse(store->m_socket, store->m_timeoutHandler);
+ POP3Response::readMultilineResponse(store->getConnection());
if (response->isSuccess())
{
@@ -452,11 +451,11 @@ void POP3Folder::fetchMessage(ref <message> 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 <POP3Response> response =
- POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
+ POP3Response::readResponse(store->getConnection());
if (response->isSuccess())
{
@@ -485,11 +484,11 @@ void POP3Folder::fetchMessage(ref <message> 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 <POP3Response> 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 <POP3Response> 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 <POP3Response> 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 <int>& nums)
for (std::vector <int>::const_iterator
it = nums.begin() ; it != nums.end() ; ++it)
{
- store->sendRequest(POP3Command::DELE(*it));
+ POP3Command::DELE(*it)->send(store->getConnection());
ref <POP3Response> 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 <POP3Response> 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 <POP3Store> store = folder.constCast <POP3Folder>()->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 <POP3Folder> msgFolder, const int options)
// Emit the "TOP" command
ref <POP3Store> 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 <header>();
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 <socket> sok, ref <timeoutHandler> toh)
// static
-ref <POP3Response> POP3Response::readResponse
- (ref <socket> sok, ref <timeoutHandler> toh)
+ref <POP3Response> POP3Response::readResponse(ref <POP3Connection> conn)
{
- ref <POP3Response> resp = vmime::create <POP3Response>(sok, toh);
+ ref <POP3Response> resp = vmime::create <POP3Response>
+ (conn->getSocket(), conn->getTimeoutHandler());
string buffer;
resp->readResponseImpl(buffer, /* multiLine */ false);
@@ -69,10 +70,10 @@ ref <POP3Response> POP3Response::readResponse
// static
-ref <POP3Response> POP3Response::readMultilineResponse
- (ref <socket> sok, ref <timeoutHandler> toh)
+ref <POP3Response> POP3Response::readMultilineResponse(ref <POP3Connection> conn)
{
- ref <POP3Response> resp = vmime::create <POP3Response>(sok, toh);
+ ref <POP3Response> resp = vmime::create <POP3Response>
+ (conn->getSocket(), conn->getTimeoutHandler());
string buffer;
resp->readResponseImpl(buffer, /* multiLine */ true);
@@ -96,10 +97,11 @@ ref <POP3Response> POP3Response::readMultilineResponse
// static
ref <POP3Response> POP3Response::readLargeResponse
- (ref <socket> sok, ref <timeoutHandler> toh,
- utility::outputStream& os, utility::progressListener* progress, const long predictedSize)
+ (ref <POP3Connection> conn, utility::outputStream& os,
+ utility::progressListener* progress, const long predictedSize)
{
- ref <POP3Response> resp = vmime::create <POP3Response>(sok, toh);
+ ref <POP3Response> resp = vmime::create <POP3Response>
+ (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 <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);
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 <POP3Command> cmd = POP3Command::createCommand("MY_COMMAND param1 param2");
vmime::ref <testSocket> sok = vmime::create <testSocket>();
- cmd->writeToSocket(sok);
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (sok.dynamicCast <vmime::net::socket>(), 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), toh);
+
socket->localSend("+OK Response Text\r\n");
vmime::ref <POP3Response> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), toh);
+
socket->localSend("-ERR Response Text\r\n");
vmime::ref <POP3Response> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), toh);
+
socket->localSend("+ challenge_string\r\n");
vmime::ref <POP3Response> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), toh);
+
socket->localSend("Invalid Response Text\r\n");
vmime::ref <POP3Response> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), toh);
+
socket->localSend("+OK Response terminated by LF\n");
vmime::ref <POP3Response> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), 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 <POP3Response> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), toh);
+
socket->localSend("+OK Response Text\n");
socket->localSend("Line 1\n");
socket->localSend("Line 2\n");
socket->localSend(".\n");
vmime::ref <POP3Response> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), 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 <POP3Response> 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 <[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 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 <vmime::net::socket> socket,
+ vmime::ref <vmime::net::timeoutHandler> timeoutHandler)
+ : POP3Connection(NULL, NULL),
+ m_socket(socket), m_timeoutHandler(timeoutHandler)
+ {
+ }
+
+ vmime::ref <vmime::net::socket> getSocket()
+ {
+ return m_socket;
+ }
+
+ vmime::ref <vmime::net::timeoutHandler> getTimeoutHandler()
+ {
+ return m_timeoutHandler;
+ }
+
+private:
+
+ vmime::ref <vmime::net::socket> m_socket;
+ vmime::ref <vmime::net::timeoutHandler> 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 <testSocket> socket = vmime::create <testSocket>();
vmime::ref <vmime::net::timeoutHandler> toh = vmime::create <testTimeoutHandler>();
+ vmime::ref <POP3ConnectionTest> conn = vmime::create <POP3ConnectionTest>
+ (socket.dynamicCast <vmime::net::socket>(), 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 <POP3Response> resp =
- POP3Response::readMultilineResponse(socket, toh);
+ POP3Response::readMultilineResponse(conn);
std::map <int, vmime::string> 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,13 +42,10 @@ class mailbox;
namespace net {
+namespace pop3 {
-class socket;
-class timeoutHandler;
-
-
-namespace pop3 {
+class POP3Connection;
/** A POP3 command that will be sent to the server.
@@ -84,11 +81,11 @@ public:
*/
static ref <POP3Command> 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 <socket> sok);
+ virtual void send(ref <POP3Connection> 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 <[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 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 <POP3Store> store, ref <security::authenticator> auth);
+ virtual ~POP3Connection();
+
+
+ virtual void connect();
+ virtual bool isConnected() const;
+ virtual void disconnect();
+
+ bool isSecuredConnection() const;
+ ref <connectionInfos> getConnectionInfos() const;
+
+ virtual ref <POP3Store> getStore();
+ virtual ref <socket> getSocket();
+ virtual ref <timeoutHandler> getTimeoutHandler();
+ virtual ref <security::authenticator> getAuthenticator();
+ virtual ref <session> 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 <string> getCapabilities();
+
+ void internalDisconnect();
+
+
+ weak_ref <POP3Store> m_store;
+
+ ref <security::authenticator> m_auth;
+ ref <socket> m_socket;
+ ref <timeoutHandler> m_timeoutHandler;
+
+ bool m_authenticated;
+ bool m_secured;
+
+ ref <connectionInfos> 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 <POP3Response> readResponse
- (ref <socket> sok, ref <timeoutHandler> toh);
+ static ref <POP3Response> readResponse(ref <POP3Connection> 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 <POP3Response> readMultilineResponse
- (ref <socket> sok, ref <timeoutHandler> toh);
+ static ref <POP3Response> readMultilineResponse(ref <POP3Connection> 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 <POP3Response> readLargeResponse
- (ref <socket> sok, ref <timeoutHandler> toh,
- utility::outputStream& os,
+ (ref <POP3Connection> 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 <connectionInfos> getConnectionInfos() const;
+ ref <POP3Connection> getConnection();
-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 <string> getCapabilities();
+ bool isPOP3S() const;
- void sendRequest(ref <POP3Command> cmd);
- ref <POP3Response> readResponse();
+private:
- void internalDisconnect();
+ ref <POP3Connection> m_connection;
void registerFolder(POP3Folder* folder);
@@ -112,16 +98,8 @@ private:
std::list <POP3Folder*> m_folders;
- ref <socket> m_socket;
- bool m_authentified;
-
- ref <timeoutHandler> m_timeoutHandler;
-
const bool m_isPOP3S;
- bool m_secured;
- ref <connectionInfos> m_cntInfos;
-
// Service infos
static POP3ServiceInfos sm_infos;