Added support for APPENDUID/COPYUID (UIDPLUS extension for IMAP).

This commit is contained in:
Vincent Richard 2013-12-30 10:26:10 +01:00
parent 28398076c2
commit fab5cd79b6
13 changed files with 398 additions and 38 deletions

View File

@ -563,6 +563,8 @@ static void connectStore()
choices.push_back("Status");
choices.push_back("List folders");
choices.push_back("Change folder");
choices.push_back("Add message (to the current folder)");
choices.push_back("Copy message (into the current folder)");
choices.push_back("Return to main menu");
const int choice = printMenu(choices);
@ -570,7 +572,7 @@ static void connectStore()
// Request message number
vmime::shared_ptr <vmime::net::message> msg;
if (choice != 6 && choice != 7 && choice != 8)
if (choice == 1 || choice == 2 || choice == 3 || choice == 4 || choice == 5 || choice == 10)
{
std::cout << "Enter message number: ";
std::cout.flush();
@ -583,7 +585,7 @@ static void connectStore()
int num = 0;
iss >> num;
if (num < 1 || num > count)
if (num < 1 || num > f->getMessageCount())
{
std::cerr << "Invalid message number." << std::endl;
continue;
@ -722,8 +724,91 @@ static void connectStore()
break;
}
// Main menu
// Add message
case 9:
{
vmime::messageBuilder mb;
mb.setExpeditor(vmime::mailbox("me@somewhere.com"));
vmime::addressList to;
to.appendAddress(vmime::make_shared <vmime::mailbox>("you@elsewhere.com"));
mb.setRecipients(to);
mb.setSubject(vmime::text("Test message from VMime example6"));
mb.getTextPart()->setText(vmime::make_shared <vmime::stringContentHandler>(
"Body of test message from VMime example6."));
vmime::shared_ptr <vmime::message> msg = mb.construct();
vmime::net::messageSet set = f->addMessage(msg);
if (set.isEmpty())
{
std::cout << "Message has successfully been added, "
<< "but its UID/number is not known." << std::endl;
}
else
{
const vmime::net::messageRange& range = set.getRangeAt(0);
if (set.isUIDSet())
{
const vmime::net::message::uid uid =
dynamic_cast <const vmime::net::UIDMessageRange&>(range).getFirst();
std::cout << "Message has successfully been added, "
<< "its UID is '" << uid << "'." << std::endl;
}
else
{
const int number =
dynamic_cast <const vmime::net::numberMessageRange&>(range).getFirst();
std::cout << "Message has successfully been added, "
<< "its number is '" << number << "'." << std::endl;
}
}
break;
}
// Copy message
case 10:
{
vmime::net::messageSet set = f->copyMessages(f->getFullPath(),
vmime::net::messageSet::byNumber(msg->getNumber()));
if (set.isEmpty())
{
std::cout << "Message has successfully been copied, "
<< "but its UID/number is not known." << std::endl;
}
else
{
const vmime::net::messageRange& range = set.getRangeAt(0);
if (set.isUIDSet())
{
const vmime::net::message::uid uid =
dynamic_cast <const vmime::net::UIDMessageRange&>(range).getFirst();
std::cout << "Message has successfully been copied, "
<< "its UID is '" << uid << "'." << std::endl;
}
else
{
const int number =
dynamic_cast <const vmime::net::numberMessageRange&>(range).getFirst();
std::cout << "Message has successfully been copied, "
<< "its number is '" << number << "'." << std::endl;
}
}
break;
}
// Main menu
case 11:
f->close(true); // 'true' to expunge deleted messages
cont = false;

View File

@ -269,9 +269,16 @@ public:
* @param flags flags for the new message
* @param date date/time for the new message (if NULL, the current time is used)
* @param progress progress listener, or NULL if not used
* @return a message set containing the number or UID of the new message, or
* an empty set if the information could not be obtained (ie. the server does not
* support returning the number or UID of an added message)
* @throw exceptions::net_exception if an error occurs
*/
virtual void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL) = 0;
virtual messageSet addMessage
(shared_ptr <vmime::message> msg,
const int flags = message::FLAG_UNDEFINED,
vmime::datetime* date = NULL,
utility::progressListener* progress = NULL) = 0;
/** Add a message to this folder.
*
@ -280,17 +287,29 @@ public:
* @param flags flags for the new message
* @param date date/time for the new message (if NULL, the current time is used)
* @param progress progress listener, or NULL if not used
* @return a message set containing the number or UID of the new message, or
* an empty set if the information could not be obtained (ie. the server does not
* support returning the number or UID of an added message)
* @throw exceptions::net_exception if an error occurs
*/
virtual void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL) = 0;
virtual messageSet addMessage
(utility::inputStream& is,
const size_t size,
const int flags = message::FLAG_UNDEFINED,
vmime::datetime* date = NULL,
utility::progressListener* progress = NULL) = 0;
/** Copy messages from this folder to another folder.
*
* @param dest destination folder path
* @param msgs index set of messages to copy
* @return a message set containing the number(s) or UID(s) of the copied message(s),
* or an empty set if the information could not be obtained (ie. the server does not
* support returning the number or UID of a copied message)
* @throw exceptions::net_exception if an error occurs
*/
virtual void copyMessages(const folder::path& dest, const messageSet& msgs) = 0;
virtual messageSet copyMessages
(const folder::path& dest, const messageSet& msgs) = 0;
/** Request folder status without opening it.
*

View File

@ -990,7 +990,8 @@ void IMAPFolder::setMessageFlags(const messageSet& msgs, const int flags, const
}
void IMAPFolder::addMessage(shared_ptr <vmime::message> msg, const int flags,
messageSet IMAPFolder::addMessage
(shared_ptr <vmime::message> msg, const int flags,
vmime::datetime* date, utility::progressListener* progress)
{
std::ostringstream oss;
@ -1001,11 +1002,12 @@ void IMAPFolder::addMessage(shared_ptr <vmime::message> msg, const int flags,
const string& str = oss.str();
utility::inputStreamStringAdapter strAdapter(str);
addMessage(strAdapter, str.length(), flags, date, progress);
return addMessage(strAdapter, str.length(), flags, date, progress);
}
void IMAPFolder::addMessage(utility::inputStream& is, const size_t size, const int flags,
messageSet IMAPFolder::addMessage
(utility::inputStream& is, const size_t size, const int flags,
vmime::datetime* date, utility::progressListener* progress)
{
shared_ptr <IMAPStore> store = m_store.lock();
@ -1063,6 +1065,8 @@ void IMAPFolder::addMessage(utility::inputStream& is, const size_t size, const i
resp->getErrorLog(), "bad response");
}
processStatusUpdate(resp.get());
// Send message data
const size_t total = size;
size_t current = 0;
@ -1105,7 +1109,15 @@ void IMAPFolder::addMessage(utility::inputStream& is, const size_t size, const i
resp->getErrorLog(), "bad response");
}
processStatusUpdate(resp.get());
processStatusUpdate(finalResp.get());
const IMAPParser::resp_text_code* respTextCode =
finalResp->response_done()->response_tagged()->resp_cond_state()->resp_text()->resp_text_code();
if (respTextCode->type() == IMAPParser::resp_text_code::APPENDUID)
return IMAPUtils::buildMessageSet(respTextCode->uid_set());
return messageSet::empty();
}
@ -1209,7 +1221,7 @@ void IMAPFolder::rename(const folder::path& newPath)
}
void IMAPFolder::copyMessages(const folder::path& dest, const messageSet& set)
messageSet IMAPFolder::copyMessages(const folder::path& dest, const messageSet& set)
{
shared_ptr <IMAPStore> store = m_store.lock();
@ -1240,6 +1252,14 @@ void IMAPFolder::copyMessages(const folder::path& dest, const messageSet& set)
}
processStatusUpdate(resp.get());
const IMAPParser::resp_text_code* respTextCode =
resp->response_done()->response_tagged()->resp_cond_state()->resp_text()->resp_text_code();
if (respTextCode->type() == IMAPParser::resp_text_code::COPYUID)
return IMAPUtils::buildMessageSet(respTextCode->uid_set2());
return messageSet::empty();
}

View File

@ -105,10 +105,20 @@ public:
void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET);
void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
messageSet addMessage
(shared_ptr <vmime::message> msg,
const int flags = message::FLAG_UNDEFINED,
vmime::datetime* date = NULL,
utility::progressListener* progress = NULL);
void copyMessages(const folder::path& dest, const messageSet& msgs);
messageSet addMessage
(utility::inputStream& is,
const size_t size,
const int flags = message::FLAG_UNDEFINED,
vmime::datetime* date = NULL,
utility::progressListener* progress = NULL);
messageSet copyMessages(const folder::path& dest, const messageSet& msgs);
void status(int& count, int& unseen);
shared_ptr <folderStatus> getStatus();

View File

@ -551,6 +551,119 @@ public:
};
//
// uniqueid ::= nz_number
// ;; Strictly ascending
//
class uniqueid : public nz_number
{
public:
uniqueid() : nz_number()
{
}
};
// uid-range = (uniqueid ":" uniqueid)
// ; two uniqueid values and all values
// ; between these two regards of order.
// ; Example: 2:4 and 4:2 are equivalent.
class uid_range : public component
{
public:
uid_range()
: m_uniqueid1(NULL), m_uniqueid2(NULL)
{
}
~uid_range()
{
delete m_uniqueid1;
delete m_uniqueid2;
}
void go(IMAPParser& parser, string& line, size_t* currentPos)
{
DEBUG_ENTER_COMPONENT("uid_range");
size_t pos = *currentPos;
m_uniqueid1 = parser.get <uniqueid>(line, &pos);
parser.check <one_char <','> >(line, &pos);
m_uniqueid2 = parser.get <uniqueid>(line, &pos);
*currentPos = pos;
}
private:
uniqueid* m_uniqueid1;
uniqueid* m_uniqueid2;
public:
uniqueid* uniqueid1() const { return m_uniqueid1; }
uniqueid* uniqueid2() const { return m_uniqueid2; }
};
//
// uid-set = (uniqueid / uid-range) *("," uid-set)
//
class uid_set : public component
{
public:
uid_set()
: m_uniqueid(NULL), m_uid_range(NULL), m_next_uid_set(NULL)
{
}
~uid_set()
{
delete m_uniqueid;
delete m_uid_range;
delete m_next_uid_set;
}
void go(IMAPParser& parser, string& line, size_t* currentPos)
{
DEBUG_ENTER_COMPONENT("uid_set");
size_t pos = *currentPos;
// We have either a 'uid_range' or a 'uniqueid'
if (!(m_uid_range = parser.get <IMAPParser::uid_range>(line, &pos, true)))
m_uniqueid = parser.get <IMAPParser::uniqueid>(line, &pos);
// And maybe another 'uid-set' following
if (parser.check <one_char <','> >(line, &pos, true))
m_next_uid_set = parser.get <IMAPParser::uid_set>(line, &pos);
*currentPos = pos;
}
private:
IMAPParser::uniqueid* m_uniqueid;
IMAPParser::uid_range* m_uid_range;
IMAPParser::uid_set* m_next_uid_set;
public:
IMAPParser::uniqueid* uniqueid() const { return m_uniqueid; }
IMAPParser::uid_range* uid_range() const { return m_uid_range; }
IMAPParser::uid_set* next_uid_set() const { return m_next_uid_set; }
};
//
// text ::= 1*TEXT_CHAR
//
@ -3850,10 +3963,6 @@ public:
};
//
// uniqueid ::= nz_number
// ;; Strictly ascending
//
// msg_att_item ::= "ENVELOPE" SPACE envelope /
// "FLAGS" SPACE "(" #(flag / "\Recent") ")" /
// "INTERNALDATE" SPACE date_time /
@ -4020,7 +4129,7 @@ public:
parser.checkWithArg <special_atom>(line, &pos, "uid");
parser.check <SPACE>(line, &pos);
m_uniqueid = parser.get <nz_number>(line, &pos);
m_uniqueid = parser.get <uniqueid>(line, &pos);
}
*currentPos = pos;
@ -4050,7 +4159,7 @@ public:
IMAPParser::date_time* m_date_time;
IMAPParser::number* m_number;
IMAPParser::envelope* m_envelope;
IMAPParser::nz_number* m_uniqueid;
IMAPParser::uniqueid* m_uniqueid;
IMAPParser::nstring* m_nstring;
IMAPParser::xbody* m_body;
IMAPParser::flag_list* m_flag_list;
@ -4064,7 +4173,7 @@ public:
const IMAPParser::date_time* date_time() const { return (m_date_time); }
const IMAPParser::number* number() const { return (m_number); }
const IMAPParser::envelope* envelope() const { return (m_envelope); }
const IMAPParser::nz_number* unique_id() const { return (m_uniqueid); }
const IMAPParser::uniqueid* unique_id() const { return (m_uniqueid); }
const IMAPParser::nstring* nstring() const { return (m_nstring); }
const IMAPParser::xbody* body() const { return (m_body); }
const IMAPParser::flag_list* flag_list() const { return (m_flag_list); }
@ -4195,6 +4304,7 @@ public:
// "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
// "UIDVALIDITY" SPACE nz_number /
// "UNSEEN" SPACE nz_number /
// "UIDNEXT" SPACE nz-number /
// atom [SPACE 1*<any TEXT_CHAR except "]">]
//
// IMAP Extension for Conditional STORE (RFC-4551):
@ -4202,6 +4312,12 @@ public:
// resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value /
// "NOMODSEQ" /
// "MODIFIED" SP set
//
// IMAP UIDPLUS Extension (RFC-4315):
//
// resp-text-code =/ "APPENDUID" SP nz-number SP append-uid /
// "COPYUID" SP nz-number SP uid-set SP uid-set /
// "UIDNOTSTICKY"
class resp_text_code : public component
{
@ -4313,6 +4429,33 @@ public:
m_sequence_set = parser.get <IMAPParser::sequence_set>(line, &pos);
}
// "APPENDUID" SP nz-number SP append-uid
else if (parser.checkWithArg <special_atom>(line, &pos, "appenduid", true))
{
m_type = APPENDUID;
parser.check <SPACE>(line, &pos);
m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos);
parser.check <SPACE>(line, &pos);
m_uid_set = parser.get <IMAPParser::uid_set>(line, &pos);
}
// "COPYUID" SP nz-number SP uid-set SP uid-set
else if (parser.checkWithArg <special_atom>(line, &pos, "copyuid", true))
{
m_type = COPYUID;
parser.check <SPACE>(line, &pos);
m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos);
parser.check <SPACE>(line, &pos);
m_uid_set = parser.get <IMAPParser::uid_set>(line, &pos);
parser.check <SPACE>(line, &pos);
m_uid_set2 = parser.get <IMAPParser::uid_set>(line, &pos);
}
// "UIDNOTSTICKY"
else if (parser.checkWithArg <special_atom>(line, &pos, "uidnotsticky", true))
{
m_type = UIDNOTSTICKY;
}
// atom [SPACE 1*<any TEXT_CHAR except "]">]
else
{
@ -4334,6 +4477,9 @@ public:
HIGHESTMODSEQ,
NOMODSEQ,
MODIFIED,
APPENDUID,
COPYUID,
UIDNOTSTICKY,
// Standard IMAP
ALERT,
@ -4360,6 +4506,8 @@ public:
IMAPParser::mod_sequence_value* m_mod_sequence_value;
IMAPParser::sequence_set* m_sequence_set;
IMAPParser::capability_data* m_capability_data;
IMAPParser::uid_set* m_uid_set;
IMAPParser::uid_set* m_uid_set2;
public:
@ -4372,6 +4520,8 @@ public:
const IMAPParser::mod_sequence_value* mod_sequence_value() const { return m_mod_sequence_value; }
const IMAPParser::sequence_set* sequence_set() const { return m_sequence_set; }
const IMAPParser::capability_data* capability_data() const { return m_capability_data; }
const IMAPParser::uid_set* uid_set() const { return (m_uid_set); }
const IMAPParser::uid_set* uid_set2() const { return (m_uid_set2); }
};

View File

@ -749,6 +749,30 @@ const std::vector <int> IMAPUtils::messageSetToNumberList(const messageSet& msgs
}
// static
messageSet IMAPUtils::buildMessageSet(const IMAPParser::uid_set* uidSet)
{
messageSet set = messageSet::empty();
for ( ; uidSet ; uidSet = uidSet->next_uid_set())
{
if (uidSet->uid_range())
{
set.addRange(UIDMessageRange
(message::uid(uidSet->uid_range()->uniqueid1()->value()),
message::uid(uidSet->uid_range()->uniqueid2()->value())));
}
else
{
set.addRange(UIDMessageRange
(message::uid(uidSet->uniqueid()->value())));
}
}
return set;
}
} // imap
} // net
} // vmime

View File

@ -112,6 +112,13 @@ public:
*/
static const std::vector <int> messageSetToNumberList(const messageSet& msgs);
/** Constructs a message set from a parser 'uid_set' structure.
*
* @param uidSet UID set, as returned by the parser
* @return message set
*/
static messageSet buildMessageSet(const IMAPParser::uid_set* uidSet);
private:
static const string buildFetchRequestImpl

