Added functions to get messages by UID (IMAP only for now).

This commit is contained in:
Vincent Richard 2012-07-28 13:01:48 +02:00
parent 891aba49ff
commit a68cebf12a
11 changed files with 301 additions and 27 deletions

View File

@ -208,7 +208,7 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable)
{
case IMAPParser::resp_text_code::UIDVALIDITY:
m_uidValidity = code->nz_number()->value();
m_uidValidity = static_cast <unsigned int>(code->nz_number()->value());
break;
default:
@ -550,6 +550,109 @@ std::vector <ref <message> > IMAPFolder::getMessages(const std::vector <int>& nu
}
ref <message> IMAPFolder::getMessageByUID(const message::uid& uid)
{
std::vector <message::uid> uids;
uids.push_back(uid);
std::vector <ref <message> > msgs = getMessagesByUID(uids);
if (msgs.size() == 0)
throw exceptions::message_not_found();
return msgs[0];
}
std::vector <ref <message> > IMAPFolder::getMessagesByUID(const std::vector <message::uid>& uids)
{
if (!isOpen())
throw exceptions::illegal_state("Folder not open");
if (uids.size() == 0)
return std::vector <ref <message> >();
// C: . UID FETCH uuuu1,uuuu2,uuuu3 UID
// S: * nnnn1 FETCH (UID uuuu1)
// S: * nnnn2 FETCH (UID uuuu2)
// S: * nnnn3 FETCH (UID uuuu3)
// S: . OK UID FETCH completed
// Prepare command and arguments
std::ostringstream cmd;
cmd.imbue(std::locale::classic());
cmd << "UID FETCH " << IMAPUtils::extractUIDFromGlobalUID(uids[0]);
for (unsigned int i = 1, n = uids.size() ; i < n ; ++i)
cmd << "," << IMAPUtils::extractUIDFromGlobalUID(uids[i]);
cmd << " UID";
// Send the request
m_connection->send(true, cmd.str(), 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("UID FETCH ... UID", m_connection->getParser()->lastLine(), "bad response");
}
// Process the response
const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
resp->continue_req_or_response_data();
std::vector <ref <message> > messages;
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("UID FETCH ... UID",
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;
// Get Process fetch response for this message
const int msgNum = static_cast <int>(messageData->number());
message::uid msgUID, msgFullUID;
// Find UID in message attributes
const std::vector <IMAPParser::msg_att_item*> atts = messageData->msg_att()->items();
for (std::vector <IMAPParser::msg_att_item*>::const_iterator
it = atts.begin() ; it != atts.end() ; ++it)
{
if ((*it)->type() == IMAPParser::msg_att_item::UID)
{
msgFullUID = IMAPUtils::makeGlobalUID(m_uidValidity, (*it)->unique_id()->value());
msgUID = (*it)->unique_id()->value();
break;
}
}
if (!msgUID.empty())
{
ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
messages.push_back(vmime::create <IMAPMessage>(thisFolder, msgNum, msgFullUID));
}
}
return messages;
}
int IMAPFolder::getMessageCount()
{
if (!isOpen())
@ -730,7 +833,7 @@ void IMAPFolder::fetchMessages(std::vector <ref <message> >& msg, const int opti
if (msg != numberToMsg.end())
{
(*msg).second->processFetchResponse(options, messageData->msg_att());
(*msg).second->processFetchResponse(options, messageData);
if (progress)
progress->progress(++current, total);
@ -1781,7 +1884,7 @@ std::vector <int> IMAPFolder::getMessageNumbersStartingOnUID(const message::uid&
std::ostringstream command;
command.imbue(std::locale::classic());
command << "SEARCH UID " << uid;
command << "SEARCH UID " << uid << ":*";
// Send the request
m_connection->send(true, command.str(), true);

View File

@ -98,6 +98,14 @@ IMAPMessage::IMAPMessage(ref <IMAPFolder> folder, const int num)
}
IMAPMessage::IMAPMessage(ref <IMAPFolder> folder, const int num, const uid& uniqueId)
: m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED),
m_expunged(false), m_uid(uniqueId), m_structure(NULL)
{
folder->registerMessage(this);
}
IMAPMessage::~IMAPMessage()
{
ref <IMAPFolder> folder = m_folder.acquire();
@ -271,7 +279,11 @@ void IMAPMessage::extract(ref <const part> p, utility::outputStream& os,
std::ostringstream command;
command.imbue(std::locale::classic());
command << "FETCH " << m_num << " BODY";
if (m_uid.empty())
command << "FETCH " << m_num << " BODY";
else
command << "UID FETCH " << IMAPUtils::extractUIDFromGlobalUID(m_uid) << " BODY";
if (peek) command << ".PEEK";
command << "[";
@ -361,19 +373,18 @@ void IMAPMessage::fetch(ref <IMAPFolder> msgFolder, const int options)
continue;
// Process fetch response for this message
processFetchResponse(options, messageData->msg_att());
processFetchResponse(options, messageData);
}
}
void IMAPMessage::processFetchResponse
(const int options, const IMAPParser::msg_att* msgAtt)
(const int options, const IMAPParser::message_data* msgData)
{
ref <IMAPFolder> folder = m_folder.acquire();
// Get message attributes
const std::vector <IMAPParser::msg_att_item*> atts =
msgAtt->items();
const std::vector <IMAPParser::msg_att_item*> atts = msgData->msg_att()->items();
int flags = 0;
@ -389,12 +400,7 @@ void IMAPMessage::processFetchResponse
}
case IMAPParser::msg_att_item::UID:
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << folder->m_uidValidity << ":" << (*it)->unique_id()->value();
m_uid = oss.str();
m_uid = IMAPUtils::makeGlobalUID(folder->m_uidValidity, (*it)->unique_id()->value());
break;
}
case IMAPParser::msg_att_item::ENVELOPE:

View File

@ -539,6 +539,24 @@ const string IMAPUtils::listToSet(const std::vector <int>& list, const int max,
}
// static
const string IMAPUtils::listToSet(const std::vector <message::uid>& list)
{
if (list.size() == 0)
return "";
std::ostringstream res;
res.imbue(std::locale::classic());
res << extractUIDFromGlobalUID(list[0]);
for (unsigned int i = 1, n = list.size() ; i < n ; ++i)
res << "," << extractUIDFromGlobalUID(list[i]);
return res.str();
}
// static
const string IMAPUtils::dateTime(const vmime::datetime& date)
{
@ -609,7 +627,8 @@ const string IMAPUtils::dateTime(const vmime::datetime& date)
// static
const string IMAPUtils::buildFetchRequest(const std::vector <int>& list, const int options)
const string IMAPUtils::buildFetchRequestImpl
(const std::string& mode, const std::string& set, const int options)
{
// Example:
// C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
@ -671,7 +690,10 @@ const string IMAPUtils::buildFetchRequest(const std::vector <int>& list, const i
std::ostringstream command;
command.imbue(std::locale::classic());
command << "FETCH " << listToSet(list, -1, false) << " (";
if (mode == "uid")
command << "UID FETCH " << set << " (";
else
command << "FETCH " << set << " (";
for (std::vector <string>::const_iterator it = items.begin() ;
it != items.end() ; ++it)
@ -686,6 +708,20 @@ const string IMAPUtils::buildFetchRequest(const std::vector <int>& list, const i
}
// static
const string IMAPUtils::buildFetchRequest(const std::vector <int>& list, const int options)
{
return buildFetchRequestImpl("number", listToSet(list, -1, false), options);
}
// static
const string IMAPUtils::buildFetchRequest(const std::vector <message::uid>& list, const int options)
{
return buildFetchRequestImpl("uid", listToSet(list), options);
}
// static
void IMAPUtils::convertAddressList
(const IMAPParser::address_list& src, mailboxList& dest)
@ -706,6 +742,46 @@ void IMAPUtils::convertAddressList
}
// static
unsigned int IMAPUtils::extractUIDFromGlobalUID(const message::uid& uid)
{
message::uid::size_type colonPos = uid.find(':');
if (colonPos == message::uid::npos)
{
std::istringstream iss(uid);
iss.imbue(std::locale::classic());
unsigned int n = 0;
iss >> n;
return n;
}
else
{
std::istringstream iss(uid.substr(colonPos + 1));
iss.imbue(std::locale::classic());
unsigned int n = 0;
iss >> n;
return n;
}
}
// static
const message::uid IMAPUtils::makeGlobalUID(const unsigned int UIDValidity, const unsigned int messageUID)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << UIDValidity << ":" << messageUID;
return message::uid(oss.str());
}
} // imap
} // net
} // vmime

