Implemented IMAP multi-fetching.

This commit is contained in:
Vincent Richard 2006-01-15 11:06:59 +00:00
parent 09d4fca9ba
commit 6c946267b1
5 changed files with 188 additions and 85 deletions

View File

@ -2,6 +2,11 @@
VERSION 0.8.1cvs
================
2006-01-15 Vincent Richard <vincent@vincent-richard.net>
* IMAP: implemented multi-fetching. Now using "FETCH x:y" instead of
sending (y-x+1) "FETCH" requests.
2005-12-26 Vincent Richard <vincent@vincent-richard.net>
* posixSocket.cpp: use getaddrinfo() if available. This should bring

View File

@ -609,19 +609,79 @@ void IMAPFolder::fetchMessages(std::vector <ref <message> >& msg, const int opti
else if (!isOpen())
throw exceptions::illegal_state("Folder not open");
// Build message numbers list
std::vector <int> list;
list.reserve(msg.size());
std::map <int, ref <IMAPMessage> > numberToMsg;
for (std::vector <ref <message> >::iterator it = msg.begin() ; it != msg.end() ; ++it)
{
list.push_back((*it)->getNumber());
numberToMsg[(*it)->getNumber()] = (*it).dynamicCast <IMAPMessage>();
}
// Send the request
const string command = IMAPUtils::buildFetchRequest(list, options);
m_connection->send(true, command, true);
// Get the response
utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
if (resp->isBad() || resp->response_done()->response_tagged()->
resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
{
throw exceptions::command_error("FETCH",
m_connection->getParser()->lastLine(), "bad response");
}
const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
resp->continue_req_or_response_data();
const int total = msg.size();
int current = 0;
if (progress)
progress->start(total);
for (std::vector <ref <message> >::iterator it = msg.begin() ;
it != msg.end() ; ++it)
try
{
(*it).dynamicCast <IMAPMessage>()->fetch(this, options);
for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
it = respDataList.begin() ; it != respDataList.end() ; ++it)
{
if ((*it)->response_data() == NULL)
{
throw exceptions::command_error("FETCH",
m_connection->getParser()->lastLine(), "invalid response");
}
const IMAPParser::message_data* messageData =
(*it)->response_data()->message_data();
// We are only interested in responses of type "FETCH"
if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH)
continue;
// Process fetch response for this message
const int num = static_cast <int>(messageData->number());
std::map <int, ref <IMAPMessage> >::iterator msg = numberToMsg.find(num);
if (msg != numberToMsg.end())
{
(*msg).second->processFetchResponse(options, messageData->msg_att());
if (progress)
progress->progress(++current, total);
}
}
}
catch (...)
{
if (progress)
progress->progress(++current, total);
progress->stop(total);
throw;
}
if (progress)

View File

@ -463,80 +463,13 @@ void IMAPMessage::fetch(IMAPFolder* folder, const int options)
if (m_folder != folder)
throw exceptions::folder_not_found();
// TODO: optimization: send the request for multiple
// messages at the same time (FETCH x:y)
// Example:
// C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
// S: * 2 FETCH ....
// S: * 3 FETCH ....
// S: * 4 FETCH ....
// S: A654 OK FETCH completed
std::vector <string> items;
if (options & folder::FETCH_SIZE)
items.push_back("RFC822.SIZE");
if (options & folder::FETCH_FLAGS)
items.push_back("FLAGS");
if (options & folder::FETCH_STRUCTURE)
items.push_back("BODYSTRUCTURE");
if (options & folder::FETCH_UID)
items.push_back("UID");
if (options & folder::FETCH_FULL_HEADER)
items.push_back("RFC822.HEADER");
else
{
if (options & folder::FETCH_ENVELOPE)
items.push_back("ENVELOPE");
std::vector <string> headerFields;
if (options & folder::FETCH_CONTENT_INFO)
headerFields.push_back("CONTENT_TYPE");
if (options & folder::FETCH_IMPORTANCE)
{
headerFields.push_back("IMPORTANCE");
headerFields.push_back("X-PRIORITY");
}
if (!headerFields.empty())
{
string list;
for (std::vector <string>::iterator it = headerFields.begin() ;
it != headerFields.end() ; ++it)
{
if (it != headerFields.begin())
list += " ";
list += *it;
}
items.push_back("BODY[HEADER.FIELDS (" + list + ")]");
}
}
// Build the request text
std::ostringstream command;
command << "FETCH " << m_num << " (";
for (std::vector <string>::const_iterator it = items.begin() ;
it != items.end() ; ++it)
{
if (it != items.begin()) command << " ";
command << *it;
}
command << ")";
// Send the request
m_folder->m_connection->send(true, command.str(), true);
std::vector <int> list;
list.push_back(m_num);
const string command = IMAPUtils::buildFetchRequest(list, options);
m_folder->m_connection->send(true, command, true);
// Get the response
utility::auto_ptr <IMAPParser::response> resp(m_folder->m_connection->readResponse());