View File

@ -704,7 +704,8 @@ void maildirFolder::setMessageFlags
}
void maildirFolder::addMessage(shared_ptr <vmime::message> msg, const int flags,
messageSet maildirFolder::addMessage
(shared_ptr <vmime::message> msg, const int flags,
vmime::datetime* date, utility::progressListener* progress)
{
std::ostringstream oss;
@ -715,11 +716,12 @@ void maildirFolder::addMessage(shared_ptr <vmime::message> msg, const int flags,
const string& str = oss.str();
utility::inputStreamStringAdapter strAdapter(str);
addMessage(strAdapter, str.length(), flags, date, progress);
return addMessage(strAdapter, str.length(), flags, date, progress);
}
void maildirFolder::addMessage(utility::inputStream& is, const size_t size,
messageSet maildirFolder::addMessage
(utility::inputStream& is, const size_t size,
const int flags, vmime::datetime* /* date */, utility::progressListener* progress)
{
shared_ptr <maildirStore> store = m_store.lock();
@ -810,6 +812,8 @@ void maildirFolder::addMessage(utility::inputStream& is, const size_t size,
(*it)->notifyMessageCount(event);
}
}
return messageSet::empty();
}
@ -902,7 +906,7 @@ void maildirFolder::copyMessageImpl(const utility::file::path& tmpDirPath,
}
void maildirFolder::copyMessages(const folder::path& dest, const messageSet& msgs)
messageSet maildirFolder::copyMessages(const folder::path& dest, const messageSet& msgs)
{
shared_ptr <maildirStore> store = m_store.lock();
@ -972,6 +976,8 @@ void maildirFolder::copyMessages(const folder::path& dest, const messageSet& msg
}
notifyMessagesCopied(dest);
return messageSet::empty();
}

View File

@ -102,10 +102,10 @@ public:
void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET);
void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
messageSet addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
messageSet addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void copyMessages(const folder::path& dest, const messageSet& msgs);
messageSet copyMessages(const folder::path& dest, const messageSet& msgs);
void status(int& count, int& unseen);
shared_ptr <folderStatus> getStatus();

View File

@ -163,6 +163,13 @@ messageSet::~messageSet()
}
// static
messageSet messageSet::empty()
{
return messageSet();
}
// static
messageSet messageSet::byNumber(const int number)
{
@ -365,5 +372,17 @@ bool messageSet::isUIDSet() const
}
size_t messageSet::getRangeCount() const
{
return m_ranges.size();
}
const messageRange& messageSet::getRangeAt(const size_t i) const
{
return *m_ranges[i];
}
} // net
} // vmime