View File

@ -444,6 +444,18 @@ std::vector <ref <message> > maildirFolder::getMessages(const std::vector <int>&
}
ref <message> maildirFolder::getMessageByUID(const message::uid& /* uid */)
{
throw exceptions::operation_not_supported();
}
std::vector <ref <message> > maildirFolder::getMessagesByUID(const std::vector <message::uid>& /* uids */)
{
throw exceptions::operation_not_supported();
}
int maildirFolder::getMessageCount()
{
return (m_messageCount);

View File

@ -249,6 +249,18 @@ std::vector <ref <message> > POP3Folder::getMessages(const int from, const int t
}
ref <message> POP3Folder::getMessageByUID(const message::uid& /* uid */)
{
throw exceptions::operation_not_supported();
}
std::vector <ref <message> > POP3Folder::getMessagesByUID(const std::vector <message::uid>& /* uids */)
{
throw exceptions::operation_not_supported();
}
std::vector <ref <message> > POP3Folder::getMessages(const std::vector <int>& nums)
{
ref <POP3Store> store = m_store.acquire();

View File

@ -169,7 +169,7 @@ public:
*/
virtual bool isOpen() const = 0;
/** Get a new reference to a message in this folder.
/** Get a new reference to a message in this folder, given its number.
*
* @param num message sequence number
* @return a new object referencing the specified message
@ -177,7 +177,7 @@ public:
*/
virtual ref <message> getMessage(const int num) = 0;
/** Get new references to messages in this folder.
/** Get new references to messages in this folder, given their numbers.
*
* @param from sequence number of the first message to get
* @param to sequence number of the last message to get
@ -186,14 +186,30 @@ public:
*/
virtual std::vector <ref <message> > getMessages(const int from = 1, const int to = -1) = 0;
/** Get new references to messages in this folder.
/** Get new references to messages in this folder, given their numbers.
*
* @param nums sequence numbers of the messages to delete
* @param nums sequence numbers of the messages to retrieve
* @return new objects referencing the specified messages
* @throw net_exception if an error occurs
*/
virtual std::vector <ref <message> > getMessages(const std::vector <int>& nums) = 0;
/** Get message in this folder, given its UID.
*
* @param uid UID of message to retrieve
* @return a new object referencing the specified message
* @throw net_exception if an error occurs
*/
virtual ref <message> getMessageByUID(const message::uid& uid) = 0;
/** Get messages in this folder, given their UIDs.
*
* @param uids UIDs of messages to retrieve
* @return new objects referencing the specified messages
* @throw net_exception if an error occurs
*/
virtual std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids) = 0;
/** Return the number of messages in this folder.
*
* @return number of messages in the folder

View File

@ -84,6 +84,12 @@ public:
ref <message> getMessage(const int num);
std::vector <ref <message> > getMessages(const int from = 1, const int to = -1);
std::vector <ref <message> > getMessages(const std::vector <int>& nums);
ref <message> getMessageByUID(const message::uid& uid);
std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids);
std::vector <int> getMessageNumbersStartingOnUID(const message::uid& uid);
int getMessageCount();
ref <folder> getFolder(const folder::path::component& name);
@ -120,8 +126,6 @@ public:
int getFetchCapabilities() const;
std::vector <int> getMessageNumbersStartingOnUID(const message::uid& uid);
private:
void registerMessage(IMAPMessage* msg);
@ -152,7 +156,7 @@ private:
int m_messageCount;
int m_uidValidity;
unsigned int m_uidValidity;
std::vector <IMAPMessage*> m_messages;
};

View File

@ -50,6 +50,7 @@ private:
friend class vmime::creator; // vmime::create <IMAPMessage>
IMAPMessage(ref <IMAPFolder> folder, const int num);
IMAPMessage(ref <IMAPFolder> folder, const int num, const uid& uniqueId);
IMAPMessage(const IMAPMessage&) : message() { }
~IMAPMessage();
@ -83,7 +84,7 @@ private:
void fetch(ref <IMAPFolder> folder, const int options);
void processFetchResponse(const int options, const IMAPParser::msg_att* msgAtt);
void processFetchResponse(const int options, const IMAPParser::message_data* msgData);
/** Recursively fetch part header for all parts in the structure.
*

View File

@ -29,6 +29,7 @@
#include "vmime/dateTime.hpp"
#include "vmime/net/folder.hpp"
#include "vmime/net/message.hpp"
#include "vmime/net/imap/IMAPParser.hpp"
#include "vmime/mailboxList.hpp"
@ -65,8 +66,8 @@ public:
static const string messageFlagList(const int flags);
/** Build an "IMAP set" given a list. The function tries to group
* consecutive message numbers to reduce the list.
/** Build an "IMAP set" given a list of message numbers. 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"
@ -81,6 +82,13 @@ public:
static const string listToSet(const std::vector <int>& list,
const int max = -1, const bool alreadySorted = false);
/** Build an "IMAP set" set given a list of message UIDs.
*
* @param list list of message UIDs
* @return a set corresponding to the list
*/
static const string listToSet(const std::vector <message::uid>& list);
/** Format a date/time to IMAP date/time format.
*
* @param date date/time to format
@ -88,7 +96,7 @@ public:
*/
static const string dateTime(const vmime::datetime& date);
/** Construct a fetch request for the specified messages.
/** Construct a fetch request for the specified messages, designated by their sequence numbers.
*
* @param list list of message numbers
* @param options fetch options
@ -96,12 +104,40 @@ public:
*/
static const string buildFetchRequest(const std::vector <int>& list, const int options);
/** Construct a fetch request for the specified messages, designated by their UIDs.
*
* @param list list of message UIDs
* @param options fetch options
* @return fetch request
*/
static const string buildFetchRequest(const std::vector <message::uid>& list, const int options);
/** Convert a parser-style address list to a mailbox list.
*
* @param src input address list
* @param dest output mailbox list
*/
static void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest);
/** Extract the message UID from a globally unique UID.
*
* @param uid globally unique UID (as returned by makeGlobalUID(), for example)
* @return message UID
*/
static unsigned int extractUIDFromGlobalUID(const message::uid& uid);
/** Construct a globally unique UID from UID Validity and a message UID.
*
* @param UIDValidity UID Validity of the folder
* @param messageUID UID of the message
* @return global UID
*/
static const message::uid makeGlobalUID(const unsigned int UIDValidity, const unsigned int messageUID);
private:
static const string buildFetchRequestImpl
(const std::string& mode, const std::string& set, const int options);
};

View File

@ -85,6 +85,10 @@ public:
ref <message> getMessage(const int num);
std::vector <ref <message> > getMessages(const int from = 1, const int to = -1);
std::vector <ref <message> > getMessages(const std::vector <int>& nums);
ref <message> getMessageByUID(const message::uid& uid);
std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids);
int getMessageCount();
ref <folder> getFolder(const folder::path::component& name);

View File

@ -83,6 +83,10 @@ public:
ref <message> getMessage(const int num);
std::vector <ref <message> > getMessages(const int from = 1, const int to = -1);
std::vector <ref <message> > getMessages(const std::vector <int>& nums);
ref <message> getMessageByUID(const message::uid& uid);
std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids);
int getMessageCount();
ref <folder> getFolder(const folder::path::component& name);