Allow messages to be designated either by their number or their UID.

Warning: this is an API-breaking change.
This commit is contained in:
Vincent Richard 2013-07-26 14:47:30 +02:00
parent 820d44377e
commit b0845eff0d
20 changed files with 1391 additions and 1013 deletions

View File

@ -206,6 +206,7 @@ libvmime_messaging_sources = [
'net/folder.cpp', 'net/folder.hpp', 'net/folder.cpp', 'net/folder.hpp',
'net/folderStatus.hpp', 'net/folderStatus.hpp',
'net/message.cpp', 'net/message.hpp', 'net/message.cpp', 'net/message.hpp',
'net/messageSet.cpp', 'net/messageSet.hpp',
'net/securedConnectionInfos.hpp', 'net/securedConnectionInfos.hpp',
'net/service.cpp', 'net/service.hpp', 'net/service.cpp', 'net/service.hpp',
'net/serviceFactory.cpp', 'net/serviceFactory.hpp', 'net/serviceFactory.cpp', 'net/serviceFactory.hpp',
@ -412,6 +413,7 @@ libvmimetest_sources = [
'tests/security/digest/md5Test.cpp', 'tests/security/digest/md5Test.cpp',
'tests/security/digest/sha1Test.cpp', 'tests/security/digest/sha1Test.cpp',
# =============================== Net ================================ # =============================== Net ================================
'tests/net/messageSetTest.cpp',
'tests/net/pop3/POP3CommandTest.cpp', 'tests/net/pop3/POP3CommandTest.cpp',
'tests/net/pop3/POP3ResponseTest.cpp', 'tests/net/pop3/POP3ResponseTest.cpp',
'tests/net/pop3/POP3UtilsTest.cpp', 'tests/net/pop3/POP3UtilsTest.cpp',

View File

@ -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: retrieve basic information to show them to the user:
\begin{lstlisting}[caption={Fetching information about multiple messages}] \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, folder->fetchMessages(allMessages,
vmime::net::folder::FETCH_FLAGS | vmime::net::folder::FETCH_FLAGS |
@ -628,17 +630,17 @@ store.
\begin{lstlisting}[caption={Deleting messages}] \begin{lstlisting}[caption={Deleting messages}]
vmime::ref <vmime::net::folder> folder = store->getDefaultFolder(); vmime::ref <vmime::net::folder> folder = store->getDefaultFolder();
folder->deleteMessage(3); folder->deleteMessages(vmime::net::messageSet::byNumber(/* from */ 2, /* to */ 3));
folder->deleteMessage(2);
// This is equivalent // This is equivalent
std::vector <int> nums; std::vector <int> nums;
nums.push_back(2); nums.push_back(2);
nums.push_back(3); nums.push_back(3);
folder->deleteMessages(nums); folder->deleteMessages(vmime::net::messageSet::byNumber(nums));
// This is also equivalent // This is also equivalent (but will require 2 roundtrips to server)
folder->deleteMessages(/* from */ 2, /* to */ 3); folder->deleteMessages(vmime::net::messageSet::byNumber(2));
folder->deleteMessages(vmime::net::messageSet::byNumber(2)); // renumbered, 3 becomes 2
\end{lstlisting} \end{lstlisting}
\subsection{Events} % -------------------------------------------------------- \subsection{Events} % --------------------------------------------------------

View File

@ -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()) if (!isOpen())
throw exceptions::illegal_state("Folder not open"); throw exceptions::illegal_state("Folder not open");
std::vector <ref <message> > v; if (msgs.isEmpty() == 0)
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)
return std::vector <ref <message> >(); return std::vector <ref <message> >();
// C: . UID FETCH uuuu1,uuuu2,uuuu3 UID
// S: * nnnn1 FETCH (UID uuuu1)
// S: * nnnn2 FETCH (UID uuuu2)
// S: * nnnn3 FETCH (UID uuuu3)
// S: . OK UID FETCH completed
// Prepare command and arguments
std::ostringstream cmd;
cmd.imbue(std::locale::classic());
cmd << "UID FETCH " << uids[0];
for (std::vector <message::uid>::size_type i = 1, n = uids.size() ; i < n ; ++i)
cmd << "," << uids[i];
cmd << " UID";
// Send the request
m_connection->send(true, cmd.str(), true);
// Get the response
utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
if (resp->isBad() || resp->response_done()->response_tagged()->
resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
{
throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "bad response");
}
// Process the response
const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
resp->continue_req_or_response_data();
std::vector <ref <message> > messages; std::vector <ref <message> > messages;
for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator if (msgs.isNumberSet())
it = respDataList.begin() ; it != respDataList.end() ; ++it)
{ {
if ((*it)->response_data() == NULL) const std::vector <int> numbers = IMAPUtils::messageSetToNumberList(msgs);
ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
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
// Prepare command and arguments
std::ostringstream cmd;
cmd.imbue(std::locale::classic());
cmd << "UID FETCH " << IMAPUtils::messageSetToSequenceSet(msgs) << " UID";
// Send the request
m_connection->send(true, cmd.str(), true);
// Get the response
utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
if (resp->isBad() || resp->response_done()->response_tagged()->
resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
{ {
throw exceptions::command_error("UID FETCH ... UID", throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "bad response");
resp->getErrorLog(), "invalid response");
} }
const IMAPParser::message_data* messageData = // Process the response
(*it)->response_data()->message_data(); const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
resp->continue_req_or_response_data();
// We are only interested in responses of type "FETCH" for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH) it = respDataList.begin() ; it != respDataList.end() ; ++it)
continue;
// 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();
for (std::vector <IMAPParser::msg_att_item*>::const_iterator
it = atts.begin() ; it != atts.end() ; ++it)
{ {
if ((*it)->type() == IMAPParser::msg_att_item::UID) if ((*it)->response_data() == NULL)
{ {
msgUID = (*it)->unique_id()->value(); throw exceptions::command_error("UID FETCH ... UID",
break; resp->getErrorLog(), "invalid response");
} }
}
if (!msgUID.empty()) const IMAPParser::message_data* messageData =
{ (*it)->response_data()->message_data();
ref <IMAPFolder> thisFolder = thisRef().dynamicCast <IMAPFolder>();
messages.push_back(vmime::create <IMAPMessage>(thisFolder, msgNum, msgUID)); // 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;
// 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)
{
msgUID = (*it)->unique_id()->value();
break;
}
}
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 // 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); m_connection->send(true, command, true);
// Get the response // Get the response
@ -945,17 +905,11 @@ void IMAPFolder::onStoreDisconnected()
} }
void IMAPFolder::deleteMessage(const int num) void IMAPFolder::deleteMessages(const messageSet& msgs)
{
deleteMessages(num, num);
}
void IMAPFolder::deleteMessages(const int from, const int to)
{ {
ref <IMAPStore> store = m_store.acquire(); ref <IMAPStore> store = m_store.acquire();
if (from < 1 || (to < from && to != -1)) if (msgs.isEmpty())
throw exceptions::invalid_argument(); throw exceptions::invalid_argument();
if (!store) if (!store)
@ -969,19 +923,10 @@ void IMAPFolder::deleteMessages(const int from, const int to)
std::ostringstream command; std::ostringstream command;
command.imbue(std::locale::classic()); command.imbue(std::locale::classic());
command << "STORE "; if (msgs.isUIDSet())
command << "UID STORE" << IMAPUtils::messageSetToSequenceSet(msgs);
if (from == to)
{
command << from;
}
else else
{ command << "STORE" << IMAPUtils::messageSetToSequenceSet(msgs);
command << from << ":";
if (to == -1) command << m_status->getMessageCount();
else command << to;
}
command << " +FLAGS (\\Deleted)"; 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 // Build the request text
std::ostringstream command; std::ostringstream command;
command.imbue(std::locale::classic()); command.imbue(std::locale::classic());
command << "STORE "; if (msgs.isUIDSet())
command << IMAPUtils::listToSet(list, m_status->getMessageCount(), true); command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
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;
}
else else
{ command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
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;
switch (mode) switch (mode)
{ {
@ -1364,7 +1209,7 @@ void IMAPFolder::rename(const folder::path& newPath)
} }
void IMAPFolder::copyMessage(const folder::path& dest, const int num) void IMAPFolder::copyMessages(const folder::path& dest, const messageSet& set)
{ {
ref <IMAPStore> store = m_store.acquire(); ref <IMAPStore> store = m_store.acquire();
@ -1373,63 +1218,11 @@ void IMAPFolder::copyMessage(const folder::path& dest, const int num)
else if (!isOpen()) else if (!isOpen())
throw exceptions::illegal_state("Folder not open"); 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)
{
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 (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 // Build the request text
std::ostringstream command; std::ostringstream command;
command.imbue(std::locale::classic()); command.imbue(std::locale::classic());
command << "COPY " << set << " "; command << "COPY " << IMAPUtils::messageSetToSequenceSet(set) << " ";
command << IMAPUtils::quoteString(IMAPUtils::pathToString command << IMAPUtils::quoteString(IMAPUtils::pathToString
(m_connection->hierarchySeparator(), dest)); (m_connection->hierarchySeparator(), dest));
@ -1599,6 +1392,8 @@ std::vector <int> IMAPFolder::getMessageNumbersStartingOnUID(const message::uid&
} }
} }
processStatusUpdate(resp);
return v; return v;
} }

View File

@ -531,7 +531,10 @@ void IMAPMessage::setFlags(const int flags, const int mode)
if (!folder) if (!folder)
throw exceptions::folder_not_found(); 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);
} }