View File

@ -214,6 +214,12 @@ public:
messageSet(const messageSet& other);
/** Constructs an empty set.
*
* @return new empty message set
*/
static messageSet empty();
/** Constructs a new message set and initializes it with a single
* message represented by its sequence number.
*
@ -320,6 +326,19 @@ public:
*/
bool isUIDSet() const;
/** Returns the number of ranges contained in this set.
*
* @return range count
*/
size_t getRangeCount() const;
/** Returns the message range at the specified index.
*
* @param i range index (from 0 to getRangeCount())
* @return a reference to the message range at the specified index
*/
const messageRange& getRangeAt(const size_t i) const;
private:
messageSet();

View File

@ -601,7 +601,7 @@ void POP3Folder::rename(const folder::path& /* newPath */)
}
void POP3Folder::addMessage
messageSet POP3Folder::addMessage
(shared_ptr <vmime::message> /* msg */, const int /* flags */,
vmime::datetime* /* date */, utility::progressListener* /* progress */)
{
@ -609,7 +609,7 @@ void POP3Folder::addMessage
}
void POP3Folder::addMessage
messageSet POP3Folder::addMessage
(utility::inputStream& /* is */, const size_t /* size */, const int /* flags */,
vmime::datetime* /* date */, utility::progressListener* /* progress */)
{
@ -617,7 +617,8 @@ void POP3Folder::addMessage
}
void POP3Folder::copyMessages(const folder::path& /* dest */, const messageSet& /* msgs */)
messageSet POP3Folder::copyMessages
(const folder::path& /* dest */, const messageSet& /* msgs */)
{
throw exceptions::operation_not_supported();
}

View File

@ -99,10 +99,10 @@ public:
void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET);
void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
messageSet addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
messageSet addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
void copyMessages(const folder::path& dest, const messageSet& msgs);
messageSet copyMessages(const folder::path& dest, const messageSet& msgs);
void status(int& count, int& unseen);
shared_ptr <folderStatus> getStatus();