Moved POP3 response receiving and parsing to a separate class.

This commit is contained in:
Vincent Richard 2012-12-06 11:02:31 +01:00
parent 053d2b4ee0
commit 1ba5e8698c
9 changed files with 720 additions and 424 deletions

View File

@ -236,6 +236,7 @@ libvmime_messaging_proto_sources = [
'net/pop3/POP3SStore.cpp', 'net/pop3/POP3SStore.hpp', 'net/pop3/POP3SStore.cpp', 'net/pop3/POP3SStore.hpp',
'net/pop3/POP3Folder.cpp', 'net/pop3/POP3Folder.hpp', 'net/pop3/POP3Folder.cpp', 'net/pop3/POP3Folder.hpp',
'net/pop3/POP3Message.cpp', 'net/pop3/POP3Message.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' 'net/pop3/POP3Utils.cpp', 'net/pop3/POP3Utils.hpp'
] ]
], ],

View File

@ -31,6 +31,7 @@
#include "vmime/net/pop3/POP3Store.hpp" #include "vmime/net/pop3/POP3Store.hpp"
#include "vmime/net/pop3/POP3Message.hpp" #include "vmime/net/pop3/POP3Message.hpp"
#include "vmime/net/pop3/POP3Response.hpp"
#include "vmime/net/pop3/POP3Utils.hpp" #include "vmime/net/pop3/POP3Utils.hpp"
@ -131,19 +132,17 @@ void POP3Folder::open(const int mode, bool failIfModeIsNotAvailable)
{ {
store->sendRequest("STAT"); store->sendRequest("STAT");
string response; ref <POP3Response> response =
store->readResponse(response, false); POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
if (!store->isSuccessResponse(response)) if (!response->isSuccess())
throw exceptions::command_error("STAT", response); throw exceptions::command_error("STAT", response->getFirstLine());
store->stripResponseCode(response, response); std::istringstream iss(response->getText());
std::istringstream iss(response);
iss >> m_messageCount; iss >> m_messageCount;
if (iss.fail()) if (iss.fail())
throw exceptions::invalid_response("STAT", response); throw exceptions::invalid_response("STAT", response->getFirstLine());
m_open = true; m_open = true;
m_mode = mode; m_mode = mode;
@ -167,9 +166,7 @@ void POP3Folder::close(const bool expunge)
if (!expunge) if (!expunge)
{ {
store->sendRequest("RSET"); store->sendRequest("RSET");
POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
string response;
store->readResponse(response, false);
} }
m_open = false; m_open = false;
@ -371,13 +368,11 @@ void POP3Folder::fetchMessages(std::vector <ref <message> >& msg, const int opti
store->sendRequest(command.str()); store->sendRequest(command.str());
// Get the response // Get the response
string response; ref <POP3Response> response =
store->readResponse(response, true, NULL); POP3Response::readMultilineResponse(store->m_socket, store->m_timeoutHandler);
if (store->isSuccessResponse(response)) if (response->isSuccess())
{ {
store->stripFirstLine(response, response, NULL);
// C: LIST // C: LIST
// S: +OK // S: +OK
// S: 1 47548 // S: 1 47548
@ -416,13 +411,11 @@ void POP3Folder::fetchMessages(std::vector <ref <message> >& msg, const int opti
store->sendRequest(command.str()); store->sendRequest(command.str());
// Get the response // Get the response
string response; ref <POP3Response> response =
store->readResponse(response, true, NULL); POP3Response::readMultilineResponse(store->m_socket, store->m_timeoutHandler);
if (store->isSuccessResponse(response)) if (response->isSuccess())
{ {
store->stripFirstLine(response, response, NULL);
// C: UIDL // C: UIDL
// S: +OK // S: +OK
// S: 1 whqtswO00WBw418f9t5JxYwZ // S: 1 whqtswO00WBw418f9t5JxYwZ
@ -472,26 +465,26 @@ void POP3Folder::fetchMessage(ref <message> msg, const int options)
store->sendRequest(command.str()); store->sendRequest(command.str());
// Get the response // Get the response
string response; ref <POP3Response> response =
store->readResponse(response, false, NULL); 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 // C: LIST 2
// S: +OK 2 4242 // S: +OK 2 4242
string::iterator it = response.begin(); string::iterator it = responseText.begin();
while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it;
while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it;
while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it;
if (it != response.end()) if (it != responseText.end())
{ {
int size = 0; int size = 0;
std::istringstream iss(string(it, response.end())); std::istringstream iss(string(it, responseText.end()));
iss >> size; iss >> size;
msg.dynamicCast <POP3Message>()->m_size = size; msg.dynamicCast <POP3Message>()->m_size = size;
@ -510,25 +503,25 @@ void POP3Folder::fetchMessage(ref <message> msg, const int options)
store->sendRequest(command.str()); store->sendRequest(command.str());
// Get the response // Get the response
string response; ref <POP3Response> response =
store->readResponse(response, false, NULL); 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 // C: UIDL 2
// S: +OK 2 QhdPYR:00WBw1Ph7x7 // 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 != responseText.end() && (*it == ' ' || *it == '\t')) ++it;
while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it;
while (it != response.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 = 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()); store->sendRequest(command.str());
string response; ref <POP3Response> response =
store->readResponse(response, false); POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
if (!store->isSuccessResponse(response)) if (!response->isSuccess())
throw exceptions::command_error("DELE", response); throw exceptions::command_error("DELE", response->getFirstLine());
// Update local flags // Update local flags
for (std::map <POP3Message*, int>::iterator it = for (std::map <POP3Message*, int>::iterator it =
@ -649,11 +642,11 @@ void POP3Folder::deleteMessages(const int from, const int to)
store->sendRequest(command.str()); store->sendRequest(command.str());
string response; ref <POP3Response> response =
store->readResponse(response, false); POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
if (!store->isSuccessResponse(response)) if (!response->isSuccess())
throw exceptions::command_error("DELE", response); throw exceptions::command_error("DELE", response->getFirstLine());
} }
// Update local flags // Update local flags
@ -702,11 +695,11 @@ void POP3Folder::deleteMessages(const std::vector <int>& nums)
store->sendRequest(command.str()); store->sendRequest(command.str());
string response; ref <POP3Response> response =
store->readResponse(response, false); POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
if (!store->isSuccessResponse(response)) if (!response->isSuccess())
throw exceptions::command_error("DELE", response); throw exceptions::command_error("DELE", response->getFirstLine());
} }
// Sort message list // Sort message list
@ -799,15 +792,13 @@ void POP3Folder::status(int& count, int& unseen)
store->sendRequest("STAT"); store->sendRequest("STAT");
string response; ref <POP3Response> response =
store->readResponse(response, false); POP3Response::readResponse(store->m_socket, store->m_timeoutHandler);
if (!store->isSuccessResponse(response)) if (!response->isSuccess())
throw exceptions::command_error("STAT", response); throw exceptions::command_error("STAT", response->getFirstLine());
store->stripResponseCode(response, response); std::istringstream iss(response->getText());
std::istringstream iss(response);
iss >> count; iss >> count;
unseen = count; unseen = count;

View File

@ -28,10 +28,12 @@
#include "vmime/net/pop3/POP3Message.hpp" #include "vmime/net/pop3/POP3Message.hpp"
#include "vmime/net/pop3/POP3Response.hpp"
#include "vmime/net/pop3/POP3Folder.hpp" #include "vmime/net/pop3/POP3Folder.hpp"
#include "vmime/net/pop3/POP3Store.hpp" #include "vmime/net/pop3/POP3Store.hpp"
#include "vmime/utility/outputStreamAdapter.hpp" #include "vmime/utility/outputStreamAdapter.hpp"
#include "vmime/utility/outputStreamStringAdapter.hpp"
#include <sstream> #include <sstream>
@ -140,12 +142,14 @@ void POP3Message::extract(utility::outputStream& os,
std::ostringstream oss; std::ostringstream oss;
oss << "RETR " << m_num; 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 try
{ {
folder.constCast <POP3Folder>()->m_store.acquire()-> POP3Response::readLargeResponse
readResponse(os, progress, m_size); (store->m_socket, store->m_timeoutHandler, os, progress, m_size);
} }
catch (exceptions::command_error& e) catch (exceptions::command_error& e)
{ {
@ -197,12 +201,17 @@ void POP3Message::fetch(ref <POP3Folder> msgFolder, const int options)
std::ostringstream oss; std::ostringstream oss;
oss << "TOP " << m_num << " 0"; oss << "TOP " << m_num << " 0";
folder->m_store.acquire()->sendRequest(oss.str()); ref <POP3Store> store = folder->m_store.acquire();
store->sendRequest(oss.str());
try try
{ {
string buffer; 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 = vmime::create <header>();
m_header->parse(buffer); m_header->parse(buffer);

View File

@ -0,0 +1,426 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2009 Vincent Richard <vincent@vincent-richard.net>
//
// 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

View File

@ -29,14 +29,12 @@
#include "vmime/net/pop3/POP3Store.hpp" #include "vmime/net/pop3/POP3Store.hpp"
#include "vmime/net/pop3/POP3Folder.hpp" #include "vmime/net/pop3/POP3Folder.hpp"
#include "vmime/net/pop3/POP3Response.hpp"
#include "vmime/exception.hpp" #include "vmime/exception.hpp"
#include "vmime/platform.hpp" #include "vmime/platform.hpp"
#include "vmime/messageId.hpp" #include "vmime/messageId.hpp"
#include "vmime/security/digest/messageDigestFactory.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" #include "vmime/net/defaultConnectionInfos.hpp"
@ -174,13 +172,12 @@ void POP3Store::connect()
// eg: C: <connection to server> // eg: C: <connection to server>
// --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <36938848.1056800841.634@somewhere.com> // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <36938848.1056800841.634@somewhere.com>
string response; ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler);
readResponse(response, false);
if (!isSuccessResponse(response)) if (!response->isSuccess())
{ {
internalDisconnect(); internalDisconnect();
throw exceptions::connection_greeting_error(response); throw exceptions::connection_greeting_error(response->getFirstLine());
} }
#if VMIME_HAVE_TLS_SUPPORT #if VMIME_HAVE_TLS_SUPPORT
@ -217,7 +214,7 @@ void POP3Store::connect()
#endif // VMIME_HAVE_TLS_SUPPORT #endif // VMIME_HAVE_TLS_SUPPORT
// Start authentication process // 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 username = getAuthenticator()->getUsername();
const string password = getAuthenticator()->getPassword(); const string password = getAuthenticator()->getPassword();
string response; ref <POP3Response> response;
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP)) if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP))
{ {
@ -280,9 +277,9 @@ void POP3Store::authenticate(const messageId& randomMID)
md5->finalize(); md5->finalize();
sendRequest("APOP " + username + " " + md5->getHexDigest()); sendRequest("APOP " + username + " " + md5->getHexDigest());
readResponse(response, false); response = POP3Response::readResponse(m_socket, m_timeoutHandler);
if (isSuccessResponse(response)) if (response->isSuccess())
{ {
m_authentified = true; m_authentified = true;
return; return;
@ -302,20 +299,19 @@ void POP3Store::authenticate(const messageId& randomMID)
{ {
// Can't fallback on basic authentication // Can't fallback on basic authentication
internalDisconnect(); internalDisconnect();
throw exceptions::authentication_error(response); throw exceptions::authentication_error(response->getFirstLine());
} }
// Ensure connection is valid (cf. note above) // Ensure connection is valid (cf. note above)
try try
{ {
string response2;
sendRequest("NOOP"); sendRequest("NOOP");
readResponse(response2, false); POP3Response::readResponse(m_socket, m_timeoutHandler);
} }
catch (exceptions::socket_exception&) catch (exceptions::socket_exception&)
{ {
internalDisconnect(); 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 // C: PASS couic
// S: +OK vincent's maildrop has 2 messages (320 octets) // S: +OK vincent's maildrop has 2 messages (320 octets)
sendRequest("USER " + username); sendRequest("USER " + username);
readResponse(response, false); response = POP3Response::readResponse(m_socket, m_timeoutHandler);
if (!isSuccessResponse(response)) if (!response->isSuccess())
{ {
internalDisconnect(); internalDisconnect();
throw exceptions::authentication_error(response); throw exceptions::authentication_error(response->getFirstLine());
} }
sendRequest("PASS " + password); sendRequest("PASS " + password);
readResponse(response, false); response = POP3Response::readResponse(m_socket, m_timeoutHandler);
if (!isSuccessResponse(response)) if (!response->isSuccess())
{ {
internalDisconnect(); internalDisconnect();
throw exceptions::authentication_error(response); throw exceptions::authentication_error(response->getFirstLine());
} }
m_authentified = true; m_authentified = true;
@ -453,17 +449,16 @@ void POP3Store::authenticateSASL()
for (bool cont = true ; cont ; ) for (bool cont = true ; cont ; )
{ {
string response; ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler);
readResponse(response, false);
switch (getResponseCode(response)) switch (response->getCode())
{ {
case RESPONSE_OK: case POP3Response::CODE_OK:
{ {
m_socket = saslSession->getSecuredSocket(m_socket); m_socket = saslSession->getSecuredSocket(m_socket);
return; return;
} }
case RESPONSE_READY: case POP3Response::CODE_READY:
{ {
byte_t* challenge = 0; byte_t* challenge = 0;
long challengeLen = 0; long challengeLen = 0;
@ -474,8 +469,7 @@ void POP3Store::authenticateSASL()
try try
{ {
// Extract challenge // Extract challenge
stripResponseCode(response, response); saslContext->decodeB64(response->getText(), &challenge, &challengeLen);
saslContext->decodeB64(response, &challenge, &challengeLen);
// Prepare response // Prepare response
saslSession->evaluateChallenge saslSession->evaluateChallenge
@ -543,11 +537,10 @@ void POP3Store::startTLS()
{ {
sendRequest("STLS"); sendRequest("STLS");
string response; ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler);
readResponse(response, false);
if (getResponseCode(response) != RESPONSE_OK) if (!response->isSuccess())
throw exceptions::command_error("STLS", response); throw exceptions::command_error("STLS", response->getFirstLine());
ref <tls::TLSSession> tlsSession = ref <tls::TLSSession> tlsSession =
tls::TLSSession::create(getCertificateVerifier()); tls::TLSSession::create(getCertificateVerifier());
@ -641,11 +634,11 @@ void POP3Store::noop()
{ {
sendRequest("NOOP"); sendRequest("NOOP");
string response; ref <POP3Response> response =
readResponse(response, false); POP3Response::readResponse(m_socket, m_timeoutHandler);
if (!isSuccessResponse(response)) if (!response->isSuccess())
throw exceptions::command_error("NOOP", response); throw exceptions::command_error("NOOP", response->getFirstLine());
} }
@ -653,89 +646,21 @@ const std::vector <string> POP3Store::getCapabilities()
{ {
sendRequest("CAPA"); sendRequest("CAPA");
string response; ref <POP3Response> response =
readResponse(response, true); POP3Response::readMultilineResponse(m_socket, m_timeoutHandler);
std::vector <string> res; std::vector <string> res;
if (isSuccessResponse(response)) if (response->isSuccess())
{ {
stripFirstLine(response, response); for (unsigned int i = 0, n = response->getLineCount() ; i < n ; ++i)
res.push_back(response->getLineAt(i));
std::istringstream iss(response);
string line;
while (std::getline(iss, line, '\n'))
res.push_back(utility::stringUtils::trim(line));
} }
return res; 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) void POP3Store::sendRequest(const string& buffer, const bool end)
{ {
if (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) void POP3Store::registerFolder(POP3Folder* folder)
{ {
m_folders.push_back(folder); m_folders.push_back(folder);

View File

@ -28,6 +28,7 @@
#include "vmime/net/pop3/POP3Utils.hpp" #include "vmime/net/pop3/POP3Utils.hpp"
#include "vmime/net/pop3/POP3Response.hpp"
#include <sstream> #include <sstream>
@ -38,15 +39,13 @@ namespace pop3 {
// static // 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; std::map <int, string> ids;
string line; for (unsigned int i = 0, n = response->getLineCount() ; i < n ; ++i)
while (std::getline(iss, line))
{ {
string line = response->getLineAt(i);
string::iterator it = line.begin(); string::iterator it = line.begin();
while (it != line.end() && (*it == ' ' || *it == '\t')) while (it != line.end() && (*it == ' ' || *it == '\t'))

View File

@ -0,0 +1,186 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2009 Vincent Richard <vincent@vincent-richard.net>
//
// 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

View File

@ -87,13 +87,6 @@ public:
private: private:
enum ResponseCode
{
RESPONSE_OK = 0,
RESPONSE_READY,
RESPONSE_ERR
};
void authenticate(const messageId& randomMID); void authenticate(const messageId& randomMID);
#if VMIME_HAVE_SASL_SUPPORT #if VMIME_HAVE_SASL_SUPPORT
void authenticateSASL(); void authenticateSASL();
@ -105,17 +98,7 @@ private:
const std::vector <string> getCapabilities(); 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 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(); void internalDisconnect();

View File

@ -41,6 +41,9 @@ namespace net {
namespace pop3 { namespace pop3 {
class POP3Response;
class POP3Utils class POP3Utils
{ {
public: public:
@ -59,7 +62,7 @@ public:
* data (either UID or size) * data (either UID or size)
*/ */
static void parseMultiListOrUidlResponse static void parseMultiListOrUidlResponse
(const string& response, std::map <int, string>& result); (ref <POP3Response> response, std::map <int, string>& result);
}; };