diff --git a/ChangeLog b/ChangeLog index 260d1e5b..ab5cfb1c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,11 @@ VERSION 0.8.1cvs ================ +2006-01-15 Vincent Richard + + * IMAP: implemented multi-fetching. Now using "FETCH x:y" instead of + sending (y-x+1) "FETCH" requests. + 2005-12-26 Vincent Richard * posixSocket.cpp: use getaddrinfo() if available. This should bring diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index 81584dd0..e9849515 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -609,19 +609,79 @@ void IMAPFolder::fetchMessages(std::vector >& msg, const int opti else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); + // Build message numbers list + std::vector list; + list.reserve(msg.size()); + + std::map > numberToMsg; + + for (std::vector >::iterator it = msg.begin() ; it != msg.end() ; ++it) + { + list.push_back((*it)->getNumber()); + numberToMsg[(*it)->getNumber()] = (*it).dynamicCast (); + } + + // Send the request + const string command = IMAPUtils::buildFetchRequest(list, options); + m_connection->send(true, command, 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("FETCH", + m_connection->getParser()->lastLine(), "bad response"); + } + + const std::vector & respDataList = + resp->continue_req_or_response_data(); + const int total = msg.size(); int current = 0; if (progress) progress->start(total); - for (std::vector >::iterator it = msg.begin() ; - it != msg.end() ; ++it) + try { - (*it).dynamicCast ()->fetch(this, options); + for (std::vector ::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 (messageData->number()); + + std::map >::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) diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index cce01196..d345db38 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -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 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 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 ::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 ::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 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 resp(m_folder->m_connection->readResponse()); diff --git a/src/net/imap/IMAPUtils.cpp b/src/net/imap/IMAPUtils.cpp index dbd62a86..a4ed9fb2 100644 --- a/src/net/imap/IMAPUtils.cpp +++ b/src/net/imap/IMAPUtils.cpp @@ -19,6 +19,7 @@ #include "vmime/net/imap/IMAPUtils.hpp" #include "vmime/net/message.hpp" +#include "vmime/net/folder.hpp" #include #include @@ -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 & list, const int max, const bool alreadySorted) { @@ -483,6 +479,7 @@ const string IMAPUtils::listToSet(const std::vector & 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 & 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 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 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 ::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 ::const_iterator it = items.begin() ; + it != items.end() ; ++it) + { + if (it != items.begin()) command << " "; + command << *it; + } + + command << ")"; + + return command.str(); +} + + } // imap } // net } // vmime diff --git a/vmime/net/imap/IMAPUtils.hpp b/vmime/net/imap/IMAPUtils.hpp index 5a64d454..2c7fc311 100644 --- a/vmime/net/imap/IMAPUtils.hpp +++ b/vmime/net/imap/IMAPUtils.hpp @@ -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 & 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 & 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 & list, const int options); };