aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2006-01-07 08:46:20 +0000
committerVincent Richard <[email protected]>2006-01-07 08:46:20 +0000
commit9dcc78085ad73b91f12e6a1fed2744ddd38b959a (patch)
tree13117d14830c9836cf1817b3259261b61abe9218
parentUpdated contributors list. (diff)
downloadvmime-9dcc78085ad73b91f12e6a1fed2744ddd38b959a.tar.gz
vmime-9dcc78085ad73b91f12e6a1fed2744ddd38b959a.zip
Added SMTPResponse to read and parse SMTP responses.
-rw-r--r--SConstruct1
-rw-r--r--src/net/smtp/SMTPResponse.cpp242
-rw-r--r--src/net/smtp/SMTPTransport.cpp162
-rw-r--r--vmime/net/smtp/SMTPResponse.hpp142
-rw-r--r--vmime/net/smtp/SMTPTransport.hpp13
5 files changed, 417 insertions, 143 deletions
diff --git a/SConstruct b/SConstruct
index dd7847fb..c0747d93 100644
--- a/SConstruct
+++ b/SConstruct
@@ -233,6 +233,7 @@ libvmime_messaging_proto_sources = [
[
'smtp',
[
+ 'net/smtp/SMTPResponse.cpp', 'net/smtp/SMTPResponse.hpp',
'net/smtp/SMTPServiceInfos.cpp', 'net/smtp/SMTPServiceInfos.hpp',
'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp',
'net/smtp/SMTPSTransport.cpp', 'net/smtp/SMTPSTransport.hpp'
diff --git a/src/net/smtp/SMTPResponse.cpp b/src/net/smtp/SMTPResponse.cpp
new file mode 100644
index 00000000..9f5efe94
--- /dev/null
+++ b/src/net/smtp/SMTPResponse.cpp
@@ -0,0 +1,242 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 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/smtp/SMTPResponse.hpp"
+
+#include "vmime/platformDependant.hpp"
+#include "vmime/utility/stringUtils.hpp"
+
+#include "vmime/net/socket.hpp"
+#include "vmime/net/timeoutHandler.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+SMTPResponse::SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh)
+ : m_socket(sok), m_timeoutHandler(toh),
+ m_responseContinues(false)
+{
+}
+
+
+SMTPResponse::SMTPResponse(const SMTPResponse&)
+ : vmime::object()
+{
+ // Not used
+}
+
+
+const int SMTPResponse::getCode() const
+{
+ const int firstCode = m_lines[0].getCode();
+
+ for (unsigned int i = 1 ; i < m_lines.size() ; ++i)
+ {
+ // All response codes returned must be equal
+ // or else this in an error...
+ if (m_lines[i].getCode() != firstCode)
+ return 0;
+ }
+
+ return firstCode;
+}
+
+
+const string SMTPResponse::getText() const
+{
+ string text = m_lines[0].getText();
+
+ for (unsigned int i = 1 ; i < m_lines.size() ; ++i)
+ {
+ text += '\n';
+ text += m_lines[i].getText();
+ }
+
+ return text;
+}
+
+
+// static
+ref <SMTPResponse> SMTPResponse::readResponse
+ (ref <socket> sok, ref <timeoutHandler> toh)
+{
+ ref <SMTPResponse> resp = vmime::create <SMTPResponse>(sok, toh);
+
+ resp->readResponse();
+
+ return resp;
+}
+
+
+void SMTPResponse::readResponse()
+{
+ responseLine line = getNextResponse();
+ m_lines.push_back(line);
+
+ while (m_responseContinues)
+ {
+ line = getNextResponse();
+ m_lines.push_back(line);
+ }
+}
+
+
+const string SMTPResponse::readResponseLine()
+{
+ string currentBuffer = m_responseBuffer;
+
+ while (true)
+ {
+ // Get a line from the response buffer
+ string::size_type lineEnd = currentBuffer.find_first_of('\n');
+
+ if (lineEnd != string::npos)
+ {
+ const string line(currentBuffer.begin(), currentBuffer.begin() + lineEnd);
+
+ currentBuffer.erase(currentBuffer.begin(), currentBuffer.begin() + lineEnd + 1);
+ m_responseBuffer = currentBuffer;
+
+ return line;
+ }
+
+ // Check whether the time-out delay is elapsed
+ if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
+ {
+ if (!m_timeoutHandler->handleTimeOut())
+ throw exceptions::operation_timed_out();
+
+ m_timeoutHandler->resetTimeOut();
+ }
+
+ // Receive data from the socket
+ string receiveBuffer;
+ m_socket->receive(receiveBuffer);
+
+ if (receiveBuffer.empty()) // buffer is empty
+ {
+ platformDependant::getHandler()->wait();
+ continue;
+ }
+
+ currentBuffer += receiveBuffer;
+ }
+}
+
+
+const SMTPResponse::responseLine SMTPResponse::getNextResponse()
+{
+ string line = readResponseLine();
+
+ // Special case where CRLF occurs after response code
+ if (line.length() < 4)
+ line = line + '\n' + readResponseLine();
+
+ const int code = extractResponseCode(line);
+ string text;
+
+ m_responseContinues = (line.length() >= 4 && line[3] == '-');
+
+ if (line.length() > 4)
+ text = utility::stringUtils::trim(line.substr(4));
+ else
+ text = utility::stringUtils::trim(line);
+
+ return responseLine(code, text);
+}
+
+
+// static
+const int SMTPResponse::extractResponseCode(const string& response)
+{
+ int code = 0;
+
+ if (response.length() >= 3)
+ {
+ code = (response[0] - '0') * 100
+ + (response[1] - '0') * 10
+ + (response[2] - '0');
+ }
+
+ return code;
+}
+
+
+const SMTPResponse::responseLine SMTPResponse::getLineAt(const unsigned int pos) const
+{
+ return m_lines[pos];
+}
+
+
+const unsigned int SMTPResponse::getLineCount() const
+{
+ return m_lines.size();
+}
+
+
+const SMTPResponse::responseLine SMTPResponse::getLastLine() const
+{
+ return m_lines[m_lines.size() - 1];
+}
+
+
+
+// SMTPResponse::responseLine
+
+SMTPResponse::responseLine::responseLine(const int code, const string& text)
+ : m_code(code), m_text(text)
+{
+}
+
+
+void SMTPResponse::responseLine::setCode(const int code)
+{
+ m_code = code;
+}
+
+
+const int SMTPResponse::responseLine::getCode() const
+{
+ return m_code;
+}
+
+
+void SMTPResponse::responseLine::setText(const string& text)
+{
+ m_text = text;
+}
+
+
+const string SMTPResponse::responseLine::getText() const
+{
+ return m_text;
+}
+
+
+} // smtp
+} // net
+} // vmime
+
diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp
index 3c70e34a..48ea5cdc 100644
--- a/src/net/smtp/SMTPTransport.cpp
+++ b/src/net/smtp/SMTPTransport.cpp
@@ -18,6 +18,7 @@
//
#include "vmime/net/smtp/SMTPTransport.hpp"
+#include "vmime/net/smtp/SMTPResponse.hpp"
#include "vmime/exception.hpp"
#include "vmime/platformDependant.hpp"
@@ -110,19 +111,17 @@ void SMTPTransport::connect()
m_socket->connect(address, port);
- m_responseBuffer.clear();
-
// Connection
//
// eg: C: <connection to server>
// --- S: 220 smtp.domain.com Service ready
- string response;
+ ref <SMTPResponse> resp;
- if (readAllResponses(response) != 220)
+ if ((resp = readResponse())->getCode() != 220)
{
internalDisconnect();
- throw exceptions::connection_greeting_error(response);
+ throw exceptions::connection_greeting_error(resp->getText());
}
// Identification
@@ -134,7 +133,7 @@ void SMTPTransport::connect()
sendRequest("EHLO " + platformDependant::getHandler()->getHostName());
- if (readAllResponses(response, true) != 250)
+ if ((resp = readResponse())->getCode() != 250)
{
// Next, try "Basic" SMTP
//
@@ -143,10 +142,10 @@ void SMTPTransport::connect()
sendRequest("HELO " + platformDependant::getHandler()->getHostName());
- if (readAllResponses(response) != 250)
+ if ((resp = readResponse())->getCode() != 250)
{
internalDisconnect();
- throw exceptions::connection_greeting_error(response);
+ throw exceptions::connection_greeting_error(resp->getLastLine().getText());
}
m_extendedSMTP = false;
@@ -154,7 +153,7 @@ void SMTPTransport::connect()
else
{
m_extendedSMTP = true;
- m_extendedSMTPResponse = response;
+ m_extendedSMTPResponse = resp->getText();
}
#if VMIME_HAVE_TLS_SUPPORT
@@ -335,9 +334,9 @@ void SMTPTransport::authenticateSASL()
for (bool cont = true ; cont ; )
{
- string response;
+ ref <SMTPResponse> response = readResponse();
- switch (readAllResponses(response))
+ switch (response->getCode())
{
case 235:
{
@@ -355,7 +354,7 @@ void SMTPTransport::authenticateSASL()
try
{
// Extract challenge
- saslContext->decodeB64(response, &challenge, &challengeLen);
+ saslContext->decodeB64(response->getText(), &challenge, &challengeLen);
// Prepare response
saslSession->evaluateChallenge
@@ -423,10 +422,10 @@ void SMTPTransport::startTLS()
{
sendRequest("STARTTLS");
- string response;
+ ref <SMTPResponse> resp = readResponse();
- if (readAllResponses(response) != 220)
- throw exceptions::command_error("STARTTLS", response);
+ if (resp->getCode() != 220)
+ throw exceptions::command_error("STARTTLS", resp->getText());
ref <tls::TLSSession> tlsSession =
vmime::create <tls::TLSSession>(getCertificateVerifier());
@@ -494,10 +493,10 @@ void SMTPTransport::noop()
{
sendRequest("NOOP");
- string response;
+ ref <SMTPResponse> resp = readResponse();
- if (readAllResponses(response) != 250)
- throw exceptions::command_error("NOOP", response);
+ if (resp->getCode() != 250)
+ throw exceptions::command_error("NOOP", resp->getText());
}
@@ -512,14 +511,14 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
throw exceptions::no_expeditor();
// Emit the "MAIL" command
- string response;
+ ref <SMTPResponse> resp;
sendRequest("MAIL FROM: <" + expeditor.getEmail() + ">");
- if (readAllResponses(response) != 250)
+ if ((resp = readResponse())->getCode() != 250)
{
internalDisconnect();
- throw exceptions::command_error("MAIL", response);
+ throw exceptions::command_error("MAIL", resp->getText());
}
// Emit a "RCPT TO" command for each recipient
@@ -529,20 +528,20 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
sendRequest("RCPT TO: <" + mbox.getEmail() + ">");
- if (readAllResponses(response) != 250)
+ if ((resp = readResponse())->getCode() != 250)
{
internalDisconnect();
- throw exceptions::command_error("RCPT TO", response);
+ throw exceptions::command_error("RCPT TO", resp->getText());
}
}
// Send the message data
sendRequest("DATA");
- if (readAllResponses(response) != 354)
+ if ((resp = readResponse())->getCode() != 354)
{
internalDisconnect();
- throw exceptions::command_error("DATA", response);
+ throw exceptions::command_error("DATA", resp->getText());
}
// Stream copy with "\n." to "\n.." transformation
@@ -556,10 +555,10 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
// Send end-of-data delimiter
m_socket->sendRaw("\r\n.\r\n", 5);
- if (readAllResponses(response) != 250)
+ if ((resp = readResponse())->getCode() != 250)
{
internalDisconnect();
- throw exceptions::command_error("DATA", response);
+ throw exceptions::command_error("DATA", resp->getText());
}
}
@@ -571,114 +570,9 @@ void SMTPTransport::sendRequest(const string& buffer, const bool end)
}
-const int SMTPTransport::getResponseCode(const string& response)
-{
- int code = 0;
-
- if (response.length() >= 3)
- {
- code = (response[0] - '0') * 100
- + (response[1] - '0') * 10
- + (response[2] - '0');
- }
-
- return (code);
-}
-
-
-const string SMTPTransport::readResponseLine()
-{
- string currentBuffer = m_responseBuffer;
-
- while (true)
- {
- // Get a line from the response buffer
- string::size_type lineEnd = currentBuffer.find_first_of('\n');
-
- if (lineEnd != string::npos)
- {
- const string line(currentBuffer.begin(), currentBuffer.begin() + lineEnd);
-
- currentBuffer.erase(currentBuffer.begin(), currentBuffer.begin() + lineEnd + 1);
- m_responseBuffer = currentBuffer;
-
- return line;
- }
-
- // Check whether the time-out delay is elapsed
- if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
- {
- if (!m_timeoutHandler->handleTimeOut())
- throw exceptions::operation_timed_out();
-
- m_timeoutHandler->resetTimeOut();
- }
-
- // Receive data from the socket
- string receiveBuffer;
- m_socket->receive(receiveBuffer);
-
- if (receiveBuffer.empty()) // buffer is empty
- {
- platformDependant::getHandler()->wait();
- continue;
- }
-
- currentBuffer += receiveBuffer;
- }
-}
-
-
-const int SMTPTransport::readResponse(string& text)
-{
- string line = readResponseLine();
-
- // Special case where CRLF occurs after response code
- if (line.length() < 4)
- line = line + '\n' + readResponseLine();
-
- const int code = getResponseCode(line);
-
- m_responseContinues = (line.length() >= 4 && line[3] == '-');
-
- if (line.length() > 4)
- text = utility::stringUtils::trim(line.substr(4));
- else
- text = utility::stringUtils::trim(line);
-
- return code;
-}
-
-
-const int SMTPTransport::readAllResponses(string& outText, const bool allText)
+ref <SMTPResponse> SMTPTransport::readResponse()
{
- string text;
-
- const int firstCode = readResponse(outText);
-
- if (allText)
- text = outText;
-
- while (m_responseContinues)
- {
- const int code = readResponse(outText);
-
- if (allText)
- text += '\n' + outText;
-
- if (code != firstCode)
- {
- if (allText)
- outText = text;
-
- return 0;
- }
- }
-
- if (allText)
- outText = text;
-
- return firstCode;
+ return SMTPResponse::readResponse(m_socket, m_timeoutHandler);
}
diff --git a/vmime/net/smtp/SMTPResponse.hpp b/vmime/net/smtp/SMTPResponse.hpp
new file mode 100644
index 00000000..49681e50
--- /dev/null
+++ b/vmime/net/smtp/SMTPResponse.hpp
@@ -0,0 +1,142 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 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_SMTP_SMTPRESPONSE_HPP_INCLUDED
+#define VMIME_NET_SMTP_SMTPRESPONSE_HPP_INCLUDED
+
+
+#include "vmime/object.hpp"
+#include "vmime/base.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+class socket;
+class timeoutHandler;
+
+
+namespace smtp {
+
+
+/** A SMTP response, as sent by the server.
+ */
+class SMTPResponse : public object
+{
+ friend class creator;
+
+public:
+
+ /** An element of a SMTP response. */
+ class responseLine
+ {
+ public:
+
+ responseLine(const int code, const string& text);
+
+ void setCode(const int code);
+ const int getCode() const;
+
+ void setText(const string& text);
+ const string getText() const;
+
+ private:
+
+ int m_code;
+ string m_text;
+ };
+
+ /** Receive and parse a new SMTP response from the
+ * specified socket.
+ *
+ * @param sok socket from which to read
+ * @param toh time-out handler
+ * @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);
+
+ /** Return the SMTP response code.
+ *
+ * @return response code
+ */
+ const int getCode() const;
+
+ /** Return the SMTP response text.
+ * The text of each line is concatenated.
+ *
+ * @return response text
+ */
+ const string getText() const;
+
+ /** Return the response line at the specified position.
+ *
+ * @param pos line index
+ * @return line at the specified index
+ */
+ const responseLine getLineAt(const unsigned int pos) const;
+
+ /** Return the number of lines in the response.
+ *
+ * @return number of lines in the response
+ */
+ const unsigned int getLineCount() const;
+
+ /** Return the last line in the response.
+ *
+ * @return last response line
+ */
+ const responseLine getLastLine() const;
+
+private:
+
+ SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh);
+ SMTPResponse(const SMTPResponse&);
+
+ void readResponse();
+
+ const string readResponseLine();
+ const responseLine getNextResponse();
+
+ static const int extractResponseCode(const string& response);
+
+
+ std::vector <responseLine> m_lines;
+
+ ref <socket> m_socket;
+ ref <timeoutHandler> m_timeoutHandler;
+
+ string m_responseBuffer;
+ bool m_responseContinues;
+};
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SMTP_SMTPRESPONSE_HPP_INCLUDED
+
diff --git a/vmime/net/smtp/SMTPTransport.hpp b/vmime/net/smtp/SMTPTransport.hpp
index 96b0bef5..d42288b7 100644
--- a/vmime/net/smtp/SMTPTransport.hpp
+++ b/vmime/net/smtp/SMTPTransport.hpp
@@ -39,6 +39,9 @@ namespace net {
namespace smtp {
+class SMTPResponse;
+
+
/** SMTP transport service.
*/
@@ -64,13 +67,8 @@ public:
private:
- static const int getResponseCode(const string& response);
-
void sendRequest(const string& buffer, const bool end = true);
-
- const string readResponseLine();
- const int readResponse(string& text);
- const int readAllResponses(string& text, const bool allText = false);
+ ref <SMTPResponse> readResponse();
void internalDisconnect();
@@ -89,9 +87,6 @@ private:
bool m_extendedSMTP;
string m_extendedSMTPResponse;
- string m_responseBuffer;
- bool m_responseContinues;
-
ref <timeoutHandler> m_timeoutHandler;
bool m_secured;