Connection trace facility.

This commit is contained in:
Vincent Richard 2014-03-16 22:52:40 +01:00
parent cfe9c6b1e7
commit 84e570bbbb
35 changed files with 810 additions and 287 deletions

View File

@ -31,178 +31,17 @@
#include "vmime/vmime.hpp"
#include "vmime/platforms/posix/posixHandler.hpp"
#include "example6_tracer.hpp"
#include "example6_authenticator.hpp"
#include "example6_certificateVerifier.hpp"
#include "example6_timeoutHandler.hpp"
// Global session object
static vmime::shared_ptr <vmime::net::session> g_session
= vmime::make_shared <vmime::net::session>();
#if VMIME_HAVE_SASL_SUPPORT
// SASL authentication handler
class interactiveAuthenticator : public vmime::security::sasl::defaultSASLAuthenticator
{
const std::vector <vmime::shared_ptr <vmime::security::sasl::SASLMechanism> > getAcceptableMechanisms
(const std::vector <vmime::shared_ptr <vmime::security::sasl::SASLMechanism> >& available,
vmime::shared_ptr <vmime::security::sasl::SASLMechanism> suggested) const
{
std::cout << std::endl << "Available SASL mechanisms:" << std::endl;
for (unsigned int i = 0 ; i < available.size() ; ++i)
{
std::cout << " " << available[i]->getName();
if (suggested && available[i]->getName() == suggested->getName())
std::cout << "(suggested)";
}
std::cout << std::endl << std::endl;
return defaultSASLAuthenticator::getAcceptableMechanisms(available, suggested);
}
void setSASLMechanism(vmime::shared_ptr <vmime::security::sasl::SASLMechanism> mech)
{
std::cout << "Trying '" << mech->getName() << "' authentication mechanism" << std::endl;
defaultSASLAuthenticator::setSASLMechanism(mech);
}
const vmime::string getUsername() const
{
if (m_username.empty())
m_username = getUserInput("Username");
return m_username;
}
const vmime::string getPassword() const
{
if (m_password.empty())
m_password = getUserInput("Password");
return m_password;
}
static const vmime::string getUserInput(const std::string& prompt)
{
std::cout << prompt << ": ";
std::cout.flush();
vmime::string res;
std::getline(std::cin, res);
return res;
}
private:
mutable vmime::string m_username;
mutable vmime::string m_password;
};
#else // !VMIME_HAVE_SASL_SUPPORT
// Simple authentication handler
class interactiveAuthenticator : public vmime::security::defaultAuthenticator
{
const vmime::string getUsername() const
{
if (m_username.empty())
m_username = getUserInput("Username");
return m_username;
}
const vmime::string getPassword() const
{
if (m_password.empty())
m_password = getUserInput("Password");
return m_password;
}
static const vmime::string getUserInput(const std::string& prompt)
{
std::cout << prompt << ": ";
std::cout.flush();
vmime::string res;
std::getline(std::cin, res);
return res;
}
private:
mutable vmime::string m_username;
mutable vmime::string m_password;
};
#endif // VMIME_HAVE_SASL_SUPPORT
#if VMIME_HAVE_TLS_SUPPORT
// Certificate verifier (TLS/SSL)
class interactiveCertificateVerifier : public vmime::security::cert::defaultCertificateVerifier
{
public:
void verify(vmime::shared_ptr <vmime::security::cert::certificateChain> chain, const vmime::string& hostname)
{
try
{
setX509TrustedCerts(m_trustedCerts);
defaultCertificateVerifier::verify(chain, hostname);
}
catch (vmime::exceptions::certificate_verification_exception&)
{
// Obtain subject's certificate
vmime::shared_ptr <vmime::security::cert::certificate> cert = chain->getAt(0);
std::cout << std::endl;
std::cout << "Server sent a '" << cert->getType() << "'" << " certificate." << std::endl;
std::cout << "Do you want to accept this certificate? (Y/n) ";
std::cout.flush();
std::string answer;
std::getline(std::cin, answer);
if (answer.length() != 0 &&
(answer[0] == 'Y' || answer[0] == 'y'))
{
// Accept it, and remember user's choice for later
if (cert->getType() == "X.509")
{
m_trustedCerts.push_back(vmime::dynamicCast
<vmime::security::cert::X509Certificate>(cert));
setX509TrustedCerts(m_trustedCerts);
defaultCertificateVerifier::verify(chain, hostname);
}
return;
}
throw vmime::exceptions::certificate_verification_exception
("User did not accept the certificate.");
}
}
private:
static std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > m_trustedCerts;
};
std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> >
interactiveCertificateVerifier::m_trustedCerts;
#endif // VMIME_HAVE_TLS_SUPPORT
/** Returns the messaging protocols supported by VMime.
*
* @param type service type (vmime::net::service::TYPE_STORE or
@ -290,53 +129,6 @@ static std::ostream& operator<<(std::ostream& os, const vmime::exception& e)
}
/** Time out handler.
* Used to stop the current operation after too much time, or if the user
* requested cancellation.
*/
class timeoutHandler : public vmime::net::timeoutHandler
{
public:
bool isTimeOut()
{
// This is a cancellation point: return true if you want to cancel
// the current operation. If you return true, handleTimeOut() will
// be called just after this, and before actually cancelling the
// operation
return false;
}
void resetTimeOut()
{
// Called at the beginning of an operation (eg. connecting,
// a read() or a write() on a socket...)
}
bool handleTimeOut()
{
// If isTimeOut() returned true, this function will be called. This
// allows you to interact with the user, ie. display a prompt to
// know whether he wants to cancel the operation.
// If you return true here, the operation will be actually cancelled.
// If not, the time out is reset and the operation continues.
return true;
}
};
class timeoutHandlerFactory : public vmime::net::timeoutHandlerFactory
{
public:
vmime::shared_ptr <vmime::net::timeoutHandler> create()
{
return vmime::make_shared <timeoutHandler>();
}
};
/** Print the MIME structure of a message on the standard output.
*
* @param s structure object
@ -445,8 +237,12 @@ static void sendMessage()
vmime::utility::url url(urlString);
vmime::shared_ptr <vmime::net::transport> tr =
g_session->getTransport(url, vmime::make_shared <interactiveAuthenticator>());
vmime::shared_ptr <vmime::net::transport> tr;
if (url.getUsername().empty() || url.getPassword().empty())
tr = g_session->getTransport(url, vmime::make_shared <interactiveAuthenticator>());
else
tr = g_session->getTransport(url);
#if VMIME_HAVE_TLS_SUPPORT
@ -465,7 +261,12 @@ static void sendMessage()
// You can also set some properties (see example7 to know the properties
// available for each service). For example, for SMTP:
// tr->setProperty("options.need-authentication", true);
if (!url.getUsername().empty() || !url.getPassword().empty())
tr->setProperty("options.need-authentication", true);
// Trace communication between client and server
vmime::shared_ptr <std::ostringstream> traceStream = vmime::make_shared <std::ostringstream>();
tr->setTracerFactory(vmime::make_shared <myTracerFactory>(traceStream));
// Information about the mail
std::cout << "Enter email of the expeditor (eg. me@somewhere.com): ";
@ -520,6 +321,12 @@ static void sendMessage()
// ...
// tr->send(&msg);
// Display connection log
std::cout << std::endl;
std::cout << "Connection Trace:" << std::endl;
std::cout << "=================" << std::endl;
std::cout << traceStream->str();
tr->disconnect();
}
catch (vmime::exception& e)
@ -580,6 +387,10 @@ static void connectStore()
#endif // VMIME_HAVE_TLS_SUPPORT
// Trace communication between client and server
vmime::shared_ptr <std::ostringstream> traceStream = vmime::make_shared <std::ostringstream>();
st->setTracerFactory(vmime::make_shared <myTracerFactory>(traceStream));
// Connect to the mail store
st->connect();
@ -621,6 +432,7 @@ static void connectStore()
choices.push_back("Change folder");
choices.push_back("Add message (to the current folder)");
choices.push_back("Copy message (into the current folder)");
choices.push_back("Display trace output");
choices.push_back("Return to main menu");
const int choice = printMenu(choices);
@ -928,9 +740,18 @@ static void connectStore()
break;
}
// Main menu
// Display trace output
case 12:
std::cout << std::endl;
std::cout << "Connection Trace:" << std::endl;
std::cout << "=================" << std::endl;
std::cout << traceStream->str();
break;
// Main menu
case 13:
f->close(true); // 'true' to expunge deleted messages
cont = false;
break;
@ -979,7 +800,9 @@ static void connectStore()
std::cerr << std::endl;
std::cerr << "std::exception: " << e.what() << std::endl;
}
}
} // for(cont)
st->disconnect();
}
catch (vmime::exception& e)
{

View File

@ -0,0 +1,106 @@
#if VMIME_HAVE_SASL_SUPPORT
// SASL authentication handler
class interactiveAuthenticator : public vmime::security::sasl::defaultSASLAuthenticator
{
const std::vector <vmime::shared_ptr <vmime::security::sasl::SASLMechanism> > getAcceptableMechanisms
(const std::vector <vmime::shared_ptr <vmime::security::sasl::SASLMechanism> >& available,
vmime::shared_ptr <vmime::security::sasl::SASLMechanism> suggested) const
{
std::cout << std::endl << "Available SASL mechanisms:" << std::endl;
for (unsigned int i = 0 ; i < available.size() ; ++i)
{
std::cout << " " << available[i]->getName();
if (suggested && available[i]->getName() == suggested->getName())
std::cout << "(suggested)";
}
std::cout << std::endl << std::endl;
return defaultSASLAuthenticator::getAcceptableMechanisms(available, suggested);
}
void setSASLMechanism(vmime::shared_ptr <vmime::security::sasl::SASLMechanism> mech)
{
std::cout << "Trying '" << mech->getName() << "' authentication mechanism" << std::endl;
defaultSASLAuthenticator::setSASLMechanism(mech);
}
const vmime::string getUsername() const
{
if (m_username.empty())
m_username = getUserInput("Username");
return m_username;
}
const vmime::string getPassword() const
{
if (m_password.empty())
m_password = getUserInput("Password");
return m_password;
}
static const vmime::string getUserInput(const std::string& prompt)
{
std::cout << prompt << ": ";
std::cout.flush();
vmime::string res;
std::getline(std::cin, res);
return res;
}
private:
mutable vmime::string m_username;
mutable vmime::string m_password;
};
#else // !VMIME_HAVE_SASL_SUPPORT
// Simple authentication handler
class interactiveAuthenticator : public vmime::security::defaultAuthenticator
{
const vmime::string getUsername() const
{
if (m_username.empty())
m_username = getUserInput("Username");
return m_username;
}
const vmime::string getPassword() const
{
if (m_password.empty())
m_password = getUserInput("Password");
return m_password;
}
static const vmime::string getUserInput(const std::string& prompt)
{
std::cout << prompt << ": ";
std::cout.flush();
vmime::string res;
std::getline(std::cin, res);
return res;
}
private:
mutable vmime::string m_username;
mutable vmime::string m_password;
};
#endif // VMIME_HAVE_SASL_SUPPORT

View File

@ -0,0 +1,62 @@
#if VMIME_HAVE_TLS_SUPPORT
// Certificate verifier (TLS/SSL)
class interactiveCertificateVerifier : public vmime::security::cert::defaultCertificateVerifier
{
public:
void verify(vmime::shared_ptr <vmime::security::cert::certificateChain> chain, const vmime::string& hostname)
{
try
{
setX509TrustedCerts(m_trustedCerts);
defaultCertificateVerifier::verify(chain, hostname);
}
catch (vmime::exceptions::certificate_verification_exception&)
{
// Obtain subject's certificate
vmime::shared_ptr <vmime::security::cert::certificate> cert = chain->getAt(0);
std::cout << std::endl;
std::cout << "Server sent a '" << cert->getType() << "'" << " certificate." << std::endl;
std::cout << "Do you want to accept this certificate? (Y/n) ";
std::cout.flush();
std::string answer;
std::getline(std::cin, answer);
if (answer.length() != 0 &&
(answer[0] == 'Y' || answer[0] == 'y'))
{
// Accept it, and remember user's choice for later
if (cert->getType() == "X.509")
{
m_trustedCerts.push_back(vmime::dynamicCast
<vmime::security::cert::X509Certificate>(cert));
setX509TrustedCerts(m_trustedCerts);
defaultCertificateVerifier::verify(chain, hostname);
}
return;
}
throw vmime::exceptions::certificate_verification_exception
("User did not accept the certificate.");
}
}
private:
static std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > m_trustedCerts;
};
std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> >
interactiveCertificateVerifier::m_trustedCerts;
#endif // VMIME_HAVE_TLS_SUPPORT

View File

@ -0,0 +1,48 @@
/** Time out handler.
* Used to stop the current operation after too much time, or if the user
* requested cancellation.
*/
class timeoutHandler : public vmime::net::timeoutHandler
{
public:
bool isTimeOut()
{
// This is a cancellation point: return true if you want to cancel
// the current operation. If you return true, handleTimeOut() will
// be called just after this, and before actually cancelling the
// operation
return false;
}
void resetTimeOut()
{
// Called at the beginning of an operation (eg. connecting,
// a read() or a write() on a socket...)
}
bool handleTimeOut()
{
// If isTimeOut() returned true, this function will be called. This
// allows you to interact with the user, ie. display a prompt to
// know whether he wants to cancel the operation.
// If you return true here, the operation will be actually cancelled.
// If not, the time out is reset and the operation continues.
return true;
}
};
class timeoutHandlerFactory : public vmime::net::timeoutHandlerFactory
{
public:
vmime::shared_ptr <vmime::net::timeoutHandler> create()
{
return vmime::make_shared <timeoutHandler>();
}
};

