SMTP Command Pipelining (RFC-2920).
This commit is contained in:
parent
f4c42cc0cf
commit
6ae75bc971
@ -41,9 +41,9 @@ namespace net {
|
||||
namespace smtp {
|
||||
|
||||
|
||||
SMTPResponse::SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh)
|
||||
SMTPResponse::SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh, const state& st)
|
||||
: m_socket(sok), m_timeoutHandler(toh),
|
||||
m_responseContinues(false)
|
||||
m_responseBuffer(st.responseBuffer), m_responseContinues(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -87,9 +87,9 @@ const string SMTPResponse::getText() const
|
||||
|
||||
// static
|
||||
ref <SMTPResponse> SMTPResponse::readResponse
|
||||
(ref <socket> sok, ref <timeoutHandler> toh)
|
||||
(ref <socket> sok, ref <timeoutHandler> toh, const state& st)
|
||||
{
|
||||
ref <SMTPResponse> resp = vmime::create <SMTPResponse>(sok, toh);
|
||||
ref <SMTPResponse> resp = vmime::create <SMTPResponse>(sok, toh, st);
|
||||
|
||||
resp->readResponse();
|
||||
|
||||
@ -218,6 +218,15 @@ const SMTPResponse::responseLine SMTPResponse::getLastLine() const
|
||||
}
|
||||
|
||||
|
||||
const SMTPResponse::state SMTPResponse::getCurrentState() const
|
||||
{
|
||||
state st;
|
||||
st.responseBuffer = m_responseBuffer;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SMTPResponse::responseLine
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace smtp {
|
||||
SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth, const bool secured)
|
||||
: transport(sess, getInfosInstance(), auth), m_socket(NULL),
|
||||
m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL),
|
||||
m_isSMTPS(secured), m_secured(false)
|
||||
m_isSMTPS(secured), m_secured(false), m_pipelineStarted(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -571,9 +571,58 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
|
||||
else if (expeditor.isEmpty())
|
||||
throw exceptions::no_expeditor();
|
||||
|
||||
// Emit the "MAIL" command
|
||||
|
||||
ref <SMTPResponse> resp;
|
||||
|
||||
if (m_extensions.find("PIPELINING") != m_extensions.end())
|
||||
{
|
||||
beginCommandPipeline();
|
||||
|
||||
// Emit the "MAIL" command
|
||||
sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">");
|
||||
|
||||
// Emit a "RCPT TO" command for each recipient
|
||||
for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)
|
||||
{
|
||||
const mailbox& mbox = *recipients.getMailboxAt(i);
|
||||
|
||||
sendRequest("RCPT TO:<" + mbox.getEmail() + ">");
|
||||
}
|
||||
|
||||
// Prepare sending of message data
|
||||
sendRequest("DATA");
|
||||
|
||||
endCommandPipeline();
|
||||
|
||||
// Read response for "MAIL" command
|
||||
if ((resp = readResponse())->getCode() != 250)
|
||||
{
|
||||
internalDisconnect();
|
||||
throw exceptions::command_error("MAIL", resp->getText());
|
||||
}
|
||||
|
||||
// Read responses for "RCPT TO" commands
|
||||
for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)
|
||||
{
|
||||
const mailbox& mbox = *recipients.getMailboxAt(i);
|
||||
|
||||
if ((resp = readResponse())->getCode() != 250)
|
||||
{
|
||||
internalDisconnect();
|
||||
throw exceptions::command_error("RCPT TO", resp->getText(), mbox.getEmail());
|
||||
}
|
||||
}
|
||||
|
||||
// Read response for "DATA" command
|
||||
if ((resp = readResponse())->getCode() != 354)
|
||||
{
|
||||
internalDisconnect();
|
||||
throw exceptions::command_error("DATA", resp->getText());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emit the "MAIL" command
|
||||
sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">");
|
||||
|
||||
if ((resp = readResponse())->getCode() != 250)
|
||||
@ -596,7 +645,7 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message data
|
||||
// Prepare sending of message data
|
||||
sendRequest("DATA");
|
||||
|
||||
if ((resp = readResponse())->getCode() != 354)
|
||||
@ -604,7 +653,9 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
|
||||
internalDisconnect();
|
||||
throw exceptions::command_error("DATA", resp->getText());
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message data
|
||||
// Stream copy with "\n." to "\n.." transformation
|
||||
utility::outputStreamSocketAdapter sos(*m_socket);
|
||||
utility::dotFilteredOutputStream fos(sos);
|
||||
@ -624,18 +675,52 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
|
||||
}
|
||||
|
||||
|
||||
void SMTPTransport::beginCommandPipeline()
|
||||
{
|
||||
m_pipeline.clear();
|
||||
m_pipelineStarted = true;
|
||||
}
|
||||
|
||||
|
||||
void SMTPTransport::endCommandPipeline()
|
||||
{
|
||||
if (m_pipelineStarted)
|
||||
{
|
||||
m_socket->send(m_pipeline.str());
|
||||
|
||||
m_pipeline.clear();
|
||||
m_pipelineStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SMTPTransport::sendRequest(const string& buffer, const bool end)
|
||||
{
|
||||
if (m_pipelineStarted)
|
||||
{
|
||||
m_pipeline << buffer;
|
||||
|
||||
if (end)
|
||||
m_pipeline << "\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (end)
|
||||
m_socket->send(buffer + "\r\n");
|
||||
else
|
||||
m_socket->send(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ref <SMTPResponse> SMTPTransport::readResponse()
|
||||
{
|
||||
return SMTPResponse::readResponse(m_socket, m_timeoutHandler);
|
||||
ref <SMTPResponse> resp = SMTPResponse::readResponse
|
||||
(m_socket, m_timeoutHandler, m_responseState);
|
||||
|
||||
m_responseState = resp->getCurrentState();
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,6 +54,12 @@ class SMTPResponse : public object
|
||||
|
||||
public:
|
||||
|
||||
/** Current state of response parser. */
|
||||
struct state
|
||||
{
|
||||
string responseBuffer;
|
||||
};
|
||||
|
||||
/** An element of a SMTP response. */
|
||||
class responseLine
|
||||
{
|
||||
@ -78,11 +84,12 @@ public:
|
||||
*
|
||||
* @param sok socket from which to read
|
||||
* @param toh time-out handler
|
||||
* @param st previous state of response parser for the specified socket
|
||||
* @return SMTP response
|
||||
* @throws exceptions::operation_timed_out if no data
|
||||
* has been received within the granted time
|
||||
*/
|
||||
static ref <SMTPResponse> readResponse(ref <socket> sok, ref <timeoutHandler> toh);
|
||||
static ref <SMTPResponse> readResponse(ref <socket> sok, ref <timeoutHandler> toh, const state& st);
|
||||
|
||||
/** Return the SMTP response code.
|
||||
*
|
||||
@ -116,9 +123,15 @@ public:
|
||||
*/
|
||||
const responseLine getLastLine() const;
|
||||
|
||||
/** Returns the current state of the response parser.
|
||||
*
|
||||
* @return current parser state
|
||||
*/
|
||||
const state getCurrentState() const;
|
||||
|
||||
private:
|
||||
|
||||
SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh);
|
||||
SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh, const state& st);
|
||||
SMTPResponse(const SMTPResponse&);
|
||||
|
||||
void readResponse();
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "vmime/net/timeoutHandler.hpp"
|
||||
|
||||
#include "vmime/net/smtp/SMTPServiceInfos.hpp"
|
||||
#include "vmime/net/smtp/SMTPResponse.hpp"
|
||||
|
||||
|
||||
namespace vmime {
|
||||
@ -43,9 +44,6 @@ namespace net {
|
||||
namespace smtp {
|
||||
|
||||
|
||||
class SMTPResponse;
|
||||
|
||||
|
||||
/** SMTP transport service.
|
||||
*/
|
||||
|
||||
@ -102,6 +100,15 @@ private:
|
||||
bool m_secured;
|
||||
ref <connectionInfos> m_cntInfos;
|
||||
|
||||
SMTPResponse::state m_responseState;
|
||||
|
||||
// Pipelining
|
||||
std::ostringstream m_pipeline;
|
||||
bool m_pipelineStarted;
|
||||
|
||||
void beginCommandPipeline();
|
||||
void endCommandPipeline();
|
||||
|
||||
|
||||
// Service infos
|
||||
static SMTPServiceInfos sm_infos;
|
||||
|
Loading…
Reference in New Issue
Block a user