View File

@ -471,98 +471,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 // static
const string IMAPUtils::dateTime(const vmime::datetime& date) const string IMAPUtils::dateTime(const vmime::datetime& date)
{ {
@ -633,8 +541,8 @@ const string IMAPUtils::dateTime(const vmime::datetime& date)
// static // static
const string IMAPUtils::buildFetchRequestImpl const string IMAPUtils::buildFetchRequest
(ref <IMAPConnection> cnt, const string& mode, const string& set, const int options) (ref <IMAPConnection> cnt, const messageSet& msgs, const int options)
{ {
// Example: // Example:
// C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) // C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
@ -702,10 +610,10 @@ const string IMAPUtils::buildFetchRequestImpl
std::ostringstream command; std::ostringstream command;
command.imbue(std::locale::classic()); command.imbue(std::locale::classic());
if (mode == "uid") if (msgs.isUIDSet())
command << "UID FETCH " << set << " ("; command << "UID FETCH " << messageSetToSequenceSet(msgs) << " (";
else else
command << "FETCH " << set << " ("; command << "FETCH " << messageSetToSequenceSet(msgs) << " (";
for (std::vector <string>::const_iterator it = items.begin() ; for (std::vector <string>::const_iterator it = items.begin() ;
it != items.end() ; ++it) it != items.end() ; ++it)
@ -720,22 +628,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 // static
void IMAPUtils::convertAddressList void IMAPUtils::convertAddressList
(const IMAPParser::address_list& src, mailboxList& dest) (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 } // imap
} // net } // net
} // vmime } // vmime

View File

@ -419,49 +419,32 @@ ref <message> maildirFolder::getMessage(const int num)
} }
std::vector <ref <message> > maildirFolder::getMessages(const int from, const int to) std::vector <ref <message> > maildirFolder::getMessages(const messageSet& msgs)
{
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)
{ {
if (!isOpen()) if (!isOpen())
throw exceptions::illegal_state("Folder not open"); throw exceptions::illegal_state("Folder not open");
std::vector <ref <message> > v; if (msgs.isNumberSet())
ref <maildirFolder> thisFolder = thisRef().dynamicCast <maildirFolder>(); {
const std::vector <int> numbers = maildirUtils::messageSetToNumberList(msgs);
for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it) std::vector <ref <message> > messages;
v.push_back(vmime::create <maildirMessage>(thisFolder, *it)); ref <maildirFolder> thisFolder = thisRef().dynamicCast <maildirFolder>();
return (v); 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));
}
ref <message> maildirFolder::getMessageByUID(const message::uid& /* uid */) return messages;
{ }
throw exceptions::operation_not_supported(); else
} {
throw exceptions::operation_not_supported();
}
std::vector <ref <message> > maildirFolder::getMessagesByUID(const std::vector <message::uid>& /* uids */)
{
throw exceptions::operation_not_supported();
} }
@ -590,118 +573,15 @@ void maildirFolder::rename(const folder::path& newPath)
} }
void maildirFolder::deleteMessage(const int num) void maildirFolder::deleteMessages(const messageSet& msgs)
{ {
// Mark messages as deleted // Mark messages as deleted
setMessageFlags(num, num, message::FLAG_DELETED, message::FLAG_MODE_ADD); setMessageFlags(msgs, 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)
{
// Mark messages as deleted
setMessageFlags(nums, message::FLAG_DELETED, message::FLAG_MODE_ADD);
} }
void maildirFolder::setMessageFlags 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())
throw exceptions::illegal_state("Folder not open");
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);
for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
nums[j] = i;
// Change message flags
setMessageFlagsImpl(nums, flags, mode);
// 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)
{
if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
(*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)
{
if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
(*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::setMessageFlags
(const std::vector <int>& nums, const int flags, const int mode)
{ {
ref <maildirStore> store = m_store.acquire(); ref <maildirStore> store = m_store.acquire();
@ -712,124 +592,116 @@ void maildirFolder::setMessageFlags
else if (m_mode == MODE_READ_ONLY) else if (m_mode == MODE_READ_ONLY)
throw exceptions::illegal_state("Folder is read-only"); throw exceptions::illegal_state("Folder is read-only");
// Sort the list of message numbers if (msgs.isNumberSet())
std::vector <int> list;
list.resize(nums.size());
std::copy(nums.begin(), nums.end(), list.begin());
std::sort(list.begin(), list.end());
// Change message flags
setMessageFlagsImpl(list, flags, mode);
// Update local flags
switch (mode)
{ {
case message::FLAG_MODE_ADD: const std::vector <int> nums = maildirUtils::messageSetToNumberList(msgs);
{
for (std::vector <maildirMessage*>::iterator it = // Change message flags
m_messages.begin() ; it != m_messages.end() ; ++it) 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)
{ {
if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) && const int num = *it - 1;
(*it)->m_flags != message::FLAG_UNDEFINED)
try
{ {
(*it)->m_flags |= flags; 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)
{
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;
}
const utility::file::path::component newPath = maildirUtils::buildFilename
(maildirUtils::extractId(path), newFlags);
file->rename(curDirPath / newPath);
if (flags & message::FLAG_DELETED)
m_messageInfos[num].type = messageInfos::TYPE_DELETED;
else
m_messageInfos[num].type = messageInfos::TYPE_CUR;
m_messageInfos[num].path = newPath;
}
catch (exceptions::filesystem_exception& e)
{
// Ignore (not important)
} }
} }
break; // Update local flags
} switch (mode)
case message::FLAG_MODE_REMOVE:
{
for (std::vector <maildirMessage*>::iterator it =
m_messages.begin() ; it != m_messages.end() ; ++it)
{ {
if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) && case message::FLAG_MODE_ADD:
(*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)
{ {
if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) && for (std::vector <maildirMessage*>::iterator it =
(*it)->m_flags != message::FLAG_UNDEFINED) 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
{
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)
{
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;
} }
const utility::file::path::component newPath = maildirUtils::buildFilename break;
(maildirUtils::extractId(path), newFlags);
file->rename(curDirPath / newPath);
if (flags & message::FLAG_DELETED)
m_messageInfos[num].type = messageInfos::TYPE_DELETED;
else
m_messageInfos[num].type = messageInfos::TYPE_CUR;
m_messageInfos[num].path = newPath;
} }
catch (exceptions::filesystem_exception& e) case message::FLAG_MODE_REMOVE:
{ {
// Ignore (not important) for (std::vector <maildirMessage*>::iterator it =
m_messages.begin() ; it != m_messages.end() ; ++it)
{
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)
{
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
}
else
{
throw exceptions::operation_not_supported();
} }
} }
@ -1032,7 +904,7 @@ void maildirFolder::copyMessageImpl(const utility::file::path& tmpDirPath,
} }
void maildirFolder::copyMessage(const folder::path& dest, const int num) void maildirFolder::copyMessages(const folder::path& dest, const messageSet& msgs)
{ {
ref <maildirStore> store = m_store.acquire(); ref <maildirStore> store = m_store.acquire();
@ -1041,54 +913,6 @@ void maildirFolder::copyMessage(const folder::path& dest, const int num)
else if (!isOpen()) else if (!isOpen())
throw exceptions::illegal_state("Folder not open"); 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)
{
ref <maildirStore> store = m_store.acquire();
if (!store)
throw exceptions::illegal_state("Store disconnected");
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(); ref <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
utility::file::path curDirPath = store->getFormat()->folderPathToFileSystemPath utility::file::path curDirPath = store->getFormat()->folderPathToFileSystemPath
@ -1121,6 +945,8 @@ void maildirFolder::copyMessagesImpl(const folder::path& dest, const std::vector
} }
// Copy messages // Copy messages
const std::vector <int> nums = maildirUtils::messageSetToNumberList(msgs);
try try
{ {
for (std::vector <int>::const_iterator it = for (std::vector <int>::const_iterator it =

View File

@ -140,7 +140,7 @@ void maildirMessage::setFlags(const int flags, const int mode)
if (!folder) if (!folder)
throw exceptions::folder_not_found(); throw exceptions::folder_not_found();
folder->setMessageFlags(m_num, m_num, flags, mode); folder->setMessageFlags(messageSet::byNumber(m_num), flags, mode);
} }

View File

@ -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 // messageIdComparator
// //

369
src/net/messageSet.cpp Normal file
View File

@ -0,0 +1,369 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// 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

View File

@ -230,42 +230,7 @@ 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();
}
std::vector <ref <message> > POP3Folder::getMessages(const std::vector <int>& nums)
{ {
ref <POP3Store> store = m_store.acquire(); ref <POP3Store> store = m_store.acquire();
@ -274,18 +239,27 @@ std::vector <ref <message> > POP3Folder::getMessages(const std::vector <int>& nu
else if (!isOpen()) else if (!isOpen())
throw exceptions::illegal_state("Folder not open"); throw exceptions::illegal_state("Folder not open");
std::vector <ref <message> > v; if (msgs.isNumberSet())
ref <POP3Folder> thisFolder = thisRef().dynamicCast <POP3Folder>();
for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
{ {
if (*it < 1|| *it > m_messageCount) const std::vector <int> numbers = POP3Utils::messageSetToNumberList(msgs);
throw exceptions::message_not_found();
v.push_back(vmime::create <POP3Message>(thisFolder, *it)); std::vector <ref <message> > messages;
ref <POP3Folder> thisFolder = thisRef().dynamicCast <POP3Folder>();
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 <POP3Message>(thisFolder, *it));
}
return messages;
}
else
{
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(); ref <POP3Store> store = m_store.acquire();
if (!store) const std::vector <int> nums = POP3Utils::messageSetToNumberList(msgs);
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();
if (nums.empty()) if (nums.empty())
throw exceptions::invalid_argument(); 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 */, void POP3Folder::setMessageFlags(const messageSet& /* msgs */,
const int /* flags */, const int /* mode */)
{
throw exceptions::operation_not_supported();
}
void POP3Folder::setMessageFlags(const std::vector <int>& /* nums */,
const int /* flags */, const int /* mode */) const int /* flags */, const int /* mode */)
{ {
throw exceptions::operation_not_supported(); 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 */) void POP3Folder::copyMessages(const folder::path& /* dest */, const messageSet& /* msgs */)
{
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 */)
{ {
throw exceptions::operation_not_supported(); throw exceptions::operation_not_supported();
} }

View File

@ -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 } // pop3
} // net } // net
} // vmime } // vmime

View File

@ -0,0 +1,200 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// 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

View File

@ -38,6 +38,7 @@
#include "vmime/message.hpp" #include "vmime/message.hpp"
#include "vmime/net/message.hpp" #include "vmime/net/message.hpp"
#include "vmime/net/messageSet.hpp"
#include "vmime/net/events.hpp" #include "vmime/net/events.hpp"
#include "vmime/net/folderStatus.hpp" #include "vmime/net/folderStatus.hpp"
@ -186,38 +187,34 @@ public:
*/ */
virtual ref <message> getMessage(const int num) = 0; 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 * To retrieve messages by their number, use:
* @param to sequence number of the last message to get * \code{.cpp}
* // Get messages from sequence number 5 to sequence number 8 (including)
* folder->getMessage(vmime::net::messageSet::byNumber(5, 8));
*
* // 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));
*
* // 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 msgs index set of messages to retrieve
* @return new objects referencing the specified messages * @return new objects referencing the specified messages
* @throw exceptions::net_exception if an error occurs * @throw exceptions::net_exception if an error occurs
*/ */
virtual std::vector <ref <message> > getMessages(const int from = 1, const int to = -1) = 0; virtual std::vector <ref <message> > getMessages(const messageSet& msgs) = 0;
/** Get new references to messages in this folder, given their numbers.
*
* @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.
*
* @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.
*
* @param uids UIDs 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;
/** Return the number of messages in this folder. /** Return the number of messages in this folder.
* *
@ -249,46 +246,21 @@ public:
*/ */
virtual void rename(const folder::path& newPath) = 0; 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. /** Remove one or more messages from this folder.
* *
* @param from sequence number of the first message to delete * @param msgs index set of messages to delete
* @param to sequence number of the last message to delete
* @throw exceptions::net_exception if an error occurs * @throw exceptions::net_exception if an error occurs
*/ */
virtual void deleteMessages(const int from = 1, const int to = -1) = 0; virtual void deleteMessages(const messageSet& msgs) = 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. /** Change the flags for one or more messages in this folder.
* *
* @param from sequence number of the first message to modify * @param msgs index set of messages on which to set the flags
* @param to sequence number of the last message to modify
* @param flags set of flags (see message::Flags) * @param flags set of flags (see message::Flags)
* @param mode indicate how to treat old and new flags (see message::FlagsModes) * @param mode indicate how to treat old and new flags (see message::FlagsModes)
* @throw exceptions::net_exception if an error occurs * @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 setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET) = 0;
/** Change the flags for one or more messages in this folder.
*
* @param nums sequence numbers of the messages to modify
* @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;
/** Add a message to this folder. /** 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; 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. /** Copy messages from this folder to another folder.
* *
* @param dest destination folder path * @param dest destination folder path
* @param from sequence number of the first message to copy * @param msgs index set of messages to copy
* @param to sequence number of the last message to copy
* @throw exceptions::net_exception if an error occurs * @throw exceptions::net_exception if an error occurs
*/ */
virtual void copyMessages(const folder::path& dest, const int from = 1, const int to = -1) = 0; virtual void copyMessages(const folder::path& dest, const messageSet& msgs) = 0;
/** Copy messages from this folder to another folder.
*
* @param dest destination folder path
* @param nums sequence numbers of the messages to copy
* @throw exceptions::net_exception if an error occurs
*/
virtual void copyMessages(const folder::path& dest, const std::vector <int>& nums) = 0;
/** Request folder status without opening it. /** Request folder status without opening it.
* *

View File

@ -91,11 +91,7 @@ public:
bool isOpen() const; bool isOpen() const;
ref <message> getMessage(const int num); 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 messageSet& msgs);
std::vector <ref <message> > getMessages(const std::vector <int>& nums);
ref <message> getMessageByUID(const message::uid& uid);
std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids);
std::vector <int> getMessageNumbersStartingOnUID(const message::uid& uid); std::vector <int> getMessageNumbersStartingOnUID(const message::uid& uid);
@ -106,19 +102,14 @@ public:
void rename(const folder::path& newPath); void rename(const folder::path& newPath);
void deleteMessage(const int num); void deleteMessages(const messageSet& msgs);
void deleteMessages(const int from = 1, const int to = -1);
void deleteMessages(const std::vector <int>& nums);
void setMessageFlags(const int from, const int to, 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 setMessageFlags(const std::vector <int>& nums, 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(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 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 messageSet& msgs);
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 status(int& count, int& unseen); void status(int& count, int& unseen);
ref <folderStatus> getStatus(); ref <folderStatus> getStatus();

View File

@ -73,29 +73,6 @@ public:
static const string messageFlagList(const int flags); 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. /** Format a date/time to IMAP date/time format.
* *
* @param date date/time to format * @param date date/time to format
@ -103,25 +80,16 @@ public:
*/ */
static const string dateTime(const vmime::datetime& date); static const string dateTime(const vmime::datetime& date);
/** Construct a fetch request for the specified messages, designated by their sequence numbers. /** Construct a fetch request for the specified messages, designated
* either by their sequence numbers or their UIDs.
* *
* @param cnt connection * @param cnt connection
* @param list list of message numbers * @param msgs message set
* @param options fetch options * @param options fetch options
* @return fetch request * @return fetch request
*/ */
static const string buildFetchRequest static const string buildFetchRequest
(ref <IMAPConnection> cnt, const std::vector <int>& list, const int options); (ref <IMAPConnection> cnt, const messageSet& msgs, const int options);
/** Construct a fetch request for the specified messages, designated by their UIDs.
*
* @param cnt connection
* @param list list of message UIDs
* @param options fetch options
* @return fetch request
*/
static const string buildFetchRequest
(ref <IMAPConnection> cnt, const std::vector <message::uid>& list, const int options);
/** Convert a parser-style address list to a mailbox list. /** 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); 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: private:
static const string buildFetchRequestImpl static const string buildFetchRequestImpl

View File

@ -89,11 +89,7 @@ public:
bool isOpen() const; bool isOpen() const;
ref <message> getMessage(const int num); 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 messageSet& msgs);
std::vector <ref <message> > getMessages(const std::vector <int>& nums);
ref <message> getMessageByUID(const message::uid& uid);
std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids);
int getMessageCount(); int getMessageCount();
@ -102,19 +98,14 @@ public:
void rename(const folder::path& newPath); void rename(const folder::path& newPath);
void deleteMessage(const int num); void deleteMessages(const messageSet& msgs);
void deleteMessages(const int from = 1, const int to = -1);
void deleteMessages(const std::vector <int>& nums);
void setMessageFlags(const int from, const int to, 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 setMessageFlags(const std::vector <int>& nums, 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(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 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 messageSet& msgs);
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 status(int& count, int& unseen); void status(int& count, int& unseen);
ref <folderStatus> getStatus(); ref <folderStatus> getStatus();

View File

@ -34,6 +34,8 @@
#include "vmime/utility/file.hpp" #include "vmime/utility/file.hpp"
#include "vmime/utility/path.hpp" #include "vmime/utility/path.hpp"
#include "vmime/net/messageSet.hpp"
namespace vmime { namespace vmime {
namespace net { namespace net {
@ -129,6 +131,13 @@ public:
* @param dir directory to delete * @param dir directory to delete
*/ */
static void recursiveFSDelete(ref <utility::file> dir); 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);
}; };