View File

@ -0,0 +1,53 @@
/** Tracer used to demonstrate logging communication between client and server.
*/
class myTracer : public vmime::net::tracer
{
public:
myTracer(vmime::shared_ptr <std::ostringstream> stream,
vmime::shared_ptr <vmime::net::service> serv, const int connectionId)
: m_stream(stream), m_service(serv), m_connectionId(connectionId)
{
}
void traceSend(const vmime::string& line)
{
*m_stream << "[" << m_service->getProtocolName() << ":" << m_connectionId
<< "] C: " << line << std::endl;
}
void traceReceive(const vmime::string& line)
{
*m_stream << "[" << m_service->getProtocolName() << ":" << m_connectionId
<< "] S: " << line << std::endl;
}
private:
vmime::shared_ptr <std::ostringstream> m_stream;
vmime::shared_ptr <vmime::net::service> m_service;
const int m_connectionId;
};
class myTracerFactory : public vmime::net::tracerFactory
{
public:
myTracerFactory(vmime::shared_ptr <std::ostringstream> stream)
: m_stream(stream)
{
}
vmime::shared_ptr <vmime::net::tracer> create
(vmime::shared_ptr <vmime::net::service> serv, const int connectionId)
{
return vmime::make_shared <myTracer>(m_stream, serv, connectionId);
}
private:
vmime::shared_ptr <std::ostringstream> m_stream;
};

