Moved POP3 response receiving and parsing to a separate class.
This commit is contained in:
parent
053d2b4ee0
commit
1ba5e8698c
@ -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'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
426
src/net/pop3/POP3Response.cpp
Normal file
426
src/net/pop3/POP3Response.cpp
Normal 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
|
@ -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);
|
||||||
|
@ -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'))
|
||||||
|
186
vmime/net/pop3/POP3Response.hpp
Normal file
186
vmime/net/pop3/POP3Response.hpp
Normal 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
|
@ -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();
|
||||||
|
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user