335
vmime/net/messageSet.hpp Normal file
View File

@ -0,0 +1,335 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// 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

View File

@ -86,11 +86,7 @@ public:
bool isOpen() const; bool isOpen() const;
ref <message> getMessage(const int num); 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 messageSet& msgs);
std::vector <ref <message> > getMessages(const std::vector <int>& nums);
ref <message> getMessageByUID(const message::uid& uid);
std::vector <ref <message> > getMessagesByUID(const std::vector <message::uid>& uids);
int getMessageCount(); int getMessageCount();
@ -99,19 +95,14 @@ public:
void rename(const folder::path& newPath); void rename(const folder::path& newPath);
void deleteMessage(const int num); void deleteMessages(const messageSet& msgs);
void deleteMessages(const int from = 1, const int to = -1);
void deleteMessages(const std::vector <int>& nums);
void setMessageFlags(const int from, const int to, 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 setMessageFlags(const std::vector <int>& nums, 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(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 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 messageSet& msgs);
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 status(int& count, int& unseen); void status(int& count, int& unseen);
ref <folderStatus> getStatus(); ref <folderStatus> getStatus();

View File

@ -35,6 +35,8 @@
#include "vmime/types.hpp" #include "vmime/types.hpp"
#include "vmime/net/messageSet.hpp"
namespace vmime { namespace vmime {
namespace net { namespace net {
@ -63,6 +65,13 @@ public:
*/ */
static void parseMultiListOrUidlResponse static void parseMultiListOrUidlResponse
(ref <POP3Response> response, std::map <int, string>& result); (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);
}; };