View File

@ -56,7 +56,7 @@ shared_ptr <IMAPCommand> IMAPCommand::LOGIN(const string& username, const string
std::ostringstream trace;
trace.imbue(std::locale::classic());
trace << "LOGIN <username> <password>";
trace << "LOGIN {username} {password}";
return createCommand(cmd.str(), trace.str());
}

View File

@ -98,7 +98,7 @@ public:
/** Returns the full text of the command, suitable for outputing
* to the tracer.
*
* @return trace text (eg. "LOGIN myusername ***")
* @return trace text (eg. "LOGIN {username} {password}")
*/
virtual const string getTraceText() const;

View File

@ -71,10 +71,16 @@ IMAPConnection::IMAPConnection(shared_ptr <IMAPStore> store, shared_ptr <securit
m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(null),
m_secured(false), m_firstTag(true), m_capabilitiesFetched(false), m_noModSeq(false)
{
static int connectionId = 0;
m_tag = make_shared <IMAPTag>();
if (store->getTracerFactory())
m_tracer = store->getTracerFactory()->create(store, ++connectionId);
m_parser = make_shared <IMAPParser>();
m_parser->setTag(m_tag);
m_parser->setTracer(m_tracer);
}
@ -439,6 +445,9 @@ void IMAPConnection::authenticateSASL()
const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n";
sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length());
if (m_tracer)
m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange");
// Server capabilities may change when logged in
invalidateCapabilities();
}
@ -458,6 +467,9 @@ void IMAPConnection::authenticateSASL()
// Cancel SASL exchange
sendRaw(utility::stringUtils::bytesFromString("*\r\n"), 3);
if (m_tracer)
m_tracer->traceSend("*");
}
catch (...)
{
@ -752,6 +764,14 @@ void IMAPConnection::sendCommand(shared_ptr <IMAPCommand> cmd)
m_socket->send("\r\n");
m_firstTag = false;
if (m_tracer)
{
std::ostringstream oss;
oss << string(*m_tag) << " " << cmd->getText();
m_tracer->traceSend(oss.str());
}
}
@ -816,6 +836,12 @@ void IMAPConnection::setSocket(shared_ptr <socket> sok)
}
shared_ptr <tracer> IMAPConnection::getTracer()
{
return m_tracer;
}
shared_ptr <IMAPTag> IMAPConnection::getTag()
{
return m_tag;

View File

@ -33,6 +33,7 @@
#include "vmime/net/socket.hpp"
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/tracer.hpp"
#include "vmime/net/session.hpp"
#include "vmime/net/connectionInfos.hpp"
@ -105,6 +106,8 @@ public:
shared_ptr <const socket> getSocket() const;
void setSocket(shared_ptr <socket> sok);
shared_ptr <tracer> getTracer();
shared_ptr <IMAPTag> getTag();
bool isMODSEQDisabled() const;
@ -151,6 +154,8 @@ private:
bool m_noModSeq;
shared_ptr <tracer> m_tracer;
void internalDisconnect();

View File

@ -1124,6 +1124,9 @@ messageSet IMAPFolder::addMessage
m_connection->sendRaw(utility::stringUtils::bytesFromString("\r\n"), 2);
if (m_connection->getTracer())
m_connection->getTracer()->traceSendBytes(current);
if (progress)
progress->stop(total);

View File

@ -49,6 +49,7 @@
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/socket.hpp"
#include "vmime/net/tracer.hpp"
#include "vmime/net/imap/IMAPTag.hpp"
@ -281,6 +282,15 @@ public:
m_timeoutHandler = toh;
}
/** Set the tracer currently used by this parser.
*
* @param tr tracer
*/
void setTracer(shared_ptr <tracer> tr)
{
m_tracer = tr;
}
/** Set whether we operate in strict mode (this may not work
* with some servers which are not fully standard-compliant).
*
@ -5687,6 +5697,7 @@ private:
weak_ptr <IMAPTag> m_tag;
weak_ptr <socket> m_socket;
shared_ptr <tracer> m_tracer;
utility::progressListener* m_progress;
@ -5731,6 +5742,13 @@ public:
std::cout << std::endl << "Read line:" << std::endl << line << std::endl;
#endif
if (m_tracer)
{
string::size_type len = line.length();
while (len != 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) --len;
m_tracer->traceReceive(line.substr(0, len));
}
return (line);
}
@ -5860,6 +5878,9 @@ public:
m_progress->progress(len, count);
}
if (m_tracer)
m_tracer->traceReceiveBytes(count);
if (m_progress)
m_progress->stop(count);
}

View File

@ -42,8 +42,8 @@ namespace net {
namespace pop3 {
POP3Command::POP3Command(const string& text)
: m_text(text)
POP3Command::POP3Command(const string& text, const string& traceText)
: m_text(text), m_traceText(traceText)
{
}
@ -109,7 +109,11 @@ shared_ptr <POP3Command> POP3Command::USER(const string& username)
cmd.imbue(std::locale::classic());
cmd << "USER " << username;
return createCommand(cmd.str());
std::ostringstream trace;
trace.imbue(std::locale::classic());
trace << "USER {username}";
return createCommand(cmd.str(), trace.str());
}
@ -120,7 +124,11 @@ shared_ptr <POP3Command> POP3Command::PASS(const string& password)
cmd.imbue(std::locale::classic());
cmd << "PASS " << password;
return createCommand(cmd.str());
std::ostringstream trace;
trace.imbue(std::locale::classic());
trace << "PASS {password}";
return createCommand(cmd.str(), trace.str());
}
@ -215,9 +223,13 @@ shared_ptr <POP3Command> POP3Command::QUIT()
// static
shared_ptr <POP3Command> POP3Command::createCommand(const string& text)
shared_ptr <POP3Command> POP3Command::createCommand
(const string& text, const string& traceText)
{
return shared_ptr <POP3Command>(new POP3Command(text));
if (traceText.empty())
return shared_ptr <POP3Command>(new POP3Command(text, text));
else
return shared_ptr <POP3Command>(new POP3Command(text, traceText));
}
@ -227,9 +239,18 @@ const string POP3Command::getText() const
}
const string POP3Command::getTraceText() const
{
return m_traceText;
}
void POP3Command::send(shared_ptr <POP3Connection> conn)
{
conn->getSocket()->send(m_text + "\r\n");
if (conn->getTracer())
conn->getTracer()->traceSend(m_traceText);
}

View File

@ -76,9 +76,10 @@ public:
/** Creates a new POP3 command with the specified text.
*
* @param text command text
* @param traceText trace text (if empty, command text is used)
* @return a new POP3Command object
*/
static shared_ptr <POP3Command> createCommand(const string& text);
static shared_ptr <POP3Command> createCommand(const string& text, const string& traceText = "");
/** Sends this command over the specified connection.
*
@ -93,14 +94,22 @@ public:
*/
virtual const string getText() const;
/** Returns the full text of the command, suitable for outputing
* to the tracer.
*
* @return trace text (eg. "USER myusername")
*/
virtual const string getTraceText() const;
protected:
POP3Command(const string& text);
POP3Command(const string& text, const string& traceText);
POP3Command(const POP3Command&);
private:
string m_text;
string m_traceText;
};