View File

@ -19,6 +19,7 @@
#include "vmime/net/imap/IMAPUtils.hpp"
#include "vmime/net/message.hpp"
#include "vmime/net/folder.hpp"
#include <sstream>
#include <iterator>
@ -30,6 +31,7 @@ namespace net {
namespace imap {
// static
const string IMAPUtils::quoteString(const string& text)
{
//
@ -405,13 +407,7 @@ const string IMAPUtils::messageFlagList(const int flags)
}
// This function builds a "IMAP set" given a list. Try to group consecutive
// message numbers to reduce the list.
//
// Example:
// IN = "1,2,3,4,5,7,8,13,15,16,17"
// OUT = "1:5,7:8,13,15:*" for a mailbox with a total of 17 messages (max = 17)
// static
const string IMAPUtils::listToSet(const std::vector <int>& list, const int max,
const bool alreadySorted)
{
@ -483,6 +479,7 @@ const string IMAPUtils::listToSet(const std::vector <int>& list, const int max,
}
// static
const string IMAPUtils::dateTime(const vmime::datetime& date)
{
std::ostringstream res;
@ -550,6 +547,82 @@ const string IMAPUtils::dateTime(const vmime::datetime& date)
}
// static
const string IMAPUtils::buildFetchRequest(const std::vector <int>& list, const int options)
{
// Example:
// C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
// S: * 2 FETCH ....
// S: * 3 FETCH ....
// S: * 4 FETCH ....
// S: A654 OK FETCH completed
std::vector <string> items;
if (options & folder::FETCH_SIZE)
items.push_back("RFC822.SIZE");
if (options & folder::FETCH_FLAGS)
items.push_back("FLAGS");
if (options & folder::FETCH_STRUCTURE)
items.push_back("BODYSTRUCTURE");
if (options & folder::FETCH_UID)
items.push_back("UID");
if (options & folder::FETCH_FULL_HEADER)
items.push_back("RFC822.HEADER");
else
{
if (options & folder::FETCH_ENVELOPE)
items.push_back("ENVELOPE");
std::vector <string> headerFields;
if (options & folder::FETCH_CONTENT_INFO)
headerFields.push_back("CONTENT_TYPE");
if (options & folder::FETCH_IMPORTANCE)
{
headerFields.push_back("IMPORTANCE");
headerFields.push_back("X-PRIORITY");
}
if (!headerFields.empty())
{
string list;
for (std::vector <string>::iterator it = headerFields.begin() ;
it != headerFields.end() ; ++it)
{
if (it != headerFields.begin())
list += " ";
list += *it;
}
items.push_back("BODY[HEADER.FIELDS (" + list + ")]");
}
}
// Build the request text
std::ostringstream command;
command << "FETCH " << listToSet(list, -1, false) << " (";
for (std::vector <string>::const_iterator it = items.begin() ;
it != items.end() ; ++it)
{
if (it != items.begin()) command << " ";
command << *it;
}
command << ")";
return command.str();
}
} // imap
} // net
} // vmime

View File

@ -49,6 +49,11 @@ public:
static const string toModifiedUTF7(const char hierarchySeparator, const folder::path::component& text);
static const folder::path::component fromModifiedUTF7(const string& text);
/** Quote string if it contains IMAP-special characters.
*
* @param text string to quote
* @return quoted string
*/
static const string quoteString(const string& text);
static const int folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list);
@ -58,9 +63,36 @@ public:
static const string messageFlagList(const int flags);
static const string listToSet(const std::vector <int>& list, const int max = -1, const bool alreadySorted = false);
/** Build an "IMAP set" given a list. The function tries to group
* consecutive message numbers to reduce the list.
*
* Example:
* IN = "1,2,3,4,5,7,8,13,15,16,17"
* OUT = "1:5,7:8,13,15:*" for a mailbox with a total of 17 messages (max = 17)
*
* @param list list of message numbers
* @param max number of messages in the mailbox (or -1 if not known)
* @param alreadySorted set to true if the list of message numbers is
* already sorted in ascending order
* @return a set corresponding to the message list
*/
static const string listToSet(const std::vector <int>& list,
const int max = -1, const bool alreadySorted = false);
/** Format a date/time to IMAP date/time format.
*
* @param date date/time to format
* @return IMAP-formatted date/time
*/
static const string dateTime(const vmime::datetime& date);
/** Construct a fetch request for the specified messages.
*
* @param list list of message numbers
* @param options fetch options
* @return fetch request
*/
static const string buildFetchRequest(const std::vector <int>& list, const int options);
};