aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct1
-rw-r--r--src/net/pop3/POP3Folder.cpp111
-rw-r--r--src/net/pop3/POP3Message.cpp19
-rw-r--r--src/net/pop3/POP3Response.cpp426
-rw-r--r--src/net/pop3/POP3Store.cpp370
-rw-r--r--src/net/pop3/POP3Utils.cpp9
-rw-r--r--vmime/net/pop3/POP3Response.hpp186
-rw-r--r--vmime/net/pop3/POP3Store.hpp17
-rw-r--r--vmime/net/pop3/POP3Utils.hpp5
9 files changed, 720 insertions, 424 deletions
diff --git a/SConstruct b/SConstruct
index 31228ed4..d1bb23c5 100644
--- a/SConstruct
+++ b/SConstruct
@@ -236,6 +236,7 @@ libvmime_messaging_proto_sources = [
'net/pop3/POP3SStore.cpp', 'net/pop3/POP3SStore.hpp',
'net/pop3/POP3Folder.cpp', 'net/pop3/POP3Folder.hpp',
'net/pop3/POP3Message.cpp', 'net/pop3/POP3Message.hpp',
+ 'net/pop3/POP3Response.cpp', 'net/pop3/POP3Response.hpp',
'net/pop3/POP3Utils.cpp', 'net/pop3/POP3Utils.hpp'
]
],
diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp
index f9a4225f..8533712e 100644
--- a/src/net/pop3/POP3Folder.cpp
+++ b/src/net/pop3/POP3Folder.cpp
@@ -31,6 +31,7 @@
#include "vmime/net/pop3/POP3Store.hpp"
#include "vmime/net/pop3/POP3Message.hpp"
+#include "vmime/net/pop3/POP3Response.hpp"
#include "vmime/net/pop3/POP3Utils.hpp"
@@ -131,19 +132,17 @@ void POP3Folder::open(const int mode, bool failIfModeIsNotAvailable)
{
store->sendRequest("STAT");
- string response;
- store->readResponse(response, false);
+ ref <POP3Response> response =
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
- if (!store->isSuccessResponse(response))
- throw exceptions::command_error("STAT", response);
+ if (!response->isSuccess())
+ throw exceptions::command_error("STAT", response->getFirstLine());
- store->stripResponseCode(response, response);
-
- std::istringstream iss(response);
+ std::istringstream iss(response->getText());
iss >> m_messageCount;
if (iss.fail())
- throw exceptions::invalid_response("STAT", response);
+ throw exceptions::invalid_response("STAT", response->getFirstLine());
m_open = true;
m_mode = mode;
@@ -167,9 +166,7 @@ void POP3Folder::close(const bool expunge)
if (!expunge)
{
store->sendRequest("RSET");
-
- string response;
- store->readResponse(response, false);
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
}
m_open = false;
@@ -371,13 +368,11 @@ void POP3Folder::fetchMessages(std::vector <ref <message> >& msg, const int opti
store->sendRequest(command.str());
// Get the response
- string response;
- store->readResponse(response, true, NULL);
+ ref <POP3Response> response =
+ POP3Response::readMultilineResponse(store->m_socket, store->m_timeoutHandler);
- if (store->isSuccessResponse(response))
+ if (response->isSuccess())
{
- store->stripFirstLine(response, response, NULL);
-
// C: LIST
// S: +OK
// S: 1 47548
@@ -416,13 +411,11 @@ void POP3Folder::fetchMessages(std::vector <ref <message> >& msg, const int opti
store->sendRequest(command.str());
// Get the response
- string response;
- store->readResponse(response, true, NULL);
+ ref <POP3Response> response =
+ POP3Response::readMultilineResponse(store->m_socket, store->m_timeoutHandler);
- if (store->isSuccessResponse(response))
+ if (response->isSuccess())
{
- store->stripFirstLine(response, response, NULL);
-
// C: UIDL
// S: +OK
// S: 1 whqtswO00WBw418f9t5JxYwZ
@@ -472,26 +465,26 @@ void POP3Folder::fetchMessage(ref <message> msg, const int options)
store->sendRequest(command.str());
// Get the response
- string response;
- store->readResponse(response, false, NULL);
+ ref <POP3Response> response =
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
- if (store->isSuccessResponse(response))
+ if (response->isSuccess())
{
- store->stripResponseCode(response, response);
+ string responseText = response->getText();
// C: LIST 2
// S: +OK 2 4242
- string::iterator it = response.begin();
+ string::iterator it = responseText.begin();
- while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
- while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it;
- while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
+ while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it;
+ while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it;
+ while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it;
- if (it != response.end())
+ if (it != responseText.end())
{
int size = 0;
- std::istringstream iss(string(it, response.end()));
+ std::istringstream iss(string(it, responseText.end()));
iss >> size;
msg.dynamicCast <POP3Message>()->m_size = size;
@@ -510,25 +503,25 @@ void POP3Folder::fetchMessage(ref <message> msg, const int options)
store->sendRequest(command.str());
// Get the response
- string response;
- store->readResponse(response, false, NULL);
+ ref <POP3Response> response =
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
- if (store->isSuccessResponse(response))
+ if (response->isSuccess())
{
- store->stripResponseCode(response, response);
+ string responseText = response->getText();
// C: UIDL 2
// S: +OK 2 QhdPYR:00WBw1Ph7x7
- string::iterator it = response.begin();
+ string::iterator it = responseText.begin();
- while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
- while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it;
- while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
+ while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it;
+ while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it;
+ while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it;
- if (it != response.end())
+ if (it != responseText.end())
{
msg.dynamicCast <POP3Message>()->m_uid =
- string(it, response.end());
+ string(it, responseText.end());
}
}
}
@@ -598,11 +591,11 @@ void POP3Folder::deleteMessage(const int num)
store->sendRequest(command.str());
- string response;
- store->readResponse(response, false);
+ ref <POP3Response> response =
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
- if (!store->isSuccessResponse(response))
- throw exceptions::command_error("DELE", response);
+ if (!response->isSuccess())
+ throw exceptions::command_error("DELE", response->getFirstLine());
// Update local flags
for (std::map <POP3Message*, int>::iterator it =
@@ -649,11 +642,11 @@ void POP3Folder::deleteMessages(const int from, const int to)
store->sendRequest(command.str());
- string response;
- store->readResponse(response, false);
+ ref <POP3Response> response =
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
- if (!store->isSuccessResponse(response))
- throw exceptions::command_error("DELE", response);
+ if (!response->isSuccess())
+ throw exceptions::command_error("DELE", response->getFirstLine());
}
// Update local flags
@@ -702,11 +695,11 @@ void POP3Folder::deleteMessages(const std::vector <int>& nums)
store->sendRequest(command.str());
- string response;
- store->readResponse(response, false);
+ ref <POP3Response> response =
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
- if (!store->isSuccessResponse(response))
- throw exceptions::command_error("DELE", response);
+ if (!response->isSuccess())
+ throw exceptions::command_error("DELE", response->getFirstLine());
}
// Sort message list
@@ -799,15 +792,13 @@ void POP3Folder::status(int& count, int& unseen)
store->sendRequest("STAT");
- string response;
- store->readResponse(response, false);
-
- if (!store->isSuccessResponse(response))
- throw exceptions::command_error("STAT", response);
+ ref <POP3Response> response =
+ POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
- store->stripResponseCode(response, response);
+ if (!response->isSuccess())
+ throw exceptions::command_error("STAT", response->getFirstLine());
- std::istringstream iss(response);
+ std::istringstream iss(response->getText());
iss >> count;
unseen = count;
diff --git a/src/net/pop3/POP3Message.cpp b/src/net/pop3/POP3Message.cpp
index eb33ddca..59b4c443 100644
--- a/src/net/pop3/POP3Message.cpp
+++ b/src/net/pop3/POP3Message.cpp
@@ -28,10 +28,12 @@
#include "vmime/net/pop3/POP3Message.hpp"
+#include "vmime/net/pop3/POP3Response.hpp"
#include "vmime/net/pop3/POP3Folder.hpp"
#include "vmime/net/pop3/POP3Store.hpp"
#include "vmime/utility/outputStreamAdapter.hpp"
+#include "vmime/utility/outputStreamStringAdapter.hpp"
#include <sstream>
@@ -140,12 +142,14 @@ void POP3Message::extract(utility::outputStream& os,
std::ostringstream oss;
oss << "RETR " << m_num;
- folder.constCast <POP3Folder>()->m_store.acquire()->sendRequest(oss.str());
+ ref <POP3Store> store = folder.constCast <POP3Folder>()->m_store.acquire();
+
+ store->sendRequest(oss.str());
try
{
- folder.constCast <POP3Folder>()->m_store.acquire()->
- readResponse(os, progress, m_size);
+ POP3Response::readLargeResponse
+ (store->m_socket, store->m_timeoutHandler, os, progress, m_size);
}
catch (exceptions::command_error& e)
{
@@ -197,12 +201,17 @@ void POP3Message::fetch(ref <POP3Folder> msgFolder, const int options)
std::ostringstream oss;
oss << "TOP " << m_num << " 0";
- folder->m_store.acquire()->sendRequest(oss.str());
+ ref <POP3Store> store = folder->m_store.acquire();
+
+ store->sendRequest(oss.str());
try
{
string buffer;
- folder->m_store.acquire()->readResponse(buffer, true);
+ utility::outputStreamStringAdapter bufferStream(buffer);
+
+ POP3Response::readLargeResponse(store->m_socket, store->m_timeoutHandler,
+ bufferStream, /* progress */ NULL, /* predictedSize */ 0);
m_header = vmime::create <header>();
m_header->parse(buffer);
diff --git a/src/net/pop3/POP3Response.cpp b/src/net/pop3/POP3Response.cpp
new file mode 100644
index 00000000..ab792611
--- /dev/null
+++ b/src/net/pop3/POP3Response.cpp
@@ -0,0 +1,426 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2009 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 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 && VMIME_HAVE_MESSAGING_PROTO_POP3
+
+
+#include "vmime/net/pop3/POP3Response.hpp"
+
+#include "vmime/platform.hpp"
+
+#include "vmime/utility/stringUtils.hpp"
+#include "vmime/utility/filteredStream.hpp"
+#include "vmime/utility/stringUtils.hpp"
+#include "vmime/utility/inputStreamSocketAdapter.hpp"
+
+#include "vmime/net/socket.hpp"
+#include "vmime/net/timeoutHandler.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+POP3Response::POP3Response(ref <socket> sok, ref <timeoutHandler> toh)
+ : m_socket(sok), m_timeoutHandler(toh)
+{
+}
+
+
+// static
+ref <POP3Response> POP3Response::readResponse
+ (ref <socket> sok, ref <timeoutHandler> toh)
+{
+ ref <POP3Response> resp = vmime::create <POP3Response>(sok, toh);
+
+ string buffer;
+ resp->readResponseImpl(buffer, /* multiLine */ false);
+
+ resp->m_firstLine = buffer;
+ resp->m_code = getResponseCode(buffer);
+ stripResponseCode(buffer, resp->m_text);
+
+ return resp;
+}
+
+
+// static
+ref <POP3Response> POP3Response::readMultilineResponse
+ (ref <socket> sok, ref <timeoutHandler> toh)
+{
+ ref <POP3Response> resp = vmime::create <POP3Response>(sok, toh);
+
+ string buffer;
+ resp->readResponseImpl(buffer, /* multiLine */ true);
+
+ string firstLine, nextLines;
+ stripFirstLine(buffer, nextLines, &firstLine);
+
+ resp->m_firstLine = firstLine;
+ resp->m_code = getResponseCode(firstLine);
+ stripResponseCode(firstLine, resp->m_text);
+
+ std::istringstream iss(nextLines);
+ string line;
+
+ while (std::getline(iss, line, '\n'))
+ resp->m_lines.push_back(utility::stringUtils::trim(line));
+
+ return resp;
+}
+
+
+// static
+ref <POP3Response> POP3Response::readLargeResponse
+ (ref <socket> sok, ref <timeoutHandler> toh,
+ utility::outputStream& os, utility::progressListener* progress, const long predictedSize)
+{
+ ref <POP3Response> resp = vmime::create <POP3Response>(sok, toh);
+
+ string firstLine;
+ resp->readResponseImpl(firstLine, os, progress, predictedSize);
+
+ resp->m_firstLine = firstLine;
+ resp->m_code = getResponseCode(firstLine);
+ stripResponseCode(firstLine, resp->m_text);
+
+ return resp;
+}
+
+
+bool POP3Response::isSuccess() const
+{
+ return m_code == CODE_OK;
+}
+
+
+const string POP3Response::getFirstLine() const
+{
+ return m_firstLine;
+}
+
+
+POP3Response::ResponseCode POP3Response::getCode() const
+{
+ return m_code;
+}
+
+
+const string POP3Response::getText() const
+{
+ return m_text;
+}
+
+
+const string POP3Response::getLineAt(const unsigned int pos) const
+{
+ return m_lines[pos];
+}
+
+
+unsigned int POP3Response::getLineCount() const
+{
+ return m_lines.size();
+}
+
+
+void POP3Response::readResponseImpl(string& buffer, const bool multiLine)
+{
+ bool foundTerminator = false;
+
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ buffer.clear();
+
+ string::value_type last1 = '\0', last2 = '\0';
+
+ for ( ; !foundTerminator ; )
+ {
+ // 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
+ {
+ platform::getHandler()->wait();
+ continue;
+ }
+
+ // We have received data: reset the time-out counter
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ // Check for transparent characters: '\n..' becomes '\n.'
+ const string::value_type first = receiveBuffer[0];
+
+ if (first == '.' && last2 == '\n' && last1 == '.')
+ {
+ receiveBuffer.erase(receiveBuffer.begin());
+ }
+ else if (receiveBuffer.length() >= 2 && first == '.' &&
+ receiveBuffer[1] == '.' && last1 == '\n')
+ {
+ receiveBuffer.erase(receiveBuffer.begin());
+ }
+
+ for (string::size_type trans ;
+ string::npos != (trans = receiveBuffer.find("\n..")) ; )
+ {
+ receiveBuffer.replace(trans, 3, "\n.");
+ }
+
+ last1 = receiveBuffer[receiveBuffer.length() - 1];
+ last2 = static_cast <char>((receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0);
+
+ // Append the data to the response buffer
+ buffer += receiveBuffer;
+
+ // Check for terminator string (and strip it if present)
+ foundTerminator = checkTerminator(buffer, multiLine);
+
+ // If there is an error (-ERR) when executing a command that
+ // requires a multi-line response, the error response will
+ // include only one line, so we stop waiting for a multi-line
+ // terminator and check for a "normal" one.
+ if (multiLine && !foundTerminator && buffer.length() >= 4 && buffer[0] == '-')
+ {
+ foundTerminator = checkTerminator(buffer, false);
+ }
+ }
+}
+
+
+void POP3Response::readResponseImpl
+ (string& firstLine, utility::outputStream& os,
+ utility::progressListener* progress, const long predictedSize)
+{
+ long current = 0, total = predictedSize;
+
+ string temp;
+ bool codeDone = false;
+
+ if (progress)
+ progress->start(total);
+
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ utility::inputStreamSocketAdapter sis(*m_socket);
+ utility::stopSequenceFilteredInputStream <5> sfis1(sis, "\r\n.\r\n");
+ utility::stopSequenceFilteredInputStream <3> sfis2(sfis1, "\n.\n");
+ utility::dotFilteredInputStream dfis(sfis2); // "\n.." --> "\n."
+
+ utility::inputStream& is = dfis;
+
+ while (!is.eof())
+ {
+#if 0 // not supported
+ // Check for possible cancellation
+ if (progress && progress->cancel())
+ throw exceptions::operation_cancelled();
+#endif
+
+ // Check whether the time-out delay is elapsed
+ if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
+ {
+ if (!m_timeoutHandler->handleTimeOut())
+ throw exceptions::operation_timed_out();
+ }
+
+ // Receive data from the socket
+ utility::stream::value_type buffer[65536];
+ const utility::stream::size_type read = is.read(buffer, sizeof(buffer));
+
+ if (read == 0) // buffer is empty
+ {
+ platform::getHandler()->wait();
+ continue;
+ }
+
+ // We have received data: reset the time-out counter
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ // Notify progress
+ current += read;
+
+ if (progress)
+ {
+ total = std::max(total, current);
+ progress->progress(current, total);
+ }
+
+ // If we don't have extracted the response code yet
+ if (!codeDone)
+ {
+ temp.append(buffer, read);
+
+ string responseData;
+
+ if (stripFirstLine(temp, responseData, &firstLine) == true)
+ {
+ if (getResponseCode(firstLine) != CODE_OK)
+ throw exceptions::command_error("?", firstLine);
+
+ codeDone = true;
+
+ os.write(responseData.data(), responseData.length());
+ temp.clear();
+
+ continue;
+ }
+ }
+ else
+ {
+ // Inject the data into the output stream
+ os.write(buffer, read);
+ }
+ }
+
+ if (progress)
+ progress->stop(total);
+}
+
+
+// static
+bool POP3Response::stripFirstLine
+ (const string& buffer, string& result, string* firstLine)
+{
+ const string::size_type end = buffer.find('\n');
+
+ if (end != string::npos)
+ {
+ if (firstLine) *firstLine = buffer.substr(0, end);
+ result = buffer.substr(end + 1);
+ return true;
+ }
+ else
+ {
+ if (firstLine) *firstLine = buffer;
+ result = "";
+ return false;
+ }
+}
+
+
+// static
+POP3Response::ResponseCode POP3Response::getResponseCode(const string& buffer)
+{
+ if (buffer.length() >= 2)
+ {
+ // +[space]
+ if (buffer[0] == '+' &&
+ (buffer[1] == ' ' || buffer[1] == '\t'))
+ {
+ return CODE_READY;
+ }
+
+ // +OK
+ if (buffer.length() >= 3)
+ {
+ if (buffer[0] == '+' &&
+ (buffer[1] == 'O' || buffer[1] == 'o') &&
+ (buffer[2] == 'K' || buffer[1] == 'k'))
+ {
+ return CODE_OK;
+ }
+ }
+ }
+
+ // -ERR or whatever
+ return CODE_ERR;
+}
+
+
+// static
+void POP3Response::stripResponseCode(const string& buffer, string& result)
+{
+ const string::size_type pos = buffer.find_first_of(" \t");
+
+ if (pos != string::npos)
+ result = buffer.substr(pos + 1);
+ else
+ result = buffer;
+}
+
+
+// static
+bool POP3Response::checkTerminator(string& buffer, const bool multiLine)
+{
+ // Multi-line response
+ if (multiLine)
+ {
+ static const string term1("\r\n.\r\n");
+ static const string term2("\n.\n");
+
+ return (checkOneTerminator(buffer, term1) ||
+ checkOneTerminator(buffer, term2));
+ }
+ // Normal response
+ else
+ {
+ static const string term1("\r\n");
+ static const string term2("\n");
+
+ return (checkOneTerminator(buffer, term1) ||
+ checkOneTerminator(buffer, term2));
+ }
+
+ return false;
+}
+
+
+// static
+bool POP3Response::checkOneTerminator(string& buffer, const string& term)
+{
+ if (buffer.length() >= term.length() &&
+ std::equal(buffer.end() - term.length(), buffer.end(), term.begin()))
+ {
+ buffer.erase(buffer.end() - term.length(), buffer.end());
+ return true;
+ }
+
+ return false;
+}
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3
diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp
index 749c5dea..a1dac0a0 100644
--- a/src/net/pop3/POP3Store.cpp
+++ b/src/net/pop3/POP3Store.cpp
@@ -29,14 +29,12 @@
#include "vmime/net/pop3/POP3Store.hpp"
#include "vmime/net/pop3/POP3Folder.hpp"
+#include "vmime/net/pop3/POP3Response.hpp"
#include "vmime/exception.hpp"
#include "vmime/platform.hpp"
#include "vmime/messageId.hpp"
#include "vmime/security/digest/messageDigestFactory.hpp"
-#include "vmime/utility/filteredStream.hpp"
-#include "vmime/utility/stringUtils.hpp"
-#include "vmime/utility/inputStreamSocketAdapter.hpp"
#include "vmime/net/defaultConnectionInfos.hpp"
@@ -174,13 +172,12 @@ void POP3Store::connect()
// eg: C: <connection to server>
// --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <[email protected]>
- string response;
- readResponse(response, false);
+ ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler);
- if (!isSuccessResponse(response))
+ if (!response->isSuccess())
{
internalDisconnect();
- throw exceptions::connection_greeting_error(response);
+ throw exceptions::connection_greeting_error(response->getFirstLine());
}
#if VMIME_HAVE_TLS_SUPPORT
@@ -217,7 +214,7 @@ void POP3Store::connect()
#endif // VMIME_HAVE_TLS_SUPPORT
// Start authentication process
- authenticate(messageId(response));
+ authenticate(messageId(response->getText()));
}
@@ -265,7 +262,7 @@ void POP3Store::authenticate(const messageId& randomMID)
const string username = getAuthenticator()->getUsername();
const string password = getAuthenticator()->getPassword();
- string response;
+ ref <POP3Response> response;
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP))
{
@@ -280,9 +277,9 @@ void POP3Store::authenticate(const messageId& randomMID)
md5->finalize();
sendRequest("APOP " + username + " " + md5->getHexDigest());
- readResponse(response, false);
+ response = POP3Response::readResponse(m_socket, m_timeoutHandler);
- if (isSuccessResponse(response))
+ if (response->isSuccess())
{
m_authentified = true;
return;
@@ -302,20 +299,19 @@ void POP3Store::authenticate(const messageId& randomMID)
{
// Can't fallback on basic authentication
internalDisconnect();
- throw exceptions::authentication_error(response);
+ throw exceptions::authentication_error(response->getFirstLine());
}
// Ensure connection is valid (cf. note above)
try
{
- string response2;
sendRequest("NOOP");
- readResponse(response2, false);
+ POP3Response::readResponse(m_socket, m_timeoutHandler);
}
catch (exceptions::socket_exception&)
{
internalDisconnect();
- throw exceptions::authentication_error(response);
+ throw exceptions::authentication_error(response->getFirstLine());
}
}
}
@@ -339,21 +335,21 @@ void POP3Store::authenticate(const messageId& randomMID)
// C: PASS couic
// S: +OK vincent's maildrop has 2 messages (320 octets)
sendRequest("USER " + username);
- readResponse(response, false);
+ response = POP3Response::readResponse(m_socket, m_timeoutHandler);
- if (!isSuccessResponse(response))
+ if (!response->isSuccess())
{
internalDisconnect();
- throw exceptions::authentication_error(response);
+ throw exceptions::authentication_error(response->getFirstLine());
}
sendRequest("PASS " + password);
- readResponse(response, false);
+ response = POP3Response::readResponse(m_socket, m_timeoutHandler);
- if (!isSuccessResponse(response))
+ if (!response->isSuccess())
{
internalDisconnect();
- throw exceptions::authentication_error(response);
+ throw exceptions::authentication_error(response->getFirstLine());
}
m_authentified = true;
@@ -453,17 +449,16 @@ void POP3Store::authenticateSASL()
for (bool cont = true ; cont ; )
{
- string response;
- readResponse(response, false);
+ ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler);
- switch (getResponseCode(response))
+ switch (response->getCode())
{
- case RESPONSE_OK:
+ case POP3Response::CODE_OK:
{
m_socket = saslSession->getSecuredSocket(m_socket);
return;
}
- case RESPONSE_READY:
+ case POP3Response::CODE_READY:
{
byte_t* challenge = 0;
long challengeLen = 0;
@@ -474,8 +469,7 @@ void POP3Store::authenticateSASL()
try
{
// Extract challenge
- stripResponseCode(response, response);
- saslContext->decodeB64(response, &challenge, &challengeLen);
+ saslContext->decodeB64(response->getText(), &challenge, &challengeLen);
// Prepare response
saslSession->evaluateChallenge
@@ -543,11 +537,10 @@ void POP3Store::startTLS()
{
sendRequest("STLS");
- string response;
- readResponse(response, false);
+ ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler);
- if (getResponseCode(response) != RESPONSE_OK)
- throw exceptions::command_error("STLS", response);
+ if (!response->isSuccess())
+ throw exceptions::command_error("STLS", response->getFirstLine());
ref <tls::TLSSession> tlsSession =
tls::TLSSession::create(getCertificateVerifier());
@@ -641,11 +634,11 @@ void POP3Store::noop()
{
sendRequest("NOOP");
- string response;
- readResponse(response, false);
+ ref <POP3Response> response =
+ POP3Response::readResponse(m_socket, m_timeoutHandler);
- if (!isSuccessResponse(response))
- throw exceptions::command_error("NOOP", response);
+ if (!response->isSuccess())
+ throw exceptions::command_error("NOOP", response->getFirstLine());
}
@@ -653,89 +646,21 @@ const std::vector <string> POP3Store::getCapabilities()
{
sendRequest("CAPA");
- string response;
- readResponse(response, true);
+ ref <POP3Response> response =
+ POP3Response::readMultilineResponse(m_socket, m_timeoutHandler);
std::vector <string> res;
- if (isSuccessResponse(response))
+ if (response->isSuccess())
{
- stripFirstLine(response, response);
-
- std::istringstream iss(response);
- string line;
-
- while (std::getline(iss, line, '\n'))
- res.push_back(utility::stringUtils::trim(line));
+ for (unsigned int i = 0, n = response->getLineCount() ; i < n ; ++i)
+ res.push_back(response->getLineAt(i));
}
return res;
}
-bool POP3Store::isSuccessResponse(const string& buffer)
-{
- return getResponseCode(buffer) == RESPONSE_OK;
-}
-
-
-bool POP3Store::stripFirstLine(const string& buffer, string& result, string* firstLine)
-{
- const string::size_type end = buffer.find('\n');
-
- if (end != string::npos)
- {
- if (firstLine) *firstLine = buffer.substr(0, end);
- result = buffer.substr(end + 1);
- return (true);
- }
- else
- {
- result = buffer;
- return (false);
- }
-}
-
-
-int POP3Store::getResponseCode(const string& buffer)
-{
- if (buffer.length() >= 2)
- {
- // +[space]
- if (buffer[0] == '+' &&
- (buffer[1] == ' ' || buffer[1] == '\t'))
- {
- return RESPONSE_READY;
- }
-
- // +OK
- if (buffer.length() >= 3)
- {
- if (buffer[0] == '+' &&
- (buffer[1] == 'O' || buffer[1] == 'o') &&
- (buffer[2] == 'K' || buffer[1] == 'k'))
- {
- return RESPONSE_OK;
- }
- }
- }
-
- // -ERR or whatever
- return RESPONSE_ERR;
-}
-
-
-void POP3Store::stripResponseCode(const string& buffer, string& result)
-{
- const string::size_type pos = buffer.find_first_of(" \t");
-
- if (pos != string::npos)
- result = buffer.substr(pos + 1);
- else
- result = buffer;
-}
-
-
void POP3Store::sendRequest(const string& buffer, const bool end)
{
if (end)
@@ -745,233 +670,6 @@ void POP3Store::sendRequest(const string& buffer, const bool end)
}
-void POP3Store::readResponse(string& buffer, const bool multiLine,
- utility::progressListener* progress)
-{
- bool foundTerminator = false;
- long current = 0, total = 0;
-
- if (progress)
- progress->start(total);
-
- if (m_timeoutHandler)
- m_timeoutHandler->resetTimeOut();
-
- buffer.clear();
-
- string::value_type last1 = '\0', last2 = '\0';
-
- for ( ; !foundTerminator ; )
- {
-#if 0 // not supported
- // Check for possible cancellation
- if (progress && progress->cancel())
- throw exceptions::operation_cancelled();
-#endif
-
- // 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
- {
- platform::getHandler()->wait();
- continue;
- }
-
- // We have received data: reset the time-out counter
- if (m_timeoutHandler)
- m_timeoutHandler->resetTimeOut();
-
- // Check for transparent characters: '\n..' becomes '\n.'
- const string::value_type first = receiveBuffer[0];
-
- if (first == '.' && last2 == '\n' && last1 == '.')
- {
- receiveBuffer.erase(receiveBuffer.begin());
- }
- else if (receiveBuffer.length() >= 2 && first == '.' &&
- receiveBuffer[1] == '.' && last1 == '\n')
- {
- receiveBuffer.erase(receiveBuffer.begin());
- }
-
- for (string::size_type trans ;
- string::npos != (trans = receiveBuffer.find("\n..")) ; )
- {
- receiveBuffer.replace(trans, 3, "\n.");
- }
-
- last1 = receiveBuffer[receiveBuffer.length() - 1];
- last2 = static_cast <char>((receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0);
-
- // Append the data to the response buffer
- buffer += receiveBuffer;
- current += receiveBuffer.length();
-
- // Check for terminator string (and strip it if present)
- foundTerminator = checkTerminator(buffer, multiLine);
-
- // Notify progress
- if (progress)
- {
- total = std::max(total, current);
- progress->progress(current, total);
- }
-
- // If there is an error (-ERR) when executing a command that
- // requires a multi-line response, the error response will
- // include only one line, so we stop waiting for a multi-line
- // terminator and check for a "normal" one.
- if (multiLine && !foundTerminator && buffer.length() >= 4 && buffer[0] == '-')
- {
- foundTerminator = checkTerminator(buffer, false);
- }
- }
-
- if (progress)
- progress->stop(total);
-}
-
-
-void POP3Store::readResponse(utility::outputStream& os,
- utility::progressListener* progress, const int predictedSize)
-{
- long current = 0, total = predictedSize;
-
- string temp;
- bool codeDone = false;
-
- if (progress)
- progress->start(total);
-
- if (m_timeoutHandler)
- m_timeoutHandler->resetTimeOut();
-
- utility::inputStreamSocketAdapter sis(*m_socket);
- utility::stopSequenceFilteredInputStream <5> sfis1(sis, "\r\n.\r\n");
- utility::stopSequenceFilteredInputStream <3> sfis2(sfis1, "\n.\n");
- utility::dotFilteredInputStream dfis(sfis2); // "\n.." --> "\n."
-
- utility::inputStream& is = dfis;
-
- while (!is.eof())
- {
-#if 0 // not supported
- // Check for possible cancellation
- if (progress && progress->cancel())
- throw exceptions::operation_cancelled();
-#endif
-
- // Check whether the time-out delay is elapsed
- if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
- {
- if (!m_timeoutHandler->handleTimeOut())
- throw exceptions::operation_timed_out();
- }
-
- // Receive data from the socket
- utility::stream::value_type buffer[65536];
- const utility::stream::size_type read = is.read(buffer, sizeof(buffer));
-
- if (read == 0) // buffer is empty
- {
- platform::getHandler()->wait();
- continue;
- }
-
- // We have received data: reset the time-out counter
- if (m_timeoutHandler)
- m_timeoutHandler->resetTimeOut();
-
- // Notify progress
- current += read;
-
- if (progress)
- {
- total = std::max(total, current);
- progress->progress(current, total);
- }
-
- // If we don't have extracted the response code yet
- if (!codeDone)
- {
- temp.append(buffer, read);
-
- string firstLine;
-
- if (stripFirstLine(temp, temp, &firstLine) == true)
- {
- if (!isSuccessResponse(firstLine))
- throw exceptions::command_error("?", firstLine);
-
- codeDone = true;
-
- os.write(temp.data(), temp.length());
- temp.clear();
-
- continue;
- }
- }
- else
- {
- // Inject the data into the output stream
- os.write(buffer, read);
- }
- }
-
- if (progress)
- progress->stop(total);
-}
-
-
-bool POP3Store::checkTerminator(string& buffer, const bool multiLine)
-{
- // Multi-line response
- if (multiLine)
- {
- static const string term1("\r\n.\r\n");
- static const string term2("\n.\n");
-
- return (checkOneTerminator(buffer, term1) ||
- checkOneTerminator(buffer, term2));
- }
- // Normal response
- else
- {
- static const string term1("\r\n");
- static const string term2("\n");
-
- return (checkOneTerminator(buffer, term1) ||
- checkOneTerminator(buffer, term2));
- }
-
- return (false);
-}
-
-
-bool POP3Store::checkOneTerminator(string& buffer, const string& term)
-{
- if (buffer.length() >= term.length() &&
- std::equal(buffer.end() - term.length(), buffer.end(), term.begin()))
- {
- buffer.erase(buffer.end() - term.length(), buffer.end());
- return (true);
- }
-
- return (false);
-}
-
-
void POP3Store::registerFolder(POP3Folder* folder)
{
m_folders.push_back(folder);
diff --git a/src/net/pop3/POP3Utils.cpp b/src/net/pop3/POP3Utils.cpp
index f75d338e..c065b695 100644
--- a/src/net/pop3/POP3Utils.cpp
+++ b/src/net/pop3/POP3Utils.cpp
@@ -28,6 +28,7 @@
#include "vmime/net/pop3/POP3Utils.hpp"
+#include "vmime/net/pop3/POP3Response.hpp"
#include <sstream>
@@ -38,15 +39,13 @@ namespace pop3 {
// static
-void POP3Utils::parseMultiListOrUidlResponse(const string& response, std::map <int, string>& result)
+void POP3Utils::parseMultiListOrUidlResponse(ref <POP3Response> response, std::map <int, string>& result)
{
- std::istringstream iss(response);
std::map <int, string> ids;
- string line;
-
- while (std::getline(iss, line))
+ for (unsigned int i = 0, n = response->getLineCount() ; i < n ; ++i)
{
+ string line = response->getLineAt(i);
string::iterator it = line.begin();
while (it != line.end() && (*it == ' ' || *it == '\t'))
diff --git a/vmime/net/pop3/POP3Response.hpp b/vmime/net/pop3/POP3Response.hpp
new file mode 100644
index 00000000..b968b577
--- /dev/null
+++ b/vmime/net/pop3/POP3Response.hpp
@@ -0,0 +1,186 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2009 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 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_SMTP_POP3RESPONSE_HPP_INCLUDED
+#define VMIME_NET_SMTP_POP3RESPONSE_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+
+#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3
+
+
+#include "vmime/object.hpp"
+#include "vmime/base.hpp"
+
+#include "vmime/utility/outputStream.hpp"
+#include "vmime/utility/progressListener.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+class socket;
+class timeoutHandler;
+
+
+namespace pop3 {
+
+
+/** A POP3 response, as sent by the server.
+ */
+class POP3Response : public object
+{
+ friend class vmime::creator;
+
+public:
+
+ /** Possible response codes. */
+ enum ResponseCode
+ {
+ CODE_OK = 0,
+ CODE_READY,
+ CODE_ERR
+ };
+
+
+ /** Receive and parse a POP3 response from the
+ * specified socket.
+ *
+ * @param sok socket from which to read
+ * @param toh time-out handler (can be NULL)
+ * @return POP3 response
+ * @throws exceptions::operation_timed_out if no data
+ * has been received within the granted time
+ */
+ static ref <POP3Response> readResponse
+ (ref <socket> sok, ref <timeoutHandler> toh);
+
+ /** Receive and parse a multiline POP3 response from
+ * the specified socket.
+ *
+ * @param sok socket from which to read
+ * @param toh time-out handler (can be NULL)
+ * @return POP3 response
+ * @throws exceptions::operation_timed_out if no data
+ * has been received within the granted time
+ */
+ static ref <POP3Response> readMultilineResponse
+ (ref <socket> sok, ref <timeoutHandler> toh);
+
+ /** Receive and parse a large POP3 response (eg. message data)
+ * from the specified socket.
+ *
+ * @param sok socket from which to read
+ * @param toh time-out handler (can be NULL)
+ * @param os output stream to which response data will be written
+ * @param progress progress listener (can be NULL)
+ * @param predictedSize estimated size of response data (in bytes)
+ * @return POP3 response
+ * @throws exceptions::operation_timed_out if no data
+ * has been received within the granted time
+ */
+ static ref <POP3Response> readLargeResponse
+ (ref <socket> sok, ref <timeoutHandler> toh,
+ utility::outputStream& os,
+ utility::progressListener* progress, const long predictedSize);
+
+
+ /** Returns whether the response is successful ("OK").
+ *
+ * @return true if the response if successful, false otherwise
+ */
+ bool isSuccess() const;
+
+ /** Return the POP3 response code.
+ *
+ * @return response code
+ */
+ ResponseCode getCode() const;
+
+ /** Return the POP3 response text (first line).
+ *
+ * @return response text
+ */
+ const string getText() const;
+
+ /** Return the first POP3 response line.
+ *
+ * @return first response line
+ */
+ const string getFirstLine() const;
+
+ /** Return the response line at the specified position.
+ *
+ * @param pos line index
+ * @return line at the specified index
+ */
+ const string getLineAt(const unsigned int pos) const;
+
+ /** Return the number of lines in the response.
+ *
+ * @return number of lines in the response
+ */
+ unsigned int getLineCount() const;
+
+private:
+
+ POP3Response(ref <socket> sok, ref <timeoutHandler> toh);
+
+ void readResponseImpl(string& buffer, const bool multiLine);
+ void readResponseImpl
+ (string& firstLine, utility::outputStream& os,
+ utility::progressListener* progress, const long predictedSize);
+
+
+ static bool stripFirstLine(const string& buffer, string& result, string* firstLine);
+
+ static ResponseCode getResponseCode(const string& buffer);
+
+ static void stripResponseCode(const string& buffer, string& result);
+
+ static bool checkTerminator(string& buffer, const bool multiLine);
+ static bool checkOneTerminator(string& buffer, const string& term);
+
+
+ ref <socket> m_socket;
+ ref <timeoutHandler> m_timeoutHandler;
+
+ string m_firstLine;
+ ResponseCode m_code;
+ string m_text;
+
+ std::vector <string> m_lines;
+};
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3
+
+#endif // VMIME_NET_SMTP_POP3RESPONSE_HPP_INCLUDED
diff --git a/vmime/net/pop3/POP3Store.hpp b/vmime/net/pop3/POP3Store.hpp
index 816e16c3..af115256 100644
--- a/vmime/net/pop3/POP3Store.hpp
+++ b/vmime/net/pop3/POP3Store.hpp
@@ -87,13 +87,6 @@ public:
private:
- enum ResponseCode
- {
- RESPONSE_OK = 0,
- RESPONSE_READY,
- RESPONSE_ERR
- };
-
void authenticate(const messageId& randomMID);
#if VMIME_HAVE_SASL_SUPPORT
void authenticateSASL();
@@ -105,17 +98,7 @@ private:
const std::vector <string> getCapabilities();
- static bool isSuccessResponse(const string& buffer);
- static bool stripFirstLine(const string& buffer, string& result, string* firstLine = NULL);
- static void stripResponseCode(const string& buffer, string& result);
- static int getResponseCode(const string& buffer);
-
void sendRequest(const string& buffer, const bool end = true);
- void readResponse(string& buffer, const bool multiLine, utility::progressListener* progress = NULL);
- void readResponse(utility::outputStream& os, utility::progressListener* progress = NULL, const int predictedSize = 0);
-
- static bool checkTerminator(string& buffer, const bool multiLine);
- static bool checkOneTerminator(string& buffer, const string& term);
void internalDisconnect();
diff --git a/vmime/net/pop3/POP3Utils.hpp b/vmime/net/pop3/POP3Utils.hpp
index 65d5043d..eca053f0 100644
--- a/vmime/net/pop3/POP3Utils.hpp
+++ b/vmime/net/pop3/POP3Utils.hpp
@@ -41,6 +41,9 @@ namespace net {
namespace pop3 {
+class POP3Response;
+
+
class POP3Utils
{
public:
@@ -59,7 +62,7 @@ public:
* data (either UID or size)
*/
static void parseMultiListOrUidlResponse
- (const string& response, std::map <int, string>& result);
+ (ref <POP3Response> response, std::map <int, string>& result);
};