View File

@ -67,6 +67,10 @@ POP3Connection::POP3Connection(shared_ptr <POP3Store> store, shared_ptr <securit
: m_store(store), m_auth(auth), m_socket(null), m_timeoutHandler(null),
m_authenticated(false), m_secured(false), m_capabilitiesFetched(false)
{
static int connectionId = 0;
if (store->getTracerFactory())
m_tracer = store->getTracerFactory()->create(store, ++connectionId);
}
@ -500,7 +504,11 @@ void POP3Connection::authenticateSASL()
(challenge, challengeLen, &resp, &respLen);
// Send response
m_socket->send(saslContext->encodeB64(resp, respLen) + "\r\n");
const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n";
m_socket->sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length());
if (m_tracer)
m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange");
}
catch (exceptions::sasl_exception& e)
{
@ -518,6 +526,9 @@ void POP3Connection::authenticateSASL()
// Cancel SASL exchange
m_socket->send("*\r\n");
if (m_tracer)
m_tracer->traceSend("*");
}
catch (...)
{
@ -677,6 +688,12 @@ shared_ptr <socket> POP3Connection::getSocket()
}
shared_ptr <tracer> POP3Connection::getTracer()
{
return m_tracer;
}
shared_ptr <timeoutHandler> POP3Connection::getTimeoutHandler()
{
return m_timeoutHandler;

View File

@ -37,6 +37,7 @@
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/session.hpp"
#include "vmime/net/connectionInfos.hpp"
#include "vmime/net/tracer.hpp"
#include "vmime/net/pop3/POP3Command.hpp"
#include "vmime/net/pop3/POP3Response.hpp"
@ -80,6 +81,7 @@ public:
virtual shared_ptr <timeoutHandler> getTimeoutHandler();
virtual shared_ptr <security::authenticator> getAuthenticator();
virtual shared_ptr <session> getSession();
virtual shared_ptr <tracer> getTracer();
private:
@ -104,6 +106,7 @@ private:
shared_ptr <security::authenticator> m_auth;
shared_ptr <socket> m_socket;
shared_ptr <timeoutHandler> m_timeoutHandler;
shared_ptr <tracer> m_tracer;
bool m_authenticated;
bool m_secured;

View File

@ -149,7 +149,7 @@ void POP3Message::extract
try
{
POP3Response::readLargeResponse
(store->getConnection(), os, progress, m_size);
(store->getConnection(), os, progress, m_size == -1 ? 0 : m_size);
}
catch (exceptions::command_error& e)
{

View File

@ -46,8 +46,8 @@ namespace net {
namespace pop3 {
POP3Response::POP3Response(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh)
: m_socket(sok), m_timeoutHandler(toh)
POP3Response::POP3Response(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, shared_ptr <tracer> tracer)
: m_socket(sok), m_timeoutHandler(toh), m_tracer(tracer)
{
}
@ -56,7 +56,7 @@ POP3Response::POP3Response(shared_ptr <socket> sok, shared_ptr <timeoutHandler>
shared_ptr <POP3Response> POP3Response::readResponse(shared_ptr <POP3Connection> conn)
{
shared_ptr <POP3Response> resp = shared_ptr <POP3Response>
(new POP3Response(conn->getSocket(), conn->getTimeoutHandler()));
(new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()));
string buffer;
resp->readResponseImpl(buffer, /* multiLine */ false);
@ -65,6 +65,9 @@ shared_ptr <POP3Response> POP3Response::readResponse(shared_ptr <POP3Connection>
resp->m_code = getResponseCode(buffer);
stripResponseCode(buffer, resp->m_text);
if (resp->m_tracer)
resp->m_tracer->traceReceive(buffer);
return resp;
}
@ -73,7 +76,7 @@ shared_ptr <POP3Response> POP3Response::readResponse(shared_ptr <POP3Connection>
shared_ptr <POP3Response> POP3Response::readMultilineResponse(shared_ptr <POP3Connection> conn)
{
shared_ptr <POP3Response> resp = shared_ptr <POP3Response>
(new POP3Response(conn->getSocket(), conn->getTimeoutHandler()));
(new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()));
string buffer;
resp->readResponseImpl(buffer, /* multiLine */ true);
@ -88,8 +91,20 @@ shared_ptr <POP3Response> POP3Response::readMultilineResponse(shared_ptr <POP3Co
std::istringstream iss(nextLines);
string line;
if (resp->m_tracer)
resp->m_tracer->traceReceive(firstLine);
while (std::getline(iss, line, '\n'))
resp->m_lines.push_back(utility::stringUtils::trim(line));
{
line = utility::stringUtils::trim(line);
resp->m_lines.push_back(line);
if (resp->m_tracer)
resp->m_tracer->traceReceive(line);
}
if (resp->m_tracer)
resp->m_tracer->traceReceive(".");
return resp;
}
@ -101,15 +116,22 @@ shared_ptr <POP3Response> POP3Response::readLargeResponse
utility::progressListener* progress, const size_t predictedSize)
{
shared_ptr <POP3Response> resp = shared_ptr <POP3Response>
(new POP3Response(conn->getSocket(), conn->getTimeoutHandler()));
(new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()));
string firstLine;
resp->readResponseImpl(firstLine, os, progress, predictedSize);
const size_t length = resp->readResponseImpl(firstLine, os, progress, predictedSize);
resp->m_firstLine = firstLine;
resp->m_code = getResponseCode(firstLine);
stripResponseCode(firstLine, resp->m_text);
if (resp->m_tracer)
{
resp->m_tracer->traceReceive(firstLine);
resp->m_tracer->traceReceiveBytes(length - firstLine.length());
resp->m_tracer->traceReceive(".");
}
return resp;
}
@ -230,7 +252,7 @@ void POP3Response::readResponseImpl(string& buffer, const bool multiLine)
}
void POP3Response::readResponseImpl
size_t POP3Response::readResponseImpl
(string& firstLine, utility::outputStream& os,
utility::progressListener* progress, const size_t predictedSize)
{
@ -327,6 +349,8 @@ void POP3Response::readResponseImpl
if (progress)
progress->stop(total);
return current;
}

View File

@ -38,6 +38,7 @@
#include "vmime/utility/progressListener.hpp"
#include "vmime/net/socket.hpp"
#include "vmime/net/tracer.hpp"
namespace vmime {
@ -143,10 +144,10 @@ public:
private:
POP3Response(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh);
POP3Response(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, shared_ptr <tracer> tracer);
void readResponseImpl(string& buffer, const bool multiLine);
void readResponseImpl
size_t readResponseImpl
(string& firstLine, utility::outputStream& os,
utility::progressListener* progress, const size_t predictedSize);
@ -163,6 +164,7 @@ private:
shared_ptr <socket> m_socket;
shared_ptr <timeoutHandler> m_timeoutHandler;
shared_ptr <tracer> m_tracer;
string m_firstLine;
ResponseCode m_code;

View File

@ -136,6 +136,18 @@ shared_ptr <socketFactory> service::getSocketFactory()
}
void service::setTracerFactory(shared_ptr <tracerFactory> tf)
{
m_tracerFactory = tf;
}
shared_ptr <tracerFactory> service::getTracerFactory()
{
return m_tracerFactory;
}
void service::setTimeoutHandlerFactory(shared_ptr <timeoutHandlerFactory> thf)
{
m_toHandlerFactory = thf;

View File

@ -40,6 +40,7 @@
#include "vmime/net/socket.hpp"
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/tracer.hpp"
#if VMIME_HAVE_TLS_SUPPORT
#include "vmime/security/cert/certificateVerifier.hpp"
@ -183,6 +184,11 @@ public:
*/
shared_ptr <timeoutHandlerFactory> getTimeoutHandlerFactory();
void setTracerFactory(shared_ptr <tracerFactory> tf);
shared_ptr <tracerFactory> getTracerFactory();
/** Set a property for this service (service prefix is added automatically).
*
* WARNING: this sets the property on the session object, so all service
@ -219,8 +225,8 @@ private:
#endif // VMIME_HAVE_TLS_SUPPORT
shared_ptr <socketFactory> m_socketFactory;
shared_ptr <timeoutHandlerFactory> m_toHandlerFactory;
shared_ptr <tracerFactory> m_tracerFactory;
};

View File

@ -73,6 +73,9 @@ void SMTPChunkingOutputStreamAdapter::sendChunk
m_progress->progress(m_totalSent, m_totalSize);
}
if (m_connection->getTracer())
m_connection->getTracer()->traceSendBytes(count);
// If PIPELINING is not supported, read one response for this BDAT command
if (!m_connection->hasExtension("PIPELINING"))
{
@ -143,6 +146,9 @@ void SMTPChunkingOutputStreamAdapter::flush()
if (m_progress)
m_progress->stop(m_totalSize);
if (m_connection->getTracer())
m_connection->getTracer()->traceSendBytes(m_bufferSize);
}

View File

@ -30,6 +30,7 @@
#include "vmime/net/smtp/SMTPCommand.hpp"
#include "vmime/net/socket.hpp"
#include "vmime/net/tracer.hpp"
#include "vmime/mailbox.hpp"
#include "vmime/utility/outputStreamAdapter.hpp"
@ -40,8 +41,8 @@ namespace net {
namespace smtp {
SMTPCommand::SMTPCommand(const string& text)
: m_text(text)
SMTPCommand::SMTPCommand(const string& text, const string& traceText)
: m_text(text), m_traceText(traceText)
{
}
@ -199,9 +200,12 @@ shared_ptr <SMTPCommand> SMTPCommand::QUIT()
// static
shared_ptr <SMTPCommand> SMTPCommand::createCommand(const string& text)
shared_ptr <SMTPCommand> SMTPCommand::createCommand(const string& text, const string& traceText)
{
return shared_ptr <SMTPCommand>(new SMTPCommand(text));
if (traceText.empty())
return shared_ptr <SMTPCommand>(new SMTPCommand(text, text));
else
return shared_ptr <SMTPCommand>(new SMTPCommand(text, traceText));
}
@ -211,9 +215,18 @@ const string SMTPCommand::getText() const
}
void SMTPCommand::writeToSocket(shared_ptr <socket> sok)
const string SMTPCommand::getTraceText() const
{
return m_traceText;
}
void SMTPCommand::writeToSocket(shared_ptr <socket> sok, shared_ptr <tracer> tr)
{
sok->send(m_text + "\r\n");
if (tr)
tr->traceSend(m_traceText);
}

View File

@ -46,6 +46,7 @@ namespace net {
class socket;
class timeoutHandler;
class tracer;
namespace smtp {
@ -76,13 +77,14 @@ public:
* @param text command text
* @return a new SMTPCommand object
*/
static shared_ptr <SMTPCommand> createCommand(const string& text);
static shared_ptr <SMTPCommand> createCommand(const string& text, const string& traceText = "");
/** Sends this command to the specified socket.
*
* @param sok socket to which the command will be written
* @param tr tracer
*/
virtual void writeToSocket(shared_ptr <socket> sok);
virtual void writeToSocket(shared_ptr <socket> sok, shared_ptr <tracer> tr);
/** Returns the full text of the command, including command name
* and parameters (if any).
@ -91,14 +93,22 @@ public:
*/
virtual const string getText() const;
/** Returns the full text of the command, suitable for outputing
* to the tracer.
*
* @return trace text (eg. "LOGIN myusername ***")
*/
virtual const string getTraceText() const;
protected:
SMTPCommand(const string& text);
SMTPCommand(const string& text, const string& traceText);
SMTPCommand(const SMTPCommand&);
private:
string m_text;
string m_traceText;
};

View File

@ -42,7 +42,7 @@ namespace smtp {
SMTPCommandSet::SMTPCommandSet(const bool pipeline)
: SMTPCommand(""), m_pipeline(pipeline),
: SMTPCommand("", ""), m_pipeline(pipeline),
m_started(false), m_lastCommandSent()
{
}
@ -67,7 +67,7 @@ void SMTPCommandSet::addCommand(shared_ptr <SMTPCommand> cmd)
}
void SMTPCommandSet::writeToSocket(shared_ptr <socket> sok)
void SMTPCommandSet::writeToSocket(shared_ptr <socket> sok, shared_ptr <tracer> tr)
{
if (m_pipeline)
{
@ -78,7 +78,7 @@ void SMTPCommandSet::writeToSocket(shared_ptr <socket> sok)
it != m_commands.end() ; ++it)
{
shared_ptr <SMTPCommand> cmd = *it;
cmd->writeToSocket(sok);
cmd->writeToSocket(sok, tr);
}
}
@ -99,7 +99,7 @@ void SMTPCommandSet::writeToSocket(shared_ptr <socket> sok)
shared_ptr <SMTPCommand> cmd = m_commands.front();
m_commands.pop_front();
cmd->writeToSocket(sok);
cmd->writeToSocket(sok, tr);
m_lastCommandSent = cmd;
}
@ -124,6 +124,21 @@ const string SMTPCommandSet::getText() const
}
const string SMTPCommandSet::getTraceText() const
{
std::ostringstream cmd;
cmd.imbue(std::locale::classic());
for (std::list <shared_ptr <SMTPCommand> >::const_iterator it = m_commands.begin() ;
it != m_commands.end() ; ++it)
{
cmd << (*it)->getTraceText() << "\r\n";
}
return cmd.str();
}
bool SMTPCommandSet::isFinished() const
{
return (m_pipeline && m_started) || (m_commands.size() == 0 && m_started);

View File

@ -78,9 +78,10 @@ public:
shared_ptr <SMTPCommand> getLastCommandSent() const;
void writeToSocket(shared_ptr <socket> sok);
void writeToSocket(shared_ptr <socket> sok, shared_ptr <tracer> tr);
const string getText() const;
const string getTraceText() const;
private:

View File

@ -68,6 +68,10 @@ SMTPConnection::SMTPConnection(shared_ptr <SMTPTransport> transport, shared_ptr
: m_transport(transport), m_auth(auth), m_socket(null), m_timeoutHandler(null),
m_authenticated(false), m_secured(false), m_extendedSMTP(false)
{
static int connectionId = 0;
if (transport->getTracerFactory())
m_tracer = transport->getTracerFactory()->create(transport, ++connectionId);
}
@ -416,7 +420,11 @@ void SMTPConnection::authenticateSASL()
(challenge, challengeLen, &resp, &respLen);
// Send response
m_socket->send(saslContext->encodeB64(resp, respLen) + "\r\n");
const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n";
m_socket->sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length());
if (m_tracer)
m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange");
}
catch (exceptions::sasl_exception& e)
{
@ -434,6 +442,9 @@ void SMTPConnection::authenticateSASL()
// Cancel SASL exchange
m_socket->send("*\r\n");
if (m_tracer)
m_tracer->traceSend("*");
}
catch (...)
{
@ -557,14 +568,14 @@ void SMTPConnection::internalDisconnect()
void SMTPConnection::sendRequest(shared_ptr <SMTPCommand> cmd)
{
cmd->writeToSocket(m_socket);
cmd->writeToSocket(m_socket, m_tracer);
}
shared_ptr <SMTPResponse> SMTPConnection::readResponse()
{
shared_ptr <SMTPResponse> resp = SMTPResponse::readResponse
(m_socket, m_timeoutHandler, m_responseState);
(m_tracer, m_socket, m_timeoutHandler, m_responseState);
m_responseState = resp->getCurrentState();
@ -608,6 +619,12 @@ shared_ptr <socket> SMTPConnection::getSocket()
}
shared_ptr <tracer> SMTPConnection::getTracer()
{
return m_tracer;
}
shared_ptr <timeoutHandler> SMTPConnection::getTimeoutHandler()
{
return m_timeoutHandler;

View File

@ -37,6 +37,7 @@
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/session.hpp"
#include "vmime/net/connectionInfos.hpp"
#include "vmime/net/tracer.hpp"
#include "vmime/net/smtp/SMTPCommand.hpp"
#include "vmime/net/smtp/SMTPResponse.hpp"
@ -80,6 +81,7 @@ public:
virtual shared_ptr <timeoutHandler> getTimeoutHandler();
virtual shared_ptr <security::authenticator> getAuthenticator();
virtual shared_ptr <session> getSession();
virtual shared_ptr <tracer> getTracer();
void sendRequest(shared_ptr <SMTPCommand> cmd);
shared_ptr <SMTPResponse> readResponse();
@ -106,6 +108,7 @@ private:
shared_ptr <security::authenticator> m_auth;
shared_ptr <socket> m_socket;
shared_ptr <timeoutHandler> m_timeoutHandler;
shared_ptr <tracer> m_tracer;
SMTPResponse::state m_responseState;

View File

@ -34,6 +34,7 @@
#include "vmime/net/socket.hpp"
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/tracer.hpp"
#include <cctype>
@ -43,8 +44,10 @@ namespace net {
namespace smtp {
SMTPResponse::SMTPResponse(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, const state& st)
: m_socket(sok), m_timeoutHandler(toh),
SMTPResponse::SMTPResponse
(shared_ptr <tracer> tr, shared_ptr <socket> sok,
shared_ptr <timeoutHandler> toh, const state& st)
: m_socket(sok), m_timeoutHandler(toh), m_tracer(tr),
m_responseBuffer(st.responseBuffer), m_responseContinues(false)
{
}
@ -95,9 +98,11 @@ const string SMTPResponse::getText() const
// static
shared_ptr <SMTPResponse> SMTPResponse::readResponse
(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, const state& st)
(shared_ptr <tracer> tr, shared_ptr <socket> sok,
shared_ptr <timeoutHandler> toh, const state& st)
{
shared_ptr <SMTPResponse> resp = shared_ptr <SMTPResponse>(new SMTPResponse(sok, toh, st));
shared_ptr <SMTPResponse> resp =
shared_ptr <SMTPResponse>(new SMTPResponse(tr, sok, toh, st));
resp->readResponse();
@ -142,6 +147,9 @@ const string SMTPResponse::readResponseLine()
currentBuffer.erase(currentBuffer.begin(), currentBuffer.begin() + lineEnd + 1);
m_responseBuffer = currentBuffer;
if (m_tracer)
m_tracer->traceReceive(line);
return line;
}

View File

@ -41,6 +41,7 @@ namespace net {
class socket;
class timeoutHandler;
class tracer;
namespace smtp {
@ -95,6 +96,7 @@ public:
/** Receive and parse a new SMTP response from the
* specified socket.
*
* @param tr tracer
* @param sok socket from which to read
* @param toh time-out handler
* @param st previous state of response parser for the specified socket
@ -102,7 +104,9 @@ public:
* @throws exceptions::operation_timed_out if no data
* has been received within the granted time
*/
static shared_ptr <SMTPResponse> readResponse(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, const state& st);
static shared_ptr <SMTPResponse> readResponse
(shared_ptr <tracer> tr, shared_ptr <socket> sok,
shared_ptr <timeoutHandler> toh, const state& st);
/** Return the SMTP response code.
*
@ -150,7 +154,7 @@ public:
private:
SMTPResponse(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, const state& st);
SMTPResponse(shared_ptr <tracer> tr, shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, const state& st);
SMTPResponse(const SMTPResponse&);
void readResponse();
@ -166,6 +170,7 @@ private:
shared_ptr <socket> m_socket;
shared_ptr <timeoutHandler> m_timeoutHandler;
shared_ptr <tracer> m_tracer;
string m_responseBuffer;
bool m_responseContinues;

View File

@ -210,7 +210,7 @@ void SMTPTransport::sendEnvelope
// Read response for "RSET" command
if (needReset)
{
commands->writeToSocket(m_connection->getSocket());
commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer());
resp = m_connection->readResponse();
@ -227,7 +227,7 @@ void SMTPTransport::sendEnvelope
}
// Read response for "MAIL" command
commands->writeToSocket(m_connection->getSocket());
commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer());
if ((resp = m_connection->readResponse())->getCode() != 250)
{
@ -257,7 +257,7 @@ void SMTPTransport::sendEnvelope
// Read responses for "RCPT TO" commands
for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i)
{
commands->writeToSocket(m_connection->getSocket());
commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer());
resp = m_connection->readResponse();
@ -291,7 +291,7 @@ void SMTPTransport::sendEnvelope
// Read response for "DATA" command
if (sendDATACommand)
{
commands->writeToSocket(m_connection->getSocket());
commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer());
if ((resp = m_connection->readResponse())->getCode() != 354)
{
@ -326,6 +326,12 @@ void SMTPTransport::send
// Send end-of-data delimiter
m_connection->getSocket()->send("\r\n.\r\n");
if (m_connection->getTracer())
{
m_connection->getTracer()->traceSendBytes(size);
m_connection->getTracer()->traceSend(".");
}
shared_ptr <SMTPResponse> resp;
if ((resp = m_connection->readResponse())->getCode() != 250)

72
src/vmime/net/tracer.cpp Normal file
View File

@ -0,0 +1,72 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2014 Vincent Richard <vincent@vmime.org>
//
// 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
#include "tracer.hpp"
#include <sstream>
namespace vmime {
namespace net {
void tracer::traceReceiveBytes(const size_t count, const string& state)
{
std::ostringstream oss;
oss << "{...";
if (!state.empty())
oss << state << ": ";
oss << count << " bytes of data...}";
traceReceive(oss.str());
}
void tracer::traceSendBytes(const size_t count, const string& state)
{
std::ostringstream oss;
oss << "{...";
if (!state.empty())
oss << state << ": ";
oss << count << " bytes of data...}";
traceSend(oss.str());
}
} // net
} // vmime
#endif // VMIME_HAVE_MESSAGING_FEATURES

109
src/vmime/net/tracer.hpp Normal file
View File

@ -0,0 +1,109 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2014 Vincent Richard <vincent@vmime.org>
//
// 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_TRACER_HPP_INCLUDED
#define VMIME_NET_TRACER_HPP_INCLUDED
#include "vmime/config.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES
#include "vmime/base.hpp"
namespace vmime {
namespace net {
class service;
/** Base class for an object used to trace network communication
* between the client and the server.
*/
class VMIME_EXPORT tracer : public object
{
public:
virtual ~tracer() { }
/** Trace raw bytes which have been received.
*
* @param count number of bytes
* @param state protocol state (eg. "SASL exchange"), or empty
*/
virtual void traceReceiveBytes(const size_t count, const string& state = "");
/** Trace raw bytes which have been sent.
*
* @param count number of bytes
* @param state protocol state (eg. "SASL exchange"), or empty
*/
virtual void traceSendBytes(const size_t count, const string& state = "");
/** Trace a command line which has been sent.
*
* @param line command line
*/
virtual void traceSend(const string& line) = 0;
/** Trace a response line which has been received.
*
* @param line response line
*/
virtual void traceReceive(const string& line) = 0;
};
/** A class to create 'tracer' objects.
*/
class VMIME_EXPORT tracerFactory : public object
{
public:
virtual ~tracerFactory() { }
/** Creates a tracer for the specified service.
*
* @param serv messaging service
* @param connectionId an identifier for the connection to distinguate between
* different connections used by a service
* @return a new tracer
*/
virtual shared_ptr <tracer> create(shared_ptr <service> serv, const int connectionId) = 0;
};
} // net
} // vmime
#endif // VMIME_HAVE_MESSAGING_FEATURES
#endif // VMIME_NET_TRACER_HPP_INCLUDED

View File

@ -69,15 +69,16 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandSetTest)
VASSERT_NO_THROW("No throw 2", cset->addCommand(SMTPCommand::createCommand("MY_COMMAND2")));
VASSERT_EQ("Text", "MY_COMMAND1\r\nMY_COMMAND2\r\n", cset->getText());
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> sok = vmime::make_shared <testSocket>();
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
VASSERT_FALSE("Finished", cset->isFinished());
// Can't add commands when writing to socket has started
VASSERT_THROW("Throw", cset->addCommand(SMTPCommand::createCommand("MY_COMMAND3")), std::runtime_error);
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
VASSERT_TRUE("Finished", cset->isFinished());
}
@ -90,10 +91,11 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandSetTest)
VASSERT_NO_THROW("No throw 2", cset->addCommand(SMTPCommand::createCommand("MY_COMMAND2")));
VASSERT_EQ("Text", "MY_COMMAND1\r\nMY_COMMAND2\r\n", cset->getText());
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> sok = vmime::make_shared <testSocket>();
vmime::string response;
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
VASSERT_TRUE("Finished", cset->isFinished());
sok->localReceive(response);
@ -110,15 +112,16 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandSetTest)
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND1"));
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND2"));
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> sok = vmime::make_shared <testSocket>();
vmime::string response;
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
sok->localReceive(response);
VASSERT_EQ("Receive cmd 1", "MY_COMMAND1\r\n", response);
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
sok->localReceive(response);
VASSERT_EQ("Receive cmd 2", "MY_COMMAND2\r\n", response);
@ -131,10 +134,11 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandSetTest)
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND1"));
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND2"));
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> sok = vmime::make_shared <testSocket>();
vmime::string response;
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
sok->localReceive(response);
VASSERT_EQ("Receive cmds", "MY_COMMAND1\r\nMY_COMMAND2\r\n", response);
@ -147,12 +151,13 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandSetTest)
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND1"));
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND2"));
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> sok = vmime::make_shared <testSocket>();
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
VASSERT_EQ("Cmd 1", "MY_COMMAND1", cset->getLastCommandSent()->getText());
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
VASSERT_EQ("Cmd 2", "MY_COMMAND2", cset->getLastCommandSent()->getText());
}
@ -163,12 +168,13 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandSetTest)
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND1"));
cset->addCommand(SMTPCommand::createCommand("MY_COMMAND2"));
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> sok = vmime::make_shared <testSocket>();
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
VASSERT_EQ("Cmd 1", "MY_COMMAND1", cset->getLastCommandSent()->getText());
cset->writeToSocket(sok);
cset->writeToSocket(sok, tracer);
VASSERT_EQ("Cmd 2", "MY_COMMAND2", cset->getLastCommandSent()->getText());
}

