From 6ae75bc9714d8a5cae48b7a2dfe435fd2a56a6da Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 11 Nov 2012 21:55:44 +0100 Subject: SMTP Command Pipelining (RFC-2920). --- src/net/smtp/SMTPResponse.cpp | 17 ++++-- src/net/smtp/SMTPTransport.cpp | 133 +++++++++++++++++++++++++++++++++-------- 2 files changed, 122 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/net/smtp/SMTPResponse.cpp b/src/net/smtp/SMTPResponse.cpp index e1ac2af6..28a1ea87 100644 --- a/src/net/smtp/SMTPResponse.cpp +++ b/src/net/smtp/SMTPResponse.cpp @@ -41,9 +41,9 @@ namespace net { namespace smtp { -SMTPResponse::SMTPResponse(ref sok, ref toh) +SMTPResponse::SMTPResponse(ref sok, ref 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::readResponse - (ref sok, ref toh) + (ref sok, ref toh, const state& st) { - ref resp = vmime::create (sok, toh); + ref resp = vmime::create (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 diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp index e658ecb7..eb29e65d 100644 --- a/src/net/smtp/SMTPTransport.cpp +++ b/src/net/smtp/SMTPTransport.cpp @@ -68,7 +68,7 @@ namespace smtp { SMTPTransport::SMTPTransport(ref sess, ref 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,40 +571,91 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients else if (expeditor.isEmpty()) throw exceptions::no_expeditor(); - // Emit the "MAIL" command - ref resp; - sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">"); + ref resp; - if ((resp = readResponse())->getCode() != 250) + if (m_extensions.find("PIPELINING") != m_extensions.end()) { - internalDisconnect(); - throw exceptions::command_error("MAIL", resp->getText()); - } + beginCommandPipeline(); - // 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() + ">"); + + // 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() + ">"); + 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("RCPT TO", resp->getText(), mbox.getEmail()); + throw exceptions::command_error("MAIL", resp->getText()); } - } - // Send the message data - sendRequest("DATA"); + // 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()); + } + } - if ((resp = readResponse())->getCode() != 354) + // Read response for "DATA" command + if ((resp = readResponse())->getCode() != 354) + { + internalDisconnect(); + throw exceptions::command_error("DATA", resp->getText()); + } + } + else { - internalDisconnect(); - throw exceptions::command_error("DATA", resp->getText()); + // Emit the "MAIL" command + sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">"); + + if ((resp = readResponse())->getCode() != 250) + { + internalDisconnect(); + 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 // 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 (end) - m_socket->send(buffer + "\r\n"); + if (m_pipelineStarted) + { + m_pipeline << buffer; + + if (end) + m_pipeline << "\r\n"; + } else - m_socket->send(buffer); + { + if (end) + m_socket->send(buffer + "\r\n"); + else + m_socket->send(buffer); + } } ref SMTPTransport::readResponse() { - return SMTPResponse::readResponse(m_socket, m_timeoutHandler); + ref resp = SMTPResponse::readResponse + (m_socket, m_timeoutHandler, m_responseState); + + m_responseState = resp->getCurrentState(); + + return resp; } -- cgit v1.2.3