From a68cebf12a775f43b39f8b46851644699320f487 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sat, 28 Jul 2012 13:01:48 +0200 Subject: [PATCH] Added functions to get messages by UID (IMAP only for now). --- src/net/imap/IMAPFolder.cpp | 109 +++++++++++++++++++++++++++- src/net/imap/IMAPMessage.cpp | 28 ++++--- src/net/imap/IMAPUtils.cpp | 80 +++++++++++++++++++- src/net/maildir/maildirFolder.cpp | 12 +++ src/net/pop3/POP3Folder.cpp | 12 +++ vmime/net/folder.hpp | 24 +++++- vmime/net/imap/IMAPFolder.hpp | 10 ++- vmime/net/imap/IMAPMessage.hpp | 3 +- vmime/net/imap/IMAPUtils.hpp | 42 ++++++++++- vmime/net/maildir/maildirFolder.hpp | 4 + vmime/net/pop3/POP3Folder.hpp | 4 + 11 files changed, 301 insertions(+), 27 deletions(-) diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index 81bf3861..3d8c17ea 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -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 (code->nz_number()->value()); break; default: @@ -550,6 +550,109 @@ std::vector > IMAPFolder::getMessages(const std::vector & nu } +ref IMAPFolder::getMessageByUID(const message::uid& uid) +{ + std::vector uids; + uids.push_back(uid); + + std::vector > msgs = getMessagesByUID(uids); + + if (msgs.size() == 0) + throw exceptions::message_not_found(); + + return msgs[0]; +} + + +std::vector > IMAPFolder::getMessagesByUID(const std::vector & uids) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (uids.size() == 0) + return std::vector >(); + + // 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 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 & respDataList = + resp->continue_req_or_response_data(); + + std::vector > messages; + + for (std::vector ::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 (messageData->number()); + message::uid msgUID, msgFullUID; + + // Find UID in message attributes + const std::vector atts = messageData->msg_att()->items(); + + for (std::vector ::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 thisFolder = thisRef().dynamicCast (); + messages.push_back(vmime::create (thisFolder, msgNum, msgFullUID)); + } + } + + return messages; +} + + int IMAPFolder::getMessageCount() { if (!isOpen()) @@ -730,7 +833,7 @@ void IMAPFolder::fetchMessages(std::vector >& 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 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); diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index 8006920c..7202a7d2 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -98,6 +98,14 @@ IMAPMessage::IMAPMessage(ref folder, const int num) } +IMAPMessage::IMAPMessage(ref 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 folder = m_folder.acquire(); @@ -271,7 +279,11 @@ void IMAPMessage::extract(ref 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 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 folder = m_folder.acquire(); // Get message attributes - const std::vector atts = - msgAtt->items(); + const std::vector 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: diff --git a/src/net/imap/IMAPUtils.cpp b/src/net/imap/IMAPUtils.cpp index 0d6fc478..eceac16b 100644 --- a/src/net/imap/IMAPUtils.cpp +++ b/src/net/imap/IMAPUtils.cpp @@ -539,6 +539,24 @@ const string IMAPUtils::listToSet(const std::vector & list, const int max, } +// static +const string IMAPUtils::listToSet(const std::vector & 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 & 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 & 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 ::const_iterator it = items.begin() ; it != items.end() ; ++it) @@ -686,6 +708,20 @@ const string IMAPUtils::buildFetchRequest(const std::vector & list, const i } +// static +const string IMAPUtils::buildFetchRequest(const std::vector & list, const int options) +{ + return buildFetchRequestImpl("number", listToSet(list, -1, false), options); +} + + +// static +const string IMAPUtils::buildFetchRequest(const std::vector & 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 diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp index 8c4b2758..b606cda0 100644 --- a/src/net/maildir/maildirFolder.cpp +++ b/src/net/maildir/maildirFolder.cpp @@ -444,6 +444,18 @@ std::vector > maildirFolder::getMessages(const std::vector & } +ref maildirFolder::getMessageByUID(const message::uid& /* uid */) +{ + throw exceptions::operation_not_supported(); +} + + +std::vector > maildirFolder::getMessagesByUID(const std::vector & /* uids */) +{ + throw exceptions::operation_not_supported(); +} + + int maildirFolder::getMessageCount() { return (m_messageCount); diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp index e0856090..21e7a8b3 100644 --- a/src/net/pop3/POP3Folder.cpp +++ b/src/net/pop3/POP3Folder.cpp @@ -249,6 +249,18 @@ std::vector > POP3Folder::getMessages(const int from, const int t } +ref POP3Folder::getMessageByUID(const message::uid& /* uid */) +{ + throw exceptions::operation_not_supported(); +} + + +std::vector > POP3Folder::getMessagesByUID(const std::vector & /* uids */) +{ + throw exceptions::operation_not_supported(); +} + + std::vector > POP3Folder::getMessages(const std::vector & nums) { ref store = m_store.acquire(); diff --git a/vmime/net/folder.hpp b/vmime/net/folder.hpp index df9cbaf5..a50ee0e5 100644 --- a/vmime/net/folder.hpp +++ b/vmime/net/folder.hpp @@ -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 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 > 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 > getMessages(const std::vector & 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 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 > getMessagesByUID(const std::vector & uids) = 0; + /** Return the number of messages in this folder. * * @return number of messages in the folder diff --git a/vmime/net/imap/IMAPFolder.hpp b/vmime/net/imap/IMAPFolder.hpp index cc52596f..33378581 100644 --- a/vmime/net/imap/IMAPFolder.hpp +++ b/vmime/net/imap/IMAPFolder.hpp @@ -84,6 +84,12 @@ public: ref getMessage(const int num); std::vector > getMessages(const int from = 1, const int to = -1); std::vector > getMessages(const std::vector & nums); + + ref getMessageByUID(const message::uid& uid); + std::vector > getMessagesByUID(const std::vector & uids); + + std::vector getMessageNumbersStartingOnUID(const message::uid& uid); + int getMessageCount(); ref getFolder(const folder::path::component& name); @@ -120,8 +126,6 @@ public: int getFetchCapabilities() const; - std::vector 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 m_messages; }; diff --git a/vmime/net/imap/IMAPMessage.hpp b/vmime/net/imap/IMAPMessage.hpp index edbf69ff..fbba6e7d 100644 --- a/vmime/net/imap/IMAPMessage.hpp +++ b/vmime/net/imap/IMAPMessage.hpp @@ -50,6 +50,7 @@ private: friend class vmime::creator; // vmime::create IMAPMessage(ref folder, const int num); + IMAPMessage(ref folder, const int num, const uid& uniqueId); IMAPMessage(const IMAPMessage&) : message() { } ~IMAPMessage(); @@ -83,7 +84,7 @@ private: void fetch(ref 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. * diff --git a/vmime/net/imap/IMAPUtils.hpp b/vmime/net/imap/IMAPUtils.hpp index d1ed5c86..9c9c4205 100644 --- a/vmime/net/imap/IMAPUtils.hpp +++ b/vmime/net/imap/IMAPUtils.hpp @@ -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 & 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 & 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 & 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 & 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); }; diff --git a/vmime/net/maildir/maildirFolder.hpp b/vmime/net/maildir/maildirFolder.hpp index 68b5b898..c9ba8996 100644 --- a/vmime/net/maildir/maildirFolder.hpp +++ b/vmime/net/maildir/maildirFolder.hpp @@ -85,6 +85,10 @@ public: ref getMessage(const int num); std::vector > getMessages(const int from = 1, const int to = -1); std::vector > getMessages(const std::vector & nums); + + ref getMessageByUID(const message::uid& uid); + std::vector > getMessagesByUID(const std::vector & uids); + int getMessageCount(); ref getFolder(const folder::path::component& name); diff --git a/vmime/net/pop3/POP3Folder.hpp b/vmime/net/pop3/POP3Folder.hpp index c4829085..090f948b 100644 --- a/vmime/net/pop3/POP3Folder.hpp +++ b/vmime/net/pop3/POP3Folder.hpp @@ -83,6 +83,10 @@ public: ref getMessage(const int num); std::vector > getMessages(const int from = 1, const int to = -1); std::vector > getMessages(const std::vector & nums); + + ref getMessageByUID(const message::uid& uid); + std::vector > getMessagesByUID(const std::vector & uids); + int getMessageCount(); ref getFolder(const folder::path::component& name);