View File

@ -231,8 +231,10 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandTest)
{
vmime::shared_ptr <SMTPCommand> cmd = SMTPCommand::createCommand("MY_COMMAND param1 param2");
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> sok = vmime::make_shared <testSocket>();
cmd->writeToSocket(sok);
cmd->writeToSocket(sok, tracer);
vmime::string response;
sok->localReceive(response);

View File

@ -43,6 +43,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testSingleLineResponse()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>();
@ -52,7 +53,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 123, resp->getCode());
VASSERT_EQ("Lines", 1, resp->getLineCount());
@ -61,6 +62,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testSingleLineResponseLF()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>();
@ -70,7 +72,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 123, resp->getCode());
VASSERT_EQ("Lines", 1, resp->getLineCount());
@ -79,6 +81,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testMultiLineResponse()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>();
@ -92,7 +95,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 123, resp->getCode());
VASSERT_EQ("Lines", 2, resp->getLineCount());
@ -107,6 +110,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testMultiLineResponseDifferentCode()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>();
@ -120,7 +124,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 0, resp->getCode());
VASSERT_EQ("Lines", 2, resp->getLineCount());
@ -135,6 +139,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testIncompleteMultiLineResponse()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>(1);
@ -149,12 +154,13 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
VASSERT_THROW("Incomplete response",
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState),
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState),
vmime::exceptions::operation_timed_out);
}
void testNoResponseText()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>(1);
@ -167,7 +173,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 250, resp->getCode());
VASSERT_EQ("Lines", 1, resp->getLineCount());
@ -176,6 +182,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testEnhancedStatusCode()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>();
@ -185,7 +192,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 250, resp->getCode());
VASSERT_EQ("Lines", 1, resp->getLineCount());
@ -197,6 +204,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testNoEnhancedStatusCode()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>();
@ -206,7 +214,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 354, resp->getCode());
VASSERT_EQ("Lines", 1, resp->getLineCount());
@ -218,6 +226,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
void testInvalidEnhancedStatusCode()
{
vmime::shared_ptr <vmime::net::tracer> tracer;
vmime::shared_ptr <testSocket> socket = vmime::make_shared <testSocket>();
vmime::shared_ptr <vmime::net::timeoutHandler> toh =
vmime::make_shared <testTimeoutHandler>();
@ -227,7 +236,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPResponseTest)
vmime::net::smtp::SMTPResponse::state responseState;
vmime::shared_ptr <vmime::net::smtp::SMTPResponse> resp =
vmime::net::smtp::SMTPResponse::readResponse(socket, toh, responseState);
vmime::net::smtp::SMTPResponse::readResponse(tracer, socket, toh, responseState);
VASSERT_EQ("Code", 250, resp->getCode());
VASSERT_EQ("Lines", 1, resp->getLineCount());