aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--src/net/imap/IMAPFolder.cpp68
-rw-r--r--src/net/imap/IMAPMessage.cpp77
-rw-r--r--src/net/imap/IMAPUtils.cpp87
-rw-r--r--vmime/net/imap/IMAPUtils.hpp36
5 files changed, 188 insertions, 85 deletions
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 <[email protected]>
+
+ * IMAP: implemented multi-fetching. Now using "FETCH x:y" instead of
+ sending (y-x+1) "FETCH" requests.
+
2005-12-26 Vincent Richard <[email protected]>
* 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 <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)
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 <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;
- }
+ // Send the request
+ std::vector <int> list;
+ list.push_back(m_num);
- command << ")";
+ const string command = IMAPUtils::buildFetchRequest(list, options);
- // Send the request
- m_folder->m_connection->send(true, command.str(), true);
+ m_folder->m_connection->send(true, command, true);
// Get the response
utility::auto_ptr <IMAPParser::response> 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 <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
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 <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);
};