aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2013-07-26 12:47:30 +0000
committerVincent Richard <[email protected]>2013-07-26 12:47:30 +0000
commitb0845eff0db7d3aa35c3dc1629d250535d704bc8 (patch)
tree3e317af991e2f02596916cda0a0cb7b552bcd998
parentAdded SMTP service properties to allow disabling PIPELINING and CHUNKING exte... (diff)
downloadvmime-b0845eff0db7d3aa35c3dc1629d250535d704bc8.tar.gz
vmime-b0845eff0db7d3aa35c3dc1629d250535d704bc8.zip
Allow messages to be designated either by their number or their UID.
Warning: this is an API-breaking change.
-rw-r--r--SConstruct2
-rw-r--r--doc/book/net.tex14
-rw-r--r--src/net/imap/IMAPFolder.cpp361
-rw-r--r--src/net/imap/IMAPMessage.cpp5
-rw-r--r--src/net/imap/IMAPUtils.cpp213
-rw-r--r--src/net/maildir/maildirFolder.cpp380
-rw-r--r--src/net/maildir/maildirMessage.cpp2
-rw-r--r--src/net/maildir/maildirUtils.cpp32
-rw-r--r--src/net/messageSet.cpp369
-rw-r--r--src/net/pop3/POP3Folder.cpp173
-rw-r--r--src/net/pop3/POP3Utils.cpp32
-rw-r--r--tests/net/messageSetTest.cpp200
-rw-r--r--vmime/net/folder.hpp101
-rw-r--r--vmime/net/imap/IMAPFolder.hpp17
-rw-r--r--vmime/net/imap/IMAPUtils.hpp54
-rw-r--r--vmime/net/maildir/maildirFolder.hpp17
-rw-r--r--vmime/net/maildir/maildirUtils.hpp9
-rw-r--r--vmime/net/messageSet.hpp335
-rw-r--r--vmime/net/pop3/POP3Folder.hpp17
-rw-r--r--vmime/net/pop3/POP3Utils.hpp9
20 files changed, 1360 insertions, 982 deletions
diff --git a/SConstruct b/SConstruct
index ed0a4d03..baff95c0 100644
--- a/SConstruct
+++ b/SConstruct
@@ -206,6 +206,7 @@ libvmime_messaging_sources = [
'net/folder.cpp', 'net/folder.hpp',
'net/folderStatus.hpp',
'net/message.cpp', 'net/message.hpp',
+ 'net/messageSet.cpp', 'net/messageSet.hpp',
'net/securedConnectionInfos.hpp',
'net/service.cpp', 'net/service.hpp',
'net/serviceFactory.cpp', 'net/serviceFactory.hpp',
@@ -412,6 +413,7 @@ libvmimetest_sources = [
'tests/security/digest/md5Test.cpp',
'tests/security/digest/sha1Test.cpp',
# =============================== Net ================================
+ 'tests/net/messageSetTest.cpp',
'tests/net/pop3/POP3CommandTest.cpp',
'tests/net/pop3/POP3ResponseTest.cpp',
'tests/net/pop3/POP3UtilsTest.cpp',
diff --git a/doc/book/net.tex b/doc/book/net.tex
index 19a6ccb9..7359c3e3 100644
--- a/doc/book/net.tex
+++ b/doc/book/net.tex
@@ -555,7 +555,9 @@ The following code shows how to list all the messages in a folder, and
retrieve basic information to show them to the user:
\begin{lstlisting}[caption={Fetching information about multiple messages}]
-std::vector <ref <vmime::net::message> > allMessages = folder->getMessages();
+std::vector <ref <vmime::net::message> > allMessages =
+ folder->getMessages(vmime::net::messageSet::byNumber(1, -1));
+ // -1 is a special value to mean "the number of the last message in the folder"
folder->fetchMessages(allMessages,
vmime::net::folder::FETCH_FLAGS |
@@ -628,17 +630,17 @@ store.
\begin{lstlisting}[caption={Deleting messages}]
vmime::ref <vmime::net::folder> folder = store->getDefaultFolder();
-folder->deleteMessage(3);
-folder->deleteMessage(2);
+folder->deleteMessages(vmime::net::messageSet::byNumber(/* from */ 2, /* to */ 3));
// This is equivalent
std::vector <int> nums;
nums.push_back(2);
nums.push_back(3);
-folder->deleteMessages(nums);
+folder->deleteMessages(vmime::net::messageSet::byNumber(nums));
-// This is also equivalent
-folder->deleteMessages(/* from */ 2, /* to */ 3);
+// This is also equivalent (but will require 2 roundtrips to server)
+folder->deleteMessages(vmime::net::messageSet::byNumber(2));
+folder->deleteMessages(vmime::net::messageSet::byNumber(2)); // renumbered, 3 becomes 2
\end{lstlisting}
\subsection{Events} % --------------------------------------------------------
diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp
index 3a38182e..dd7f2512 100644
--- a/src/net/imap/IMAPFolder.cpp
+++ b/src/net/imap/IMAPFolder.cpp
@@ -529,135 +529,93 @@ ref <message> IMAPFolder::getMessage(const int num)
}
-std::vector <ref <message> > IMAPFolder::getMessages(const int from, const int to)
+std::vector <ref <message> > IMAPFolder::getMessages(const messageSet& msgs)
{
- const int messageCount = m_status->getMessageCount();
- const int to2 = (to == -1 ? messageCount : to);
-
if (!isOpen())
throw exceptions::illegal_state("Folder not open");
- else if (to2 < from || from < 1 || to2 < 1 || from > messageCount || to2 > messageCount)
- throw exceptions::message_not_found();
-
- std::vector <ref <message> > v;
- ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
-
- for (int i = from ; i <= to2 ; ++i)
- v.push_back(vmime::create <IMAPMessage>(thisFolder, i));
-
- return (v);
-}
-
-
-std::vector <ref <message> > IMAPFolder::getMessages(const std::vector <int>& nums)
-{
- if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
-
- std::vector <ref <message> > v;
- ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
-
- for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
- v.push_back(vmime::create <IMAPMessage>(thisFolder, *it));
-
- return (v);
-}
-
-
-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)
+ if (msgs.isEmpty() == 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
+ std::vector <ref <message> > messages;
- // Prepare command and arguments
- std::ostringstream cmd;
- cmd.imbue(std::locale::classic());
+ if (msgs.isNumberSet())
+ {
+ const std::vector <int> numbers = IMAPUtils::messageSetToNumberList(msgs);
- cmd << "UID FETCH " << uids[0];
+ ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
- for (std::vector <message::uid>::size_type i = 1, n = uids.size() ; i < n ; ++i)
- cmd << "," << uids[i];
+ for (std::vector <int>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it)
+ messages.push_back(vmime::create <IMAPMessage>(thisFolder, *it));
+ }
+ else if (msgs.isUIDSet())
+ {
+ // 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
- cmd << " UID";
+ // Prepare command and arguments
+ std::ostringstream cmd;
+ cmd.imbue(std::locale::classic());
- // Send the request
- m_connection->send(true, cmd.str(), true);
+ cmd << "UID FETCH " << IMAPUtils::messageSetToSequenceSet(msgs) << " UID";
- // Get the response
- utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+ // Send the request
+ m_connection->send(true, cmd.str(), true);
- if (resp->isBad() || resp->response_done()->response_tagged()->
- resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
- {
- throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "bad response");
- }
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
- // Process the response
- const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
- resp->continue_req_or_response_data();
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "bad response");
+ }
- std::vector <ref <message> > messages;
+ // Process the response
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
- for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
- it = respDataList.begin() ; it != respDataList.end() ; ++it)
- {
- if ((*it)->response_data() == NULL)
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
{
- throw exceptions::command_error("UID FETCH ... UID",
- resp->getErrorLog(), "invalid response");
- }
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("UID FETCH ... UID",
+ resp->getErrorLog(), "invalid response");
+ }
- const IMAPParser::message_data* messageData =
- (*it)->response_data()->message_data();
+ 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;
+ // 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;
+ // Get Process fetch response for this message
+ const int msgNum = static_cast <int>(messageData->number());
+ message::uid msgUID;
- // Find UID in message attributes
- const std::vector <IMAPParser::msg_att_item*> atts = messageData->msg_att()->items();
+ // 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)
+ for (std::vector <IMAPParser::msg_att_item*>::const_iterator
+ it = atts.begin() ; it != atts.end() ; ++it)
{
- msgUID = (*it)->unique_id()->value();
- break;
+ if ((*it)->type() == IMAPParser::msg_att_item::UID)
+ {
+ msgUID = (*it)->unique_id()->value();
+ break;
+ }
}
- }
- if (!msgUID.empty())
- {
- ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
- messages.push_back(vmime::create <IMAPMessage>(thisFolder, msgNum, msgUID));
+ if (!msgUID.empty())
+ {
+ ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
+ messages.push_back(vmime::create <IMAPMessage>(thisFolder, msgNum, msgUID));
+ }
}
}
@@ -816,7 +774,9 @@ void IMAPFolder::fetchMessages(std::vector <ref <message> >& msg, const int opti
}
// Send the request
- const string command = IMAPUtils::buildFetchRequest(m_connection, list, options);
+ const string command = IMAPUtils::buildFetchRequest
+ (m_connection, messageSet::byNumber(list), options);
+
m_connection->send(true, command, true);
// Get the response
@@ -945,17 +905,11 @@ void IMAPFolder::onStoreDisconnected()
}
-void IMAPFolder::deleteMessage(const int num)
-{
- deleteMessages(num, num);
-}
-
-
-void IMAPFolder::deleteMessages(const int from, const int to)
+void IMAPFolder::deleteMessages(const messageSet& msgs)
{
ref <IMAPStore> store = m_store.acquire();
- if (from < 1 || (to < from && to != -1))
+ if (msgs.isEmpty())
throw exceptions::invalid_argument();
if (!store)
@@ -969,19 +923,10 @@ void IMAPFolder::deleteMessages(const int from, const int to)
std::ostringstream command;
command.imbue(std::locale::classic());
- command << "STORE ";
-
- if (from == to)
- {
- command << from;
- }
+ if (msgs.isUIDSet())
+ command << "UID STORE" << IMAPUtils::messageSetToSequenceSet(msgs);
else
- {
- command << from << ":";
-
- if (to == -1) command << m_status->getMessageCount();
- else command << to;
- }
+ command << "STORE" << IMAPUtils::messageSetToSequenceSet(msgs);
command << " +FLAGS (\\Deleted)";
@@ -1002,116 +947,16 @@ void IMAPFolder::deleteMessages(const int from, const int to)
}
-void IMAPFolder::deleteMessages(const std::vector <int>& nums)
+void IMAPFolder::setMessageFlags(const messageSet& msgs, const int flags, const int mode)
{
- ref <IMAPStore> store = m_store.acquire();
-
- if (nums.empty())
- throw exceptions::invalid_argument();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
- else if (m_mode == MODE_READ_ONLY)
- throw exceptions::illegal_state("Folder is read-only");
-
- // Sort the list of message numbers
- std::vector <int> list;
-
- list.resize(nums.size());
- std::copy(nums.begin(), nums.end(), list.begin());
-
- std::sort(list.begin(), list.end());
-
// Build the request text
std::ostringstream command;
command.imbue(std::locale::classic());
- command << "STORE ";
- command << IMAPUtils::listToSet(list, m_status->getMessageCount(), true);
- command << " +FLAGS (\\Deleted)";
-
- // Send the request
- m_connection->send(true, command.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("STORE",
- resp->getErrorLog(), "bad response");
- }
-
- processStatusUpdate(resp);
-}
-
-
-void IMAPFolder::setMessageFlags(const int from, const int to, const int flags, const int mode)
-{
- ref <IMAPStore> store = m_store.acquire();
-
- if (from < 1 || (to < from && to != -1))
- throw exceptions::invalid_argument();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
- else if (m_mode == MODE_READ_ONLY)
- throw exceptions::illegal_state("Folder is read-only");
-
- std::ostringstream oss;
- oss.imbue(std::locale::classic());
-
- if (from == to)
- {
- oss << from;
- }
+ if (msgs.isUIDSet())
+ command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
else
- {
- if (to == -1)
- oss << from << ":*";
- else
- oss << from << ":" << to;
- }
-
- setMessageFlagsImpl(oss.str(), flags, mode);
-}
-
-
-void IMAPFolder::setMessageFlags(const std::vector <int>& nums, const int flags, const int mode)
-{
- ref <IMAPStore> store = m_store.acquire();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
- else if (m_mode == MODE_READ_ONLY)
- throw exceptions::illegal_state("Folder is read-only");
-
- // Sort the list of message numbers
- std::vector <int> list;
-
- list.resize(nums.size());
- std::copy(nums.begin(), nums.end(), list.begin());
- std::sort(list.begin(), list.end());
-
- // Delegates call
- setMessageFlagsImpl(IMAPUtils::listToSet(list, m_status->getMessageCount(), true), flags, mode);
-}
-
-
-void IMAPFolder::setMessageFlagsImpl(const string& set, const int flags, const int mode)
-{
- // Build the request text
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- command << "STORE " << set;
+ command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
switch (mode)
{
@@ -1364,27 +1209,7 @@ void IMAPFolder::rename(const folder::path& newPath)
}
-void IMAPFolder::copyMessage(const folder::path& dest, const int num)
-{
- ref <IMAPStore> store = m_store.acquire();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
-
- // Construct set
- std::ostringstream set;
- set.imbue(std::locale::classic());
-
- set << num;
-
- // Delegate message copy
- copyMessagesImpl(set.str(), dest);
-}
-
-
-void IMAPFolder::copyMessages(const folder::path& dest, const int from, const int to)
+void IMAPFolder::copyMessages(const folder::path& dest, const messageSet& set)
{
ref <IMAPStore> store = m_store.acquire();
@@ -1392,44 +1217,12 @@ void IMAPFolder::copyMessages(const folder::path& dest, const int from, const in
throw exceptions::illegal_state("Store disconnected");
else if (!isOpen())
throw exceptions::illegal_state("Folder not open");
- else if (from < 1 || (to < from && to != -1))
- throw exceptions::invalid_argument();
-
- // Construct set
- std::ostringstream set;
- set.imbue(std::locale::classic());
- if (to == -1)
- set << from << ":*";
- else
- set << from << ":" << to;
-
- // Delegate message copy
- copyMessagesImpl(set.str(), dest);
-}
-
-
-void IMAPFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums)
-{
- ref <IMAPStore> store = m_store.acquire();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
-
- // Delegate message copy
- copyMessagesImpl(IMAPUtils::listToSet(nums, m_status->getMessageCount()), dest);
-}
-
-
-void IMAPFolder::copyMessagesImpl(const string& set, const folder::path& dest)
-{
// Build the request text
std::ostringstream command;
command.imbue(std::locale::classic());
- command << "COPY " << set << " ";
+ command << "COPY " << IMAPUtils::messageSetToSequenceSet(set) << " ";
command << IMAPUtils::quoteString(IMAPUtils::pathToString
(m_connection->hierarchySeparator(), dest));
@@ -1599,6 +1392,8 @@ std::vector <int> IMAPFolder::getMessageNumbersStartingOnUID(const message::uid&
}
}
+ processStatusUpdate(resp);
+
return v;
}
diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp
index 96114e94..33599689 100644
--- a/src/net/imap/IMAPMessage.cpp
+++ b/src/net/imap/IMAPMessage.cpp
@@ -531,7 +531,10 @@ void IMAPMessage::setFlags(const int flags, const int mode)
if (!folder)
throw exceptions::folder_not_found();
- folder->setMessageFlags(m_num, m_num, flags, mode);
+ if (!m_uid.empty())
+ folder->setMessageFlags(messageSet::byUID(m_uid), flags, mode);
+ else
+ folder->setMessageFlags(messageSet::byNumber(m_num), flags, mode);
}
diff --git a/src/net/imap/IMAPUtils.cpp b/src/net/imap/IMAPUtils.cpp
index dc583f84..b5f8b38d 100644
--- a/src/net/imap/IMAPUtils.cpp
+++ b/src/net/imap/IMAPUtils.cpp
@@ -472,98 +472,6 @@ const string IMAPUtils::messageFlagList(const int flags)
// static
-const string IMAPUtils::listToSet(const std::vector <int>& list, const int max,
- const bool alreadySorted)
-{
- // Sort a copy of the list (if not already sorted)
- std::vector <int> temp;
-
- if (!alreadySorted)
- {
- temp.resize(list.size());
- std::copy(list.begin(), list.end(), temp.begin());
-
- std::sort(temp.begin(), temp.end());
- }
-
- const std::vector <int>& theList = (alreadySorted ? list : temp);
-
- // Build the set
- std::ostringstream res;
- res.imbue(std::locale::classic());
-
- int previous = -1, setBegin = -1;
-
- for (std::vector <int>::const_iterator it = theList.begin() ;
- it != theList.end() ; ++it)
- {
- const int current = *it;
-
- if (previous == -1)
- {
- res << current;
-
- previous = current;
- setBegin = current;
- }
- else
- {
- if (current == previous + 1)
- {
- previous = current;
- }
- else
- {
- if (setBegin != previous)
- {
- res << ":" << previous << "," << current;
-
- previous = current;
- setBegin = current;
- }
- else
- {
- if (setBegin != current) // skip duplicates
- res << "," << current;
-
- previous = current;
- setBegin = current;
- }
- }
- }
- }
-
- if (previous != setBegin)
- {
- if (previous == max)
- res << ":*";
- else
- res << ":" << previous;
- }
-
- return (res.str());
-}
-
-
-// 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 << list[0];
-
- for (std::vector <message::uid>::size_type i = 1, n = list.size() ; i < n ; ++i)
- res << "," << list[i];
-
- return res.str();
-}
-
-
-// static
const string IMAPUtils::dateTime(const vmime::datetime& date)
{
std::ostringstream res;
@@ -633,8 +541,8 @@ const string IMAPUtils::dateTime(const vmime::datetime& date)
// static
-const string IMAPUtils::buildFetchRequestImpl
- (ref <IMAPConnection> cnt, const string& mode, const string& set, const int options)
+const string IMAPUtils::buildFetchRequest
+ (ref <IMAPConnection> cnt, const messageSet& msgs, const int options)
{
// Example:
// C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
@@ -702,10 +610,10 @@ const string IMAPUtils::buildFetchRequestImpl
std::ostringstream command;
command.imbue(std::locale::classic());
- if (mode == "uid")
- command << "UID FETCH " << set << " (";
+ if (msgs.isUIDSet())
+ command << "UID FETCH " << messageSetToSequenceSet(msgs) << " (";
else
- command << "FETCH " << set << " (";
+ command << "FETCH " << messageSetToSequenceSet(msgs) << " (";
for (std::vector <string>::const_iterator it = items.begin() ;
it != items.end() ; ++it)
@@ -721,22 +629,6 @@ const string IMAPUtils::buildFetchRequestImpl
// static
-const string IMAPUtils::buildFetchRequest
- (ref <IMAPConnection> cnt, const std::vector <int>& list, const int options)
-{
- return buildFetchRequestImpl(cnt, "number", listToSet(list, -1, false), options);
-}
-
-
-// static
-const string IMAPUtils::buildFetchRequest
- (ref <IMAPConnection> cnt, const std::vector <message::uid>& list, const int options)
-{
- return buildFetchRequestImpl(cnt, "uid", listToSet(list), options);
-}
-
-
-// static
void IMAPUtils::convertAddressList
(const IMAPParser::address_list& src, mailboxList& dest)
{
@@ -756,6 +648,101 @@ void IMAPUtils::convertAddressList
}
+
+class IMAPUIDMessageSetEnumerator : public messageSetEnumerator
+{
+public:
+
+ IMAPUIDMessageSetEnumerator()
+ : m_first(true)
+ {
+ }
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ if (!m_first)
+ m_oss << ",";
+
+ if (range.getFirst() == range.getLast())
+ m_oss << range.getFirst();
+ else
+ m_oss << range.getFirst() << ":" << range.getLast();
+
+ m_first = false;
+ }
+
+ void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& range)
+ {
+ if (!m_first)
+ m_oss << ",";
+
+ if (range.getFirst() == range.getLast())
+ m_oss << range.getFirst();
+ else
+ m_oss << range.getFirst() << ":" << range.getLast();
+
+ m_first = false;
+ }
+
+ const std::string str() const
+ {
+ return m_oss.str();
+ }
+
+private:
+
+ std::ostringstream m_oss;
+ bool m_first;
+};
+
+
+class IMAPMessageSetEnumerator : public messageSetEnumerator
+{
+public:
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ for (int i = range.getFirst(), last = range.getLast() ; i <= last ; ++i)
+ m_list.push_back(i);
+ }
+
+ void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */)
+ {
+ // Not used
+ }
+
+ const std::vector <int>& list() const
+ {
+ return m_list;
+ }
+
+public:
+
+ std::vector <int> m_list;
+};
+
+
+
+// static
+const string IMAPUtils::messageSetToSequenceSet(const messageSet& msgs)
+{
+ IMAPUIDMessageSetEnumerator en;
+ msgs.enumerate(en);
+
+ return en.str();
+}
+
+
+// static
+const std::vector <int> IMAPUtils::messageSetToNumberList(const messageSet& msgs)
+{
+ IMAPMessageSetEnumerator en;
+ msgs.enumerate(en);
+
+ return en.list();
+}
+
+
} // imap
} // net
} // vmime
diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp
index 3042d4b5..42a2c5ff 100644
--- a/src/net/maildir/maildirFolder.cpp
+++ b/src/net/maildir/maildirFolder.cpp
@@ -419,49 +419,32 @@ ref <message> maildirFolder::getMessage(const int num)
}
-std::vector <ref <message> > maildirFolder::getMessages(const int from, const int to)
-{
- const int to2 = (to == -1 ? m_messageCount : to);
-
- if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
- else if (to2 < from || from < 1 || to2 < 1 || from > m_messageCount || to2 > m_messageCount)
- throw exceptions::message_not_found();
-
- std::vector <ref <message> > v;
- ref <maildirFolder> thisFolder = thisRef().dynamicCast <maildirFolder>();
-
- for (int i = from ; i <= to2 ; ++i)
- v.push_back(vmime::create <maildirMessage>(thisFolder, i));
-
- return (v);
-}
-
-
-std::vector <ref <message> > maildirFolder::getMessages(const std::vector <int>& nums)
+std::vector <ref <message> > maildirFolder::getMessages(const messageSet& msgs)
{
if (!isOpen())
throw exceptions::illegal_state("Folder not open");
- std::vector <ref <message> > v;
- ref <maildirFolder> thisFolder = thisRef().dynamicCast <maildirFolder>();
-
- for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
- v.push_back(vmime::create <maildirMessage>(thisFolder, *it));
-
- return (v);
-}
+ if (msgs.isNumberSet())
+ {
+ const std::vector <int> numbers = maildirUtils::messageSetToNumberList(msgs);
+ std::vector <ref <message> > messages;
+ ref <maildirFolder> thisFolder = thisRef().dynamicCast <maildirFolder>();
-ref <message> maildirFolder::getMessageByUID(const message::uid& /* uid */)
-{
- throw exceptions::operation_not_supported();
-}
+ for (std::vector <int>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it)
+ {
+ if (*it < 1|| *it > m_messageCount)
+ throw exceptions::message_not_found();
+ messages.push_back(vmime::create <maildirMessage>(thisFolder, *it));
+ }
-std::vector <ref <message> > maildirFolder::getMessagesByUID(const std::vector <message::uid>& /* uids */)
-{
- throw exceptions::operation_not_supported();
+ return messages;
+ }
+ else
+ {
+ throw exceptions::operation_not_supported();
+ }
}
@@ -590,35 +573,18 @@ void maildirFolder::rename(const folder::path& newPath)
}
-void maildirFolder::deleteMessage(const int num)
-{
- // Mark messages as deleted
- setMessageFlags(num, num, message::FLAG_DELETED, message::FLAG_MODE_ADD);
-}
-
-
-void maildirFolder::deleteMessages(const int from, const int to)
-{
- // Mark messages as deleted
- setMessageFlags(from, to, message::FLAG_DELETED, message::FLAG_MODE_ADD);
-}
-
-
-void maildirFolder::deleteMessages(const std::vector <int>& nums)
+void maildirFolder::deleteMessages(const messageSet& msgs)
{
// Mark messages as deleted
- setMessageFlags(nums, message::FLAG_DELETED, message::FLAG_MODE_ADD);
+ setMessageFlags(msgs, message::FLAG_DELETED, message::FLAG_MODE_ADD);
}
void maildirFolder::setMessageFlags
- (const int from, const int to, const int flags, const int mode)
+ (const messageSet& msgs, const int flags, const int mode)
{
ref <maildirStore> store = m_store.acquire();
- if (from < 1 || (to < from && to != -1))
- throw exceptions::invalid_argument();
-
if (!store)
throw exceptions::illegal_state("Store disconnected");
else if (!isOpen())
@@ -626,210 +592,116 @@ void maildirFolder::setMessageFlags
else if (m_mode == MODE_READ_ONLY)
throw exceptions::illegal_state("Folder is read-only");
- // Construct the list of message numbers
- const int to2 = (to == -1) ? m_messageCount : to;
- const int count = to - from + 1;
-
- std::vector <int> nums;
- nums.resize(count);
+ if (msgs.isNumberSet())
+ {
+ const std::vector <int> nums = maildirUtils::messageSetToNumberList(msgs);
- for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
- nums[j] = i;
+ // Change message flags
+ ref <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
- // Change message flags
- setMessageFlagsImpl(nums, flags, mode);
+ utility::file::path curDirPath = store->getFormat()->
+ folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY);
- // Update local flags
- switch (mode)
- {
- case message::FLAG_MODE_ADD:
- {
- for (std::vector <maildirMessage*>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
- {
- if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
- (*it)->m_flags != message::FLAG_UNDEFINED)
- {
- (*it)->m_flags |= flags;
- }
- }
-
- break;
- }
- case message::FLAG_MODE_REMOVE:
- {
- for (std::vector <maildirMessage*>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
+ for (std::vector <int>::const_iterator it =
+ nums.begin() ; it != nums.end() ; ++it)
{
- if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
- (*it)->m_flags != message::FLAG_UNDEFINED)
- {
- (*it)->m_flags &= ~flags;
- }
- }
+ const int num = *it - 1;
- break;
- }
- default:
- case message::FLAG_MODE_SET:
- {
- for (std::vector <maildirMessage*>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
- {
- if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
- (*it)->m_flags != message::FLAG_UNDEFINED)
+ try
{
- (*it)->m_flags = flags;
- }
- }
-
- break;
- }
-
- }
-
- // Notify message flags changed
- ref <events::messageChangedEvent> event =
- vmime::create <events::messageChangedEvent>
- (thisRef().dynamicCast <folder>(),
- events::messageChangedEvent::TYPE_FLAGS, nums);
-
- notifyMessageChanged(event);
-
- // TODO: notify other folders with the same path
-}
-
+ const utility::file::path::component path = m_messageInfos[num].path;
+ ref <utility::file> file = fsf->create(curDirPath / path);
-void maildirFolder::setMessageFlags
- (const std::vector <int>& nums, const int flags, const int mode)
-{
- ref <maildirStore> store = m_store.acquire();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
- else if (m_mode == MODE_READ_ONLY)
- throw exceptions::illegal_state("Folder is read-only");
+ int newFlags = maildirUtils::extractFlags(path);
- // Sort the list of message numbers
- std::vector <int> list;
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD: newFlags |= flags; break;
+ case message::FLAG_MODE_REMOVE: newFlags &= ~flags; break;
+ default:
+ case message::FLAG_MODE_SET: newFlags = flags; break;
+ }
- list.resize(nums.size());
- std::copy(nums.begin(), nums.end(), list.begin());
+ const utility::file::path::component newPath = maildirUtils::buildFilename
+ (maildirUtils::extractId(path), newFlags);
- std::sort(list.begin(), list.end());
+ file->rename(curDirPath / newPath);
- // Change message flags
- setMessageFlagsImpl(list, flags, mode);
+ if (flags & message::FLAG_DELETED)
+ m_messageInfos[num].type = messageInfos::TYPE_DELETED;
+ else
+ m_messageInfos[num].type = messageInfos::TYPE_CUR;
- // Update local flags
- switch (mode)
- {
- case message::FLAG_MODE_ADD:
- {
- for (std::vector <maildirMessage*>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
- {
- if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
- (*it)->m_flags != message::FLAG_UNDEFINED)
+ m_messageInfos[num].path = newPath;
+ }
+ catch (exceptions::filesystem_exception& e)
{
- (*it)->m_flags |= flags;
+ // Ignore (not important)
}
}
- break;
- }
- case message::FLAG_MODE_REMOVE:
- {
- for (std::vector <maildirMessage*>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
+ // Update local flags
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD:
{
- if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
- (*it)->m_flags != message::FLAG_UNDEFINED)
+ for (std::vector <maildirMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
{
- (*it)->m_flags &= ~flags;
+ if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= flags;
+ }
}
- }
- break;
- }
- default:
- case message::FLAG_MODE_SET:
- {
- for (std::vector <maildirMessage*>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
+ break;
+ }
+ case message::FLAG_MODE_REMOVE:
{
- if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
- (*it)->m_flags != message::FLAG_UNDEFINED)
+ for (std::vector <maildirMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
{
- (*it)->m_flags = flags;
+ if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags &= ~flags;
+ }
}
- }
-
- break;
- }
-
- }
-
- // Notify message flags changed
- ref <events::messageChangedEvent> event =
- vmime::create <events::messageChangedEvent>
- (thisRef().dynamicCast <folder>(),
- events::messageChangedEvent::TYPE_FLAGS, nums);
-
- notifyMessageChanged(event);
- // TODO: notify other folders with the same path
-}
-
-
-void maildirFolder::setMessageFlagsImpl
- (const std::vector <int>& nums, const int flags, const int mode)
-{
- ref <maildirStore> store = m_store.acquire();
-
- ref <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
-
- utility::file::path curDirPath = store->getFormat()->
- folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY);
-
- for (std::vector <int>::const_iterator it =
- nums.begin() ; it != nums.end() ; ++it)
- {
- const int num = *it - 1;
-
- try
+ break;
+ }
+ default:
+ case message::FLAG_MODE_SET:
{
- const utility::file::path::component path = m_messageInfos[num].path;
- ref <utility::file> file = fsf->create(curDirPath / path);
-
- int newFlags = maildirUtils::extractFlags(path);
-
- switch (mode)
+ for (std::vector <maildirMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
{
- case message::FLAG_MODE_ADD: newFlags |= flags; break;
- case message::FLAG_MODE_REMOVE: newFlags &= ~flags; break;
- default:
- case message::FLAG_MODE_SET: newFlags = flags; break;
+ if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags = flags;
+ }
}
- const utility::file::path::component newPath = maildirUtils::buildFilename
- (maildirUtils::extractId(path), newFlags);
+ break;
+ }
- file->rename(curDirPath / newPath);
+ }
- if (flags & message::FLAG_DELETED)
- m_messageInfos[num].type = messageInfos::TYPE_DELETED;
- else
- m_messageInfos[num].type = messageInfos::TYPE_CUR;
+ // Notify message flags changed
+ ref <events::messageChangedEvent> event =
+ vmime::create <events::messageChangedEvent>
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
- m_messageInfos[num].path = newPath;
- }
- catch (exceptions::filesystem_exception& e)
- {
- // Ignore (not important)
- }
+ notifyMessageChanged(event);
+
+ // TODO: notify other folders with the same path
+ }
+ else
+ {
+ throw exceptions::operation_not_supported();
}
}
@@ -1032,46 +904,7 @@ void maildirFolder::copyMessageImpl(const utility::file::path& tmpDirPath,
}
-void maildirFolder::copyMessage(const folder::path& dest, const int num)
-{
- ref <maildirStore> store = m_store.acquire();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
-
- copyMessages(dest, num, num);
-}
-
-
-void maildirFolder::copyMessages(const folder::path& dest, const int from, const int to)
-{
- ref <maildirStore> store = m_store.acquire();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
- else if (from < 1 || (to < from && to != -1))
- throw exceptions::invalid_argument();
-
- // Construct the list of message numbers
- const int to2 = (to == -1) ? m_messageCount : to;
- const int count = to - from + 1;
-
- std::vector <int> nums;
- nums.resize(count);
-
- for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
- nums[j] = i;
-
- // Copy messages
- copyMessagesImpl(dest, nums);
-}
-
-
-void maildirFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums)
+void maildirFolder::copyMessages(const folder::path& dest, const messageSet& msgs)
{
ref <maildirStore> store = m_store.acquire();
@@ -1080,15 +913,6 @@ void maildirFolder::copyMessages(const folder::path& dest, const std::vector <in
else if (!isOpen())
throw exceptions::illegal_state("Folder not open");
- // Copy messages
- copyMessagesImpl(dest, nums);
-}
-
-
-void maildirFolder::copyMessagesImpl(const folder::path& dest, const std::vector <int>& nums)
-{
- ref <maildirStore> store = m_store.acquire();
-
ref <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
utility::file::path curDirPath = store->getFormat()->folderPathToFileSystemPath
@@ -1121,6 +945,8 @@ void maildirFolder::copyMessagesImpl(const folder::path& dest, const std::vector
}
// Copy messages
+ const std::vector <int> nums = maildirUtils::messageSetToNumberList(msgs);
+
try
{
for (std::vector <int>::const_iterator it =
diff --git a/src/net/maildir/maildirMessage.cpp b/src/net/maildir/maildirMessage.cpp
index e63d5edf..a7c2a22f 100644
--- a/src/net/maildir/maildirMessage.cpp
+++ b/src/net/maildir/maildirMessage.cpp
@@ -140,7 +140,7 @@ void maildirMessage::setFlags(const int flags, const int mode)
if (!folder)
throw exceptions::folder_not_found();
- folder->setMessageFlags(m_num, m_num, flags, mode);
+ folder->setMessageFlags(messageSet::byNumber(m_num), flags, mode);
}
diff --git a/src/net/maildir/maildirUtils.cpp b/src/net/maildir/maildirUtils.cpp
index 9028fa59..c4ba2857 100644
--- a/src/net/maildir/maildirUtils.cpp
+++ b/src/net/maildir/maildirUtils.cpp
@@ -214,6 +214,38 @@ void maildirUtils::recursiveFSDelete(ref <utility::file> dir)
+class maildirMessageSetEnumerator : public messageSetEnumerator
+{
+public:
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ for (int i = range.getFirst(), last = range.getLast() ; i <= last ; ++i)
+ list.push_back(i);
+ }
+
+ void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */)
+ {
+ // Not supported
+ }
+
+public:
+
+ std::vector <int> list;
+};
+
+
+// static
+const std::vector <int> maildirUtils::messageSetToNumberList(const messageSet& msgs)
+{
+ maildirMessageSetEnumerator en;
+ msgs.enumerate(en);
+
+ return en.list;
+}
+
+
+
//
// messageIdComparator
//
diff --git a/src/net/messageSet.cpp b/src/net/messageSet.cpp
new file mode 100644
index 00000000..04f1debb
--- /dev/null
+++ b/src/net/messageSet.cpp
@@ -0,0 +1,369 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/net/messageSet.hpp"
+
+#include <iterator>
+#include <algorithm>
+#include <typeinfo>
+
+
+namespace vmime {
+namespace net {
+
+
+// messageRange
+
+messageRange::messageRange()
+{
+}
+
+
+messageRange::~messageRange()
+{
+}
+
+
+// numberMessageRange
+
+numberMessageRange::numberMessageRange(const int number)
+ : m_first(number), m_last(number)
+{
+ if (number < 1)
+ throw std::invalid_argument("number");
+}
+
+
+numberMessageRange::numberMessageRange(const int first, const int last)
+ : m_first(first), m_last(last)
+{
+ if (first < 1)
+ throw std::invalid_argument("first");
+ else if (last != -1 && last < first)
+ throw std::invalid_argument("last");
+}
+
+
+numberMessageRange::numberMessageRange(const numberMessageRange& other)
+ : messageRange(), m_first(other.m_first), m_last(other.m_last)
+{
+}
+
+
+int numberMessageRange::getFirst() const
+{
+ return m_first;
+}
+
+
+int numberMessageRange::getLast() const
+{
+ return m_last;
+}
+
+
+void numberMessageRange::enumerate(messageSetEnumerator& en) const
+{
+ en.enumerateNumberMessageRange(*this);
+}
+
+
+messageRange* numberMessageRange::clone() const
+{
+ return new numberMessageRange(*this);
+}
+
+
+// UIDMessageRange
+
+UIDMessageRange::UIDMessageRange(const message::uid& uid)
+ : m_first(uid), m_last(uid)
+{
+}
+
+
+UIDMessageRange::UIDMessageRange(const message::uid& first, const message::uid& last)
+ : m_first(first), m_last(last)
+{
+}
+
+
+UIDMessageRange::UIDMessageRange(const UIDMessageRange& other)
+ : messageRange(), m_first(other.m_first), m_last(other.m_last)
+{
+}
+
+
+const message::uid UIDMessageRange::getFirst() const
+{
+ return m_first;
+}
+
+
+const message::uid UIDMessageRange::getLast() const
+{
+ return m_last;
+}
+
+
+void UIDMessageRange::enumerate(messageSetEnumerator& en) const
+{
+ en.enumerateUIDMessageRange(*this);
+}
+
+
+messageRange* UIDMessageRange::clone() const
+{
+ return new UIDMessageRange(*this);
+}
+
+
+// messageSet
+
+
+messageSet::messageSet()
+{
+}
+
+
+messageSet::messageSet(const messageSet& other)
+ : object()
+{
+ m_ranges.resize(other.m_ranges.size());
+
+ for (unsigned int i = 0, n = other.m_ranges.size() ; i < n ; ++i)
+ m_ranges[i] = other.m_ranges[i]->clone();
+}
+
+
+messageSet::~messageSet()
+{
+ for (unsigned int i = 0, n = m_ranges.size() ; i < n ; ++i)
+ delete m_ranges[i];
+}
+
+
+// static
+messageSet messageSet::byNumber(const int number)
+{
+ messageSet set;
+ set.m_ranges.push_back(new numberMessageRange(number));
+
+ return set;
+}
+
+
+// static
+messageSet messageSet::byNumber(const int first, const int last)
+{
+ messageSet set;
+ set.m_ranges.push_back(new numberMessageRange(first, last));
+
+ return set;
+}
+
+
+// static
+messageSet messageSet::byNumber(const std::vector <int>& numbers)
+{
+ // Sort a copy of the list
+ std::vector <int> sortedNumbers;
+
+ sortedNumbers.resize(numbers.size());
+
+ std::copy(numbers.begin(), numbers.end(), sortedNumbers.begin());
+ std::sort(sortedNumbers.begin(), sortedNumbers.end());
+
+ // Build the set by detecting ranges of continuous numbers
+ int previous = -1, rangeStart = -1;
+ messageSet set;
+
+ for (std::vector <int>::const_iterator it = sortedNumbers.begin() ;
+ it != sortedNumbers.end() ; ++it)
+ {
+ const int current = *it;
+
+ if (current == previous)
+ continue; // skip duplicates
+
+ if (previous == -1)
+ {
+ previous = current;
+ rangeStart = current;
+ }
+ else
+ {
+ if (current == previous + 1)
+ {
+ previous = current;
+ }
+ else
+ {
+ set.m_ranges.push_back(new numberMessageRange(rangeStart, previous));
+
+ previous = current;
+ rangeStart = current;
+ }
+ }
+ }
+
+ set.m_ranges.push_back(new numberMessageRange(rangeStart, previous));
+
+ return set;
+}
+
+
+// static
+messageSet messageSet::byUID(const message::uid& uid)
+{
+ messageSet set;
+ set.m_ranges.push_back(new UIDMessageRange(uid));
+
+ return set;
+}
+
+
+messageSet messageSet::byUID(const message::uid& first, const message::uid& last)
+{
+ messageSet set;
+ set.m_ranges.push_back(new UIDMessageRange(first, last));
+
+ return set;
+}
+
+
+messageSet messageSet::byUID(const std::vector <message::uid>& uids)
+{
+ std::vector <vmime_uint32> numericUIDs;
+
+ for (unsigned int i = 0, n = uids.size() ; i < n ; ++i)
+ {
+ const string uid = uids[i];
+ int numericUID = 0;
+
+ const string::value_type* p = uid.c_str();
+
+ for ( ; *p >= '0' && *p <= '9' ; ++p)
+ numericUID = (numericUID * 10) + (*p - '0');
+
+ if (*p != '\0')
+ {
+ messageSet set;
+
+ // Non-numeric UID, fall back to plain UID list (single-UID ranges)
+ for (unsigned int i = 0, n = uids.size() ; i < n ; ++i)
+ set.m_ranges.push_back(new UIDMessageRange(uids[i]));
+
+ return set;
+ }
+
+ numericUIDs.push_back(numericUID);
+ }
+
+ // Sort a copy of the list
+ std::vector <vmime_uint32> sortedUIDs;
+
+ sortedUIDs.resize(numericUIDs.size());
+
+ std::copy(numericUIDs.begin(), numericUIDs.end(), sortedUIDs.begin());
+ std::sort(sortedUIDs.begin(), sortedUIDs.end());
+
+ // Build the set by detecting ranges of continuous numbers
+ vmime_uint32 previous = -1U, rangeStart = -1U;
+ messageSet set;
+
+ for (std::vector <vmime_uint32>::const_iterator it = sortedUIDs.begin() ;
+ it != sortedUIDs.end() ; ++it)
+ {
+ const vmime_uint32 current = *it;
+
+ if (current == previous)
+ continue; // skip duplicates
+
+ if (previous == -1U)
+ {
+ previous = current;
+ rangeStart = current;
+ }
+ else
+ {
+ if (current == previous + 1)
+ {
+ previous = current;
+ }
+ else
+ {
+ set.m_ranges.push_back(new UIDMessageRange
+ (static_cast <std::ostringstream*>(&(std::ostringstream() << rangeStart))->str(),
+ static_cast <std::ostringstream*>(&(std::ostringstream() << previous))->str()));
+
+ previous = current;
+ rangeStart = current;
+ }
+ }
+ }
+
+ set.m_ranges.push_back(new UIDMessageRange
+ (static_cast <std::ostringstream*>(&(std::ostringstream() << rangeStart))->str(),
+ static_cast <std::ostringstream*>(&(std::ostringstream() << previous))->str()));
+
+ return set;
+}
+
+
+void messageSet::addRange(const messageRange& range)
+{
+ if (!m_ranges.empty() && typeid(*m_ranges[0]) != typeid(range))
+ throw std::invalid_argument("range");
+
+ m_ranges.push_back(range.clone());
+}
+
+
+void messageSet::enumerate(messageSetEnumerator& en) const
+{
+ for (unsigned int i = 0, n = m_ranges.size() ; i < n ; ++i)
+ m_ranges[i]->enumerate(en);
+}
+
+
+bool messageSet::isEmpty() const
+{
+ return m_ranges.empty();
+}
+
+
+bool messageSet::isNumberSet() const
+{
+ return !isEmpty() && dynamic_cast <numberMessageRange*>(m_ranges[0]) != NULL;
+}
+
+
+bool messageSet::isUIDSet() const
+{
+ return !isEmpty() && dynamic_cast <UIDMessageRange*>(m_ranges[0]) != NULL;
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp
index 9dc4589b..6a652de0 100644
--- a/src/net/pop3/POP3Folder.cpp
+++ b/src/net/pop3/POP3Folder.cpp
@@ -230,62 +230,36 @@ ref <message> POP3Folder::getMessage(const int num)
}
-std::vector <ref <message> > POP3Folder::getMessages(const int from, const int to)
+std::vector <ref <message> > POP3Folder::getMessages(const messageSet& msgs)
{
ref <POP3Store> store = m_store.acquire();
- const int to2 = (to == -1 ? m_messageCount : to);
-
if (!store)
throw exceptions::illegal_state("Store disconnected");
else if (!isOpen())
throw exceptions::illegal_state("Folder not open");
- else if (to2 < from || from < 1 || to2 < 1 || from > m_messageCount || to2 > m_messageCount)
- throw exceptions::message_not_found();
-
- std::vector <ref <message> > v;
- ref <POP3Folder> thisFolder = thisRef().dynamicCast <POP3Folder>();
-
- for (int i = from ; i <= to2 ; ++i)
- v.push_back(vmime::create <POP3Message>(thisFolder, i));
-
- return (v);
-}
-
-
-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();
-}
+ if (msgs.isNumberSet())
+ {
+ const std::vector <int> numbers = POP3Utils::messageSetToNumberList(msgs);
-std::vector <ref <message> > POP3Folder::getMessages(const std::vector <int>& nums)
-{
- ref <POP3Store> store = m_store.acquire();
+ std::vector <ref <message> > messages;
+ ref <POP3Folder> thisFolder = thisRef().dynamicCast <POP3Folder>();
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
+ for (std::vector <int>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it)
+ {
+ if (*it < 1|| *it > m_messageCount)
+ throw exceptions::message_not_found();
- std::vector <ref <message> > v;
- ref <POP3Folder> thisFolder = thisRef().dynamicCast <POP3Folder>();
+ messages.push_back(vmime::create <POP3Message>(thisFolder, *it));
+ }
- for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
+ return messages;
+ }
+ else
{
- if (*it < 1|| *it > m_messageCount)
- throw exceptions::message_not_found();
-
- v.push_back(vmime::create <POP3Message>(thisFolder, *it));
+ throw exceptions::operation_not_supported();
}
-
- return (v);
}
@@ -560,99 +534,11 @@ void POP3Folder::onStoreDisconnected()
}
-void POP3Folder::deleteMessage(const int num)
+void POP3Folder::deleteMessages(const messageSet& msgs)
{
ref <POP3Store> store = m_store.acquire();
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
-
- POP3Command::DELE(num)->send(store->getConnection());
-
- ref <POP3Response> response =
- POP3Response::readResponse(store->getConnection());
-
- if (!response->isSuccess())
- throw exceptions::command_error("DELE", response->getFirstLine());
-
- // Update local flags
- for (std::map <POP3Message*, int>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
- {
- POP3Message* msg = (*it).first;
-
- if (msg->getNumber() == num)
- msg->m_deleted = true;
- }
-
- // Notify message flags changed
- std::vector <int> nums;
- nums.push_back(num);
-
- ref <events::messageChangedEvent> event =
- vmime::create <events::messageChangedEvent>
- (thisRef().dynamicCast <folder>(),
- events::messageChangedEvent::TYPE_FLAGS, nums);
-
- notifyMessageChanged(event);
-}
-
-
-void POP3Folder::deleteMessages(const int from, const int to)
-{
- ref <POP3Store> store = m_store.acquire();
-
- if (from < 1 || (to < from && to != -1))
- throw exceptions::invalid_argument();
-
- if (!store)
- throw exceptions::illegal_state("Store disconnected");
- else if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
-
- const int to2 = (to == -1 ? m_messageCount : to);
-
- for (int i = from ; i <= to2 ; ++i)
- {
- POP3Command::DELE(i)->send(store->getConnection());
-
- ref <POP3Response> response =
- POP3Response::readResponse(store->getConnection());
-
- if (!response->isSuccess())
- throw exceptions::command_error("DELE", response->getFirstLine());
- }
-
- // Update local flags
- for (std::map <POP3Message*, int>::iterator it =
- m_messages.begin() ; it != m_messages.end() ; ++it)
- {
- POP3Message* msg = (*it).first;
-
- if (msg->getNumber() >= from && msg->getNumber() <= to2)
- msg->m_deleted = true;
- }
-
- // Notify message flags changed
- std::vector <int> nums;
-
- for (int i = from ; i <= to2 ; ++i)
- nums.push_back(i);
-
- ref <events::messageChangedEvent> event =
- vmime::create <events::messageChangedEvent>
- (thisRef().dynamicCast <folder>(),
- events::messageChangedEvent::TYPE_FLAGS, nums);
-
- notifyMessageChanged(event);
-}
-
-
-void POP3Folder::deleteMessages(const std::vector <int>& nums)
-{
- ref <POP3Store> store = m_store.acquire();
+ const std::vector <int> nums = POP3Utils::messageSetToNumberList(msgs);
if (nums.empty())
throw exceptions::invalid_argument();
@@ -702,14 +588,7 @@ void POP3Folder::deleteMessages(const std::vector <int>& nums)
}
-void POP3Folder::setMessageFlags(const int /* from */, const int /* to */,
- const int /* flags */, const int /* mode */)
-{
- throw exceptions::operation_not_supported();
-}
-
-
-void POP3Folder::setMessageFlags(const std::vector <int>& /* nums */,
+void POP3Folder::setMessageFlags(const messageSet& /* msgs */,
const int /* flags */, const int /* mode */)
{
throw exceptions::operation_not_supported();
@@ -736,19 +615,7 @@ void POP3Folder::addMessage(utility::inputStream& /* is */, const int /* size */
}
-void POP3Folder::copyMessage(const folder::path& /* dest */, const int /* num */)
-{
- throw exceptions::operation_not_supported();
-}
-
-
-void POP3Folder::copyMessages(const folder::path& /* dest */, const int /* from */, const int /* to */)
-{
- throw exceptions::operation_not_supported();
-}
-
-
-void POP3Folder::copyMessages(const folder::path& /* dest */, const std::vector <int>& /* nums */)
+void POP3Folder::copyMessages(const folder::path& /* dest */, const messageSet& /* msgs */)
{
throw exceptions::operation_not_supported();
}
diff --git a/src/net/pop3/POP3Utils.cpp b/src/net/pop3/POP3Utils.cpp
index f239627f..e2722104 100644
--- a/src/net/pop3/POP3Utils.cpp
+++ b/src/net/pop3/POP3Utils.cpp
@@ -73,6 +73,38 @@ void POP3Utils::parseMultiListOrUidlResponse(ref <POP3Response> response, std::m
}
+
+class POP3MessageSetEnumerator : public messageSetEnumerator
+{
+public:
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ for (int i = range.getFirst(), last = range.getLast() ; i <= last ; ++i)
+ list.push_back(i);
+ }
+
+ void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */)
+ {
+ // Not supported
+ }
+
+public:
+
+ std::vector <int> list;
+};
+
+
+// static
+const std::vector <int> POP3Utils::messageSetToNumberList(const messageSet& msgs)
+{
+ POP3MessageSetEnumerator en;
+ msgs.enumerate(en);
+
+ return en.list;
+}
+
+
} // pop3
} // net
} // vmime
diff --git a/tests/net/messageSetTest.cpp b/tests/net/messageSetTest.cpp
new file mode 100644
index 00000000..3aef7ab0
--- /dev/null
+++ b/tests/net/messageSetTest.cpp
@@ -0,0 +1,200 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "tests/testUtils.hpp"
+
+#include "vmime/net/messageSet.hpp"
+
+
+VMIME_TEST_SUITE_BEGIN(messageSetTest)
+
+ VMIME_TEST_LIST_BEGIN
+ VMIME_TEST(testNumberSet_Single)
+ VMIME_TEST(testNumberSet_Range)
+ VMIME_TEST(testNumberSet_InfiniteRange)
+ VMIME_TEST(testNumberSet_Multiple)
+ VMIME_TEST(testUIDSet_Single)
+ VMIME_TEST(testUIDSet_Range)
+ VMIME_TEST(testUIDSet_InfiniteRange)
+ VMIME_TEST(testUIDSet_MultipleNumeric)
+ VMIME_TEST(testUIDSet_MultipleNonNumeric)
+ VMIME_TEST(testIsNumberSet)
+ VMIME_TEST(testIsUIDSet)
+ VMIME_TEST_LIST_END
+
+
+ class messageSetStringEnumerator : public vmime::net::messageSetEnumerator
+ {
+ public:
+
+ messageSetStringEnumerator()
+ : m_first(true)
+ {
+ }
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ if (!m_first)
+ m_oss << ",";
+
+ if (range.getFirst() == range.getLast())
+ m_oss << range.getFirst();
+ else
+ m_oss << range.getFirst() << ":" << range.getLast();
+
+ m_first = false;
+ }
+
+ void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& range)
+ {
+ if (!m_first)
+ m_oss << ",";
+
+ if (range.getFirst() == range.getLast())
+ m_oss << range.getFirst();
+ else
+ m_oss << range.getFirst() << ":" << range.getLast();
+
+ m_first = false;
+ }
+
+ const std::string str() const
+ {
+ return m_oss.str();
+ }
+
+ private:
+
+ std::ostringstream m_oss;
+ bool m_first;
+ };
+
+
+ const std::string enumerateAsString(const vmime::net::messageSet& set)
+ {
+ messageSetStringEnumerator en;
+ set.enumerate(en);
+
+ return en.str();
+ }
+
+
+ void testNumberSet_Single()
+ {
+ VASSERT_EQ("str", "42", enumerateAsString(vmime::net::messageSet::byNumber(42)));
+ }
+
+ void testNumberSet_Range()
+ {
+ VASSERT_EQ("str", "42:100", enumerateAsString(vmime::net::messageSet::byNumber(42, 100)));
+ }
+
+ void testNumberSet_InfiniteRange()
+ {
+ VASSERT_EQ("str", "42:-1", enumerateAsString(vmime::net::messageSet::byNumber(42, -1)));
+ }
+
+ void testNumberSet_Multiple()
+ {
+ std::vector <int> numbers;
+ numbers.push_back(1); // test grouping 1:3
+ numbers.push_back(89); // test sorting
+ numbers.push_back(2);
+ numbers.push_back(3);
+ numbers.push_back(42);
+ numbers.push_back(53); // test grouping 53:57
+ numbers.push_back(54);
+ numbers.push_back(55);
+ numbers.push_back(56);
+ numbers.push_back(56); // test duplicates
+ numbers.push_back(57);
+ numbers.push_back(99);
+
+ VASSERT_EQ("str", "1:3,42,53:57,89,99", enumerateAsString(vmime::net::messageSet::byNumber(numbers)));
+ }
+
+
+ void testUIDSet_Single()
+ {
+ VASSERT_EQ("str", "abcdef", enumerateAsString(vmime::net::messageSet::byUID("abcdef")));
+ }
+
+ void testUIDSet_Range()
+ {
+ VASSERT_EQ("str", "abc:def", enumerateAsString(vmime::net::messageSet::byUID("abc:def")));
+ }
+
+ void testUIDSet_InfiniteRange()
+ {
+ VASSERT_EQ("str", "abc:*", enumerateAsString(vmime::net::messageSet::byUID("abc", "*")));
+ }
+
+ void testUIDSet_MultipleNumeric()
+ {
+ std::vector <vmime::net::message::uid> uids;
+ uids.push_back("1"); // test grouping 1:3
+ uids.push_back("89"); // test sorting
+ uids.push_back("2");
+ uids.push_back("3");
+ uids.push_back("42");
+ uids.push_back("53"); // test grouping 53:57
+ uids.push_back("54");
+ uids.push_back("55");
+ uids.push_back("56");
+ uids.push_back("56"); // test duplicates
+ uids.push_back("57");
+ uids.push_back("99");
+
+ VASSERT_EQ("str", "1:3,42,53:57,89,99", enumerateAsString(vmime::net::messageSet::byUID(uids)));
+ }
+
+ void testUIDSet_MultipleNonNumeric()
+ {
+ std::vector <vmime::net::message::uid> uids;
+ uids.push_back("12");
+ uids.push_back("34");
+ uids.push_back("ab56");
+ uids.push_back("78cd");
+
+ VASSERT_EQ("str", "12,34,ab56,78cd", enumerateAsString(vmime::net::messageSet::byUID(uids)));
+ }
+
+ void testIsNumberSet()
+ {
+ VASSERT_TRUE("number1", vmime::net::messageSet::byNumber(42).isNumberSet());
+ VASSERT_FALSE("uid1", vmime::net::messageSet::byUID("42").isNumberSet());
+
+ VASSERT_TRUE("number2", vmime::net::messageSet::byNumber(42, -1).isNumberSet());
+ VASSERT_FALSE("uid2", vmime::net::messageSet::byUID("42", "*").isNumberSet());
+ }
+
+ void testIsUIDSet()
+ {
+ VASSERT_FALSE("number1", vmime::net::messageSet::byNumber(42).isUIDSet());
+ VASSERT_TRUE("uid1", vmime::net::messageSet::byUID("42").isUIDSet());
+
+ VASSERT_FALSE("number2", vmime::net::messageSet::byNumber(42, -1).isUIDSet());
+ VASSERT_TRUE("uid2", vmime::net::messageSet::byUID("42", "*").isUIDSet());
+ }
+
+VMIME_TEST_SUITE_END
diff --git a/vmime/net/folder.hpp b/vmime/net/folder.hpp
index 4cb6bb1a..fc878fbf 100644
--- a/vmime/net/folder.hpp
+++ b/vmime/net/folder.hpp
@@ -38,6 +38,7 @@
#include "vmime/message.hpp"
#include "vmime/net/message.hpp"
+#include "vmime/net/messageSet.hpp"
#include "vmime/net/events.hpp"
#include "vmime/net/folderStatus.hpp"
@@ -186,38 +187,34 @@ public:
*/
virtual ref <message> getMessage(const int num) = 0;
- /** Get new references to messages in this folder, given their numbers.
+ /** Get new references to messages in this folder, given either their
+ * sequence numbers or UIDs.
*
- * @param from sequence number of the first message to get
- * @param to sequence number of the last message to get
- * @return new objects referencing the specified messages
- * @throw exceptions::net_exception if an error occurs
- */
- virtual std::vector <ref <message> > getMessages(const int from = 1, const int to = -1) = 0;
-
- /** Get new references to messages in this folder, given their numbers.
+ * To retrieve messages by their number, use:
+ * \code{.cpp}
+ * // Get messages from sequence number 5 to sequence number 8 (including)
+ * folder->getMessage(vmime::net::messageSet::byNumber(5, 8));
*
- * @param nums sequence numbers of the messages to retrieve
- * @return new objects referencing the specified messages
- * @throw exceptions::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.
+ * // Get all messages in the folder, starting from number 42
+ * folder->getMessage(vmime::net::messageSet::byNumber(42, -1));
+ * \endcode
+ * Or, to retrieve messages by their UID, use:
+ * \code{.cpp}
+ * // Get messages from UID 1000 to UID 1042 (including)
+ * folder->getMessage(vmime::net::messageSet::byUID(1000, 1042));
*
- * @param uid UID of message to retrieve
- * @return a new object referencing the specified message
- * @throw exceptions::net_exception if an error occurs
- */
- virtual ref <message> getMessageByUID(const message::uid& uid) = 0;
-
- /** Get messages in this folder, given their UIDs.
+ * // Get message with UID 1042
+ * folder->getMessage(vmime::net::messageSet::byUID(1042));
+ *
+ * // Get all messages in the folder, starting from UID 1000
+ * folder->getMessage(vmime::net::messageSet::byUID(1000, "*"));
+ * \endcode
*
- * @param uids UIDs of messages to retrieve
+ * @param msgs index set of messages to retrieve
* @return new objects referencing the specified messages
* @throw exceptions::net_exception if an error occurs
*/
- virtual std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids) = 0;
+ virtual std::vector <ref <message> > getMessages(const messageSet& msgs) = 0;
/** Return the number of messages in this folder.
*
@@ -249,46 +246,21 @@ public:
*/
virtual void rename(const folder::path& newPath) = 0;
- /** Remove a message in this folder.
- *
- * @param num sequence number of the message to delete
- * @throw exceptions::net_exception if an error occurs
- */
- virtual void deleteMessage(const int num) = 0;
-
- /** Remove one or more messages from this folder.
- *
- * @param from sequence number of the first message to delete
- * @param to sequence number of the last message to delete
- * @throw exceptions::net_exception if an error occurs
- */
- virtual void deleteMessages(const int from = 1, const int to = -1) = 0;
-
/** Remove one or more messages from this folder.
*
- * @param nums sequence numbers of the messages to delete
- * @throw exceptions::net_exception if an error occurs
- */
- virtual void deleteMessages(const std::vector <int>& nums) = 0;
-
- /** Change the flags for one or more messages in this folder.
- *
- * @param from sequence number of the first message to modify
- * @param to sequence number of the last message to modify
- * @param flags set of flags (see message::Flags)
- * @param mode indicate how to treat old and new flags (see message::FlagsModes)
+ * @param msgs index set of messages to delete
* @throw exceptions::net_exception if an error occurs
*/
- virtual void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET) = 0;
+ virtual void deleteMessages(const messageSet& msgs) = 0;
/** Change the flags for one or more messages in this folder.
*
- * @param nums sequence numbers of the messages to modify
+ * @param msgs index set of messages on which to set the flags
* @param flags set of flags (see message::Flags)
* @param mode indicate how to treat old and new flags (see message::FlagsModes)
* @throw exceptions::net_exception if an error occurs
*/
- virtual void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET) = 0;
+ virtual void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET) = 0;
/** Add a message to this folder.
*
@@ -311,30 +283,13 @@ public:
*/
virtual void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL) = 0;
- /** Copy a message from this folder to another folder.
- *
- * @param dest destination folder path
- * @param num sequence number of the message to copy
- * @throw exceptions::net_exception if an error occurs
- */
- virtual void copyMessage(const folder::path& dest, const int num) = 0;
-
- /** Copy messages from this folder to another folder.
- *
- * @param dest destination folder path
- * @param from sequence number of the first message to copy
- * @param to sequence number of the last message to copy
- * @throw exceptions::net_exception if an error occurs
- */
- virtual void copyMessages(const folder::path& dest, const int from = 1, const int to = -1) = 0;
-
/** Copy messages from this folder to another folder.
*
* @param dest destination folder path
- * @param nums sequence numbers of the messages to copy
+ * @param msgs index set of messages to copy
* @throw exceptions::net_exception if an error occurs
*/
- virtual void copyMessages(const folder::path& dest, const std::vector <int>& nums) = 0;
+ virtual void copyMessages(const folder::path& dest, const messageSet& msgs) = 0;
/** Request folder status without opening it.
*
diff --git a/vmime/net/imap/IMAPFolder.hpp b/vmime/net/imap/IMAPFolder.hpp
index c1d2d34a..b7fc46a1 100644
--- a/vmime/net/imap/IMAPFolder.hpp
+++ b/vmime/net/imap/IMAPFolder.hpp
@@ -91,11 +91,7 @@ public:
bool isOpen() const;
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 <ref <message> > getMessages(const messageSet& msgs);
std::vector <int> getMessageNumbersStartingOnUID(const message::uid& uid);
@@ -106,19 +102,14 @@ public:
void rename(const folder::path& newPath);
- void deleteMessage(const int num);
- void deleteMessages(const int from = 1, const int to = -1);
- void deleteMessages(const std::vector <int>& nums);
+ void deleteMessages(const messageSet& msgs);
- void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET);
- void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET);
+ void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET);
void addMessage(ref <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
- void copyMessage(const folder::path& dest, const int num);
- void copyMessages(const folder::path& dest, const int from = 1, const int to = -1);
- void copyMessages(const folder::path& dest, const std::vector <int>& nums);
+ void copyMessages(const folder::path& dest, const messageSet& msgs);
void status(int& count, int& unseen);
ref <folderStatus> getStatus();
diff --git a/vmime/net/imap/IMAPUtils.hpp b/vmime/net/imap/IMAPUtils.hpp
index 7a03fcac..1dfde7a9 100644
--- a/vmime/net/imap/IMAPUtils.hpp
+++ b/vmime/net/imap/IMAPUtils.hpp
@@ -73,29 +73,6 @@ public:
static const string messageFlagList(const int flags);
- /** 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"
- * 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);
-
- /** 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
@@ -103,25 +80,16 @@ public:
*/
static const string dateTime(const vmime::datetime& date);
- /** Construct a fetch request for the specified messages, designated by their sequence numbers.
- *
- * @param cnt connection
- * @param list list of message numbers
- * @param options fetch options
- * @return fetch request
- */
- static const string buildFetchRequest
- (ref <IMAPConnection> cnt, const std::vector <int>& list, const int options);
-
- /** Construct a fetch request for the specified messages, designated by their UIDs.
+ /** Construct a fetch request for the specified messages, designated
+ * either by their sequence numbers or their UIDs.
*
* @param cnt connection
- * @param list list of message UIDs
+ * @param msgs message set
* @param options fetch options
* @return fetch request
*/
static const string buildFetchRequest
- (ref <IMAPConnection> cnt, const std::vector <message::uid>& list, const int options);
+ (ref <IMAPConnection> cnt, const messageSet& msgs, const int options);
/** Convert a parser-style address list to a mailbox list.
*
@@ -130,6 +98,20 @@ public:
*/
static void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest);
+ /** Returns an IMAP-formatted sequence set given a message set.
+ *
+ * @param msgs message set
+ * @return IMAP sequence set (eg. "1:5,7,15:*")
+ */
+ static const string messageSetToSequenceSet(const messageSet& msgs);
+
+ /** Returns a list of message sequence numbers given a message set.
+ *
+ * @param msgs message set
+ * @return list of message numbers
+ */
+ static const std::vector <int> messageSetToNumberList(const messageSet& msgs);
+
private:
static const string buildFetchRequestImpl
diff --git a/vmime/net/maildir/maildirFolder.hpp b/vmime/net/maildir/maildirFolder.hpp
index 940fcaae..2b6f8b4d 100644
--- a/vmime/net/maildir/maildirFolder.hpp
+++ b/vmime/net/maildir/maildirFolder.hpp
@@ -89,11 +89,7 @@ public:
bool isOpen() const;
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 <ref <message> > getMessages(const messageSet& msgs);
int getMessageCount();
@@ -102,19 +98,14 @@ public:
void rename(const folder::path& newPath);
- void deleteMessage(const int num);
- void deleteMessages(const int from = 1, const int to = -1);
- void deleteMessages(const std::vector <int>& nums);
+ void deleteMessages(const messageSet& msgs);
- void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET);
- void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET);
+ void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET);
void addMessage(ref <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
- void copyMessage(const folder::path& dest, const int num);
- void copyMessages(const folder::path& dest, const int from = 1, const int to = -1);
- void copyMessages(const folder::path& dest, const std::vector <int>& nums);
+ void copyMessages(const folder::path& dest, const messageSet& msgs);
void status(int& count, int& unseen);
ref <folderStatus> getStatus();
diff --git a/vmime/net/maildir/maildirUtils.hpp b/vmime/net/maildir/maildirUtils.hpp
index 22246fb7..072a2648 100644
--- a/vmime/net/maildir/maildirUtils.hpp
+++ b/vmime/net/maildir/maildirUtils.hpp
@@ -34,6 +34,8 @@
#include "vmime/utility/file.hpp"
#include "vmime/utility/path.hpp"
+#include "vmime/net/messageSet.hpp"
+
namespace vmime {
namespace net {
@@ -129,6 +131,13 @@ public:
* @param dir directory to delete
*/
static void recursiveFSDelete(ref <utility::file> dir);
+
+ /** Returns a list of message numbers given a message set.
+ *
+ * @param msgs message set
+ * @return list of message numbers
+ */
+ static const std::vector <int> messageSetToNumberList(const messageSet& msgs);
};
diff --git a/vmime/net/messageSet.hpp b/vmime/net/messageSet.hpp
new file mode 100644
index 00000000..2535ac25
--- /dev/null
+++ b/vmime/net/messageSet.hpp
@@ -0,0 +1,335 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#ifndef VMIME_NET_MESSAGESET_HPP_INCLUDED
+#define VMIME_NET_MESSAGESET_HPP_INCLUDED
+
+
+#include "vmime/net/message.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+// Forward references
+class numberMessageRange;
+class UIDMessageRange;
+
+
+/** Enumerator used to retrieve the message number/UID ranges contained
+ * in a messageSet object.
+ */
+
+class VMIME_EXPORT messageSetEnumerator
+{
+public:
+
+ virtual void enumerateNumberMessageRange(const numberMessageRange& range) = 0;
+ virtual void enumerateUIDMessageRange(const UIDMessageRange& range) = 0;
+};
+
+
+/** A range of (continuous) messages, designated either by their
+ * sequence number, or by their UID.
+ */
+
+class messageRange : public object
+{
+public:
+
+ virtual ~messageRange();
+
+ /** Enumerates this range with the specified enumerator.
+ *
+ * @param en enumerator that will receive the method calls while
+ * enumerating this range
+ */
+ virtual void enumerate(messageSetEnumerator& en) const = 0;
+
+ /** Clones this message range.
+ */
+ virtual messageRange* clone() const = 0;
+
+protected:
+
+ messageRange();
+ messageRange(const messageRange&);
+};
+
+
+/** A range of (continuous) messages designated by their sequence number.
+ */
+
+class numberMessageRange : public messageRange
+{
+public:
+
+ /** Constructs a message range containing a single message.
+ *
+ * @param number message number (numbering starts at 1, not 0)
+ */
+ numberMessageRange(const int number);
+
+ /** Constructs a message range for multiple messages.
+ *
+ * @param first number of the first message in the range (numbering
+ * starts at 1, not 0)
+ * @param last number of the last message in the range, or use the
+ * special value -1 to designate the last message in the folder
+ */
+ numberMessageRange(const int first, const int last);
+
+ /** Constructs a message range by copying from another range.
+ *
+ * @param other range to copy
+ */
+ numberMessageRange(const numberMessageRange& other);
+
+ /** Returns the number of the first message in the range.
+ *
+ * @return number of the first message
+ */
+ int getFirst() const;
+
+ /** Returns the number of the last message in the range, or -1
+ * to designate the last message in the folder
+ *
+ * @return number of the last message
+ */
+ int getLast() const;
+
+ void enumerate(messageSetEnumerator& en) const;
+
+ messageRange* clone() const;
+
+private:
+
+ int m_first, m_last;
+};
+
+
+/** A range of (continuous) messages represented by their UID.
+ */
+
+class UIDMessageRange : public messageRange
+{
+public:
+
+ /** Constructs a message range containing a single message.
+ *
+ * @param uid message UID
+ */
+ UIDMessageRange(const message::uid& uid);
+
+ /** Constructs a message range for multiple messages.
+ *
+ * @param first UID of the first message in the range
+ * @param last UID of the last message in the range, or use the
+ * special value '*' to designate the last message in the folder
+ */
+ UIDMessageRange(const message::uid& first, const message::uid& last);
+
+ /** Constructs a message range by copying from another range.
+ *
+ * @param other range to copy
+ */
+ UIDMessageRange(const UIDMessageRange& other);
+
+ /** Returns the UID of the first message in the range.
+ *
+ * @return UID of the first message
+ */
+ const message::uid getFirst() const;
+
+ /** Returns the UID of the last message in the range, or '*'
+ * to designate the last message in the folder
+ *
+ * @return UID of the last message
+ */
+ const message::uid getLast() const;
+
+ void enumerate(messageSetEnumerator& en) const;
+
+ messageRange* clone() const;
+
+private:
+
+ message::uid m_first, m_last;
+};
+
+
+/** Represents a set of messages, designated either by their sequence
+ * number, or by their UID (but not both).
+ *
+ * Following is example code to designate messages by their number:
+ * \code{.cpp}
+ * // Designate a single message with sequence number 42
+ * vmime::net::messageSet::byNumber(42)
+ *
+ * // Designate messages from sequence number 5 to sequence number 8 (including)
+ * vmime::net::messageSet::byNumber(5, 8)
+ *
+ * // Designate all messages in the folder, starting from number 42
+ * vmime::net::messageSet::byNumber(42, -1)
+ * \endcode
+ * Or, to designate messages by their UID, use:
+ * \code{.cpp}
+ * // Designate a single message with UID 1042
+ * vmime::net::messageSet::byUID(1042)
+ *
+ * // Designate messages from UID 1000 to UID 1042 (including)
+ * vmime::net::messageSet::byUID(1000, 1042)
+ *
+ * // Designate all messages in the folder, starting from UID 1000
+ * vmime::net::messageSet::byUID(1000, "*")
+ * \endcode
+ */
+
+class VMIME_EXPORT messageSet : public object
+{
+public:
+
+ ~messageSet();
+
+ messageSet(const messageSet& other);
+
+ /** Constructs a new message set and initializes it with a single
+ * message represented by its sequence number.
+ *
+ * @param number message number (numbering starts at 1, not 0)
+ * @return new message set
+ */
+ static messageSet byNumber(const int number);
+
+ /** Constructs a new message set and initializes it with a range
+ * of messages represented by their sequence number.
+ *
+ * @param first number of the first message in the range (numbering
+ * starts at 1, not 0)
+ * @param last number of the last message in the range, or use the
+ * special value -1 to designate the last message in the folder
+ * @return new message set
+ */
+ static messageSet byNumber(const int first, const int last);
+
+ /** Constructs a new message set and initializes it with a possibly
+ * unsorted list of messages represented by their sequence number.
+ * Please note that numbering starts at 1, not 0.
+ *
+ * The function tries to group consecutive message numbers into
+ * ranges to reduce the size of the resulting set.
+ *
+ * For example, given the list "1,2,3,4,5,7,8,13,15,16,17" it will
+ * result in the following ranges: "1:5,7:8,13,15:17".
+ *
+ * @param numbers a vector containing numbers of the messages
+ * @return new message set
+ */
+ static messageSet byNumber(const std::vector <int>& numbers);
+
+ /** Constructs a new message set and initializes it with a single
+ * message represented by its UID.
+ *
+ * @param uid message UID
+ * @return new message set
+ */
+ static messageSet byUID(const message::uid& uid);
+
+ /** Constructs a new message set and initializes it with a range
+ * of messages represented by their sequence number.
+ *
+ * @param first UID of the first message in the range
+ * @param last UID of the last message in the range, or use the
+ * special value '*' to designate the last message in the folder
+ * @return new message set
+ */
+ static messageSet byUID(const message::uid& first, const message::uid& last);
+
+ /** Constructs a new message set and initializes it with a possibly
+ * unsorted list of messages represented by their UID.
+ *
+ * For UIDs that actually are numbers (this is the case for IMAP), the
+ * function tries to group consecutive UIDs into ranges to reduce the
+ * size of the resulting set.
+ *
+ * For example, given the list "1,2,3,4,5,7,8,13,15,16,17" it will
+ * result in the following ranges: "1:5,7:8,13,15:17".
+ *
+ * @param uids a vector containing UIDs of the messages
+ * @return new message set
+ */
+ static messageSet byUID(const std::vector <message::uid>& uids);
+
+ /** Adds the specified range to this set. The type of message range
+ * (either number or UID) must match the type of the ranges already
+ * contained in this set (ie. it's not possible to have a message
+ * set which contains both number ranges and UID ranges).
+ *
+ * @param range range to add
+ * @throw std::invalid_argument exception if the range type does
+ * not match the type of the ranges in this set
+ */
+ void addRange(const messageRange& range);
+
+ /** Enumerates this set with the specified enumerator.
+ *
+ * @param en enumerator that will receive the method calls while
+ * enumerating the ranges in this set
+ */
+ void enumerate(messageSetEnumerator& en) const;
+
+ /** Returns whether this set is empty (contains no range).
+ *
+ * @return true if this set is empty, or false otherwise
+ */
+ bool isEmpty() const;
+
+ /** Returns whether this set references messages by their sequence
+ * number.
+ *
+ * @return true if this set references messages by their sequence
+ * number, or false otherwise
+ */
+ bool isNumberSet() const;
+
+ /** Returns whether this set references messages by their UID.
+ *
+ * @return true if this set references messages by their UID,
+ * or false otherwise
+ */
+ bool isUIDSet() const;
+
+private:
+
+ messageSet();
+
+ std::vector <messageRange*> m_ranges;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_MESSAGESET_HPP_INCLUDED
diff --git a/vmime/net/pop3/POP3Folder.hpp b/vmime/net/pop3/POP3Folder.hpp
index 8a97213c..93c1d257 100644
--- a/vmime/net/pop3/POP3Folder.hpp
+++ b/vmime/net/pop3/POP3Folder.hpp
@@ -86,11 +86,7 @@ public:
bool isOpen() const;
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 <ref <message> > getMessages(const messageSet& msgs);
int getMessageCount();
@@ -99,19 +95,14 @@ public:
void rename(const folder::path& newPath);
- void deleteMessage(const int num);
- void deleteMessages(const int from = 1, const int to = -1);
- void deleteMessages(const std::vector <int>& nums);
+ void deleteMessages(const messageSet& msgs);
- void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET);
- void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET);
+ void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET);
void addMessage(ref <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
- void copyMessage(const folder::path& dest, const int num);
- void copyMessages(const folder::path& dest, const int from = 1, const int to = -1);
- void copyMessages(const folder::path& dest, const std::vector <int>& nums);
+ void copyMessages(const folder::path& dest, const messageSet& msgs);
void status(int& count, int& unseen);
ref <folderStatus> getStatus();
diff --git a/vmime/net/pop3/POP3Utils.hpp b/vmime/net/pop3/POP3Utils.hpp
index 7a2376a1..9d20431c 100644
--- a/vmime/net/pop3/POP3Utils.hpp
+++ b/vmime/net/pop3/POP3Utils.hpp
@@ -35,6 +35,8 @@
#include "vmime/types.hpp"
+#include "vmime/net/messageSet.hpp"
+
namespace vmime {
namespace net {
@@ -63,6 +65,13 @@ public:
*/
static void parseMultiListOrUidlResponse
(ref <POP3Response> response, std::map <int, string>& result);
+
+ /** Returns a list of message numbers given a message set.
+ *
+ * @param msgs message set
+ * @return list of message numbers
+ */
+ static const std::vector <int> messageSetToNumberList(const messageSet& msgs);
};