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