From 9a9794cb7d91893ec720a1e1a4aab1c52ae13e1b Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 18 Jul 2013 21:51:09 +0200 Subject: [PATCH] Reworked IMAP event notification code. --- src/net/events.cpp | 59 +++- src/net/folder.cpp | 29 +- src/net/imap/IMAPFolder.cpp | 338 +++++++--------------- src/net/imap/IMAPFolderStatus.cpp | 165 +++++++++-- src/net/imap/IMAPMessage.cpp | 134 ++------- src/net/maildir/maildirFolder.cpp | 91 +++--- src/net/maildir/maildirFolderStatus.cpp | 21 ++ src/net/pop3/POP3Folder.cpp | 35 ++- src/net/pop3/POP3FolderStatus.cpp | 21 ++ vmime/net/events.hpp | 56 +++- vmime/net/folder.hpp | 7 +- vmime/net/folderStatus.hpp | 6 + vmime/net/imap/IMAPFolder.hpp | 9 + vmime/net/imap/IMAPFolderStatus.hpp | 9 +- vmime/net/imap/IMAPMessage.hpp | 20 +- vmime/net/maildir/maildirFolderStatus.hpp | 6 + vmime/net/message.hpp | 4 +- vmime/net/pop3/POP3FolderStatus.hpp | 6 + 18 files changed, 564 insertions(+), 452 deletions(-) diff --git a/src/net/events.cpp b/src/net/events.cpp index 7dbb4adb..8cc23706 100644 --- a/src/net/events.cpp +++ b/src/net/events.cpp @@ -38,10 +38,27 @@ namespace net { namespace events { +// +// event +// + +event::event() +{ +} + + +event::~event() +{ +} + + // // messageCountEvent // +const char* messageCountEvent::EVENT_CLASS = "messageCountEvent"; + + messageCountEvent::messageCountEvent (ref folder, const Types type, const std::vector & nums) : m_folder(folder), m_type(type) @@ -56,12 +73,18 @@ messageCountEvent::Types messageCountEvent::getType() const { return (m_type); } const std::vector & messageCountEvent::getNumbers() const { return (m_nums); } -void messageCountEvent::dispatch(messageCountListener* listener) const +void messageCountEvent::dispatch(messageCountListener* listener) { if (m_type == TYPE_ADDED) - listener->messagesAdded(*this); + listener->messagesAdded(thisRef().dynamicCast ()); else - listener->messagesRemoved(*this); + listener->messagesRemoved(thisRef().dynamicCast ()); +} + + +const char* messageCountEvent::getClass() const +{ + return EVENT_CLASS; } @@ -69,6 +92,9 @@ void messageCountEvent::dispatch(messageCountListener* listener) const // messageChangedEvent // +const char* messageChangedEvent::EVENT_CLASS = "messageChangedEvent"; + + messageChangedEvent::messageChangedEvent (ref folder, const Types type, const std::vector & nums) : m_folder(folder), m_type(type) @@ -83,9 +109,15 @@ messageChangedEvent::Types messageChangedEvent::getType() const { return (m_type const std::vector & messageChangedEvent::getNumbers() const { return (m_nums); } -void messageChangedEvent::dispatch(messageChangedListener* listener) const +void messageChangedEvent::dispatch(messageChangedListener* listener) { - listener->messageChanged(*this); + listener->messageChanged(thisRef().dynamicCast ()); +} + + +const char* messageChangedEvent::getClass() const +{ + return EVENT_CLASS; } @@ -93,6 +125,9 @@ void messageChangedEvent::dispatch(messageChangedListener* listener) const // folderEvent // +const char* folderEvent::EVENT_CLASS = "folderEvent"; + + folderEvent::folderEvent (ref folder, const Types type, const utility::path& oldPath, const utility::path& newPath) @@ -105,17 +140,23 @@ ref folderEvent::getFolder() const { return (m_folder); } folderEvent::Types folderEvent::getType() const { return (m_type); } -void folderEvent::dispatch(folderListener* listener) const +void folderEvent::dispatch(folderListener* listener) { switch (m_type) { - case TYPE_CREATED: listener->folderCreated(*this); break; - case TYPE_RENAMED: listener->folderRenamed(*this); break; - case TYPE_DELETED: listener->folderDeleted(*this); break; + case TYPE_CREATED: listener->folderCreated(thisRef().dynamicCast ()); break; + case TYPE_RENAMED: listener->folderRenamed(thisRef().dynamicCast ()); break; + case TYPE_DELETED: listener->folderDeleted(thisRef().dynamicCast ()); break; } } +const char* folderEvent::getClass() const +{ + return EVENT_CLASS; +} + + } // events } // net } // vmime diff --git a/src/net/folder.cpp b/src/net/folder.cpp index 58523399..1cc618d4 100644 --- a/src/net/folder.cpp +++ b/src/net/folder.cpp @@ -48,12 +48,12 @@ void folder::removeMessageChangedListener(events::messageChangedListener* l) } -void folder::notifyMessageChanged(const events::messageChangedEvent& event) +void folder::notifyMessageChanged(ref event) { for (std::list ::iterator it = m_messageChangedListeners.begin() ; it != m_messageChangedListeners.end() ; ++it) { - event.dispatch(*it); + event->dispatch(*it); } } @@ -70,12 +70,12 @@ void folder::removeMessageCountListener(events::messageCountListener* l) } -void folder::notifyMessageCount(const events::messageCountEvent& event) +void folder::notifyMessageCount(ref event) { for (std::list ::iterator it = m_messageCountListeners.begin() ; it != m_messageCountListeners.end() ; ++it) { - event.dispatch(*it); + event->dispatch(*it); } } @@ -92,12 +92,29 @@ void folder::removeFolderListener(events::folderListener* l) } -void folder::notifyFolder(const events::folderEvent& event) +void folder::notifyFolder(ref event) { for (std::list ::iterator it = m_folderListeners.begin() ; it != m_folderListeners.end() ; ++it) { - event.dispatch(*it); + event->dispatch(*it); + } +} + + +void folder::notifyEvent(ref event) +{ + if (event->getClass() == events::messageCountEvent::EVENT_CLASS) + { + notifyMessageCount(event.dynamicCast ()); + } + else if (event->getClass() == events::messageChangedEvent::EVENT_CLASS) + { + notifyMessageChanged(event.dynamicCast ()); + } + else if (event->getClass() == events::folderEvent::EVENT_CLASS) + { + notifyFolder(event.dynamicCast ()); } } diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index cbcfd418..60133a29 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -381,9 +381,10 @@ void IMAPFolder::create(const int type) } // Notify folder created - events::folderEvent event - (thisRef().dynamicCast (), - events::folderEvent::TYPE_CREATED, m_path, m_path); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::folderEvent::TYPE_CREATED, m_path, m_path); notifyFolder(event); } @@ -418,9 +419,10 @@ void IMAPFolder::destroy() } // Notify folder deleted - events::folderEvent event - (thisRef().dynamicCast (), - events::folderEvent::TYPE_DELETED, m_path, m_path); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::folderEvent::TYPE_DELETED, m_path, m_path); notifyFolder(event); } @@ -674,6 +676,15 @@ int IMAPFolder::getMessageCount() } +vmime_uint64 IMAPFolder::getHighestModSequence() const +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return m_status->getHighestModSeq(); +} + + ref IMAPFolder::getFolder(const folder::path::component& name) { ref store = m_store.acquire(); @@ -965,7 +976,7 @@ void IMAPFolder::deleteMessages(const int from, const int to) else command << to; } - command << " +FLAGS.SILENT (\\Deleted)"; + command << " +FLAGS (\\Deleted)"; // Send the request m_connection->send(true, command.str(), true); @@ -980,33 +991,6 @@ void IMAPFolder::deleteMessages(const int from, const int to) resp->getErrorLog(), "bad response"); } - // Update local flags - const int to2 = (to == -1) ? m_status->getMessageCount() : to; - const int count = to - from + 1; - - for (std::vector ::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 |= message::FLAG_DELETED; - } - } - - // Notify message flags changed - std::vector nums; - nums.resize(count); - - for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) - nums[j] = i; - - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, nums); - - notifyMessageChanged(event); - processStatusUpdate(resp); } @@ -1039,7 +1023,7 @@ void IMAPFolder::deleteMessages(const std::vector & nums) command << "STORE "; command << IMAPUtils::listToSet(list, m_status->getMessageCount(), true); - command << " +FLAGS.SILENT (\\Deleted)"; + command << " +FLAGS (\\Deleted)"; // Send the request m_connection->send(true, command.str(), true); @@ -1054,24 +1038,6 @@ void IMAPFolder::deleteMessages(const std::vector & nums) resp->getErrorLog(), "bad response"); } - // Update local flags - for (std::vector ::iterator it = - m_messages.begin() ; it != m_messages.end() ; ++it) - { - if (std::binary_search(list.begin(), list.end(), (*it)->getNumber())) - { - if ((*it)->m_flags != message::FLAG_UNDEFINED) - (*it)->m_flags |= message::FLAG_DELETED; - } - } - - // Notify message flags changed - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, list); - - notifyMessageChanged(event); - processStatusUpdate(resp); } @@ -1093,77 +1059,19 @@ void IMAPFolder::setMessageFlags(const int from, const int to, const int flags, std::ostringstream oss; oss.imbue(std::locale::classic()); - if (to == -1) - oss << from << ":*"; + if (from == to) + { + oss << from; + } else - oss << from << ":" << to; + { + if (to == -1) + oss << from << ":*"; + else + oss << from << ":" << to; + } setMessageFlagsImpl(oss.str(), flags, mode); - - // Update local flags - const int to2 = (to == -1) ? m_status->getMessageCount() : to; - const int count = to - from + 1; - - switch (mode) - { - case message::FLAG_MODE_ADD: - { - for (std::vector ::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 ::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 ::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 - std::vector nums; - nums.resize(count); - - for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) - nums[j] = i; - - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, nums); - - notifyMessageChanged(event); } @@ -1183,67 +1091,10 @@ void IMAPFolder::setMessageFlags(const std::vector & nums, const int flags, 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); - - // Update local flags - switch (mode) - { - case message::FLAG_MODE_ADD: - { - for (std::vector ::iterator it = - m_messages.begin() ; it != m_messages.end() ; ++it) - { - if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) && - (*it)->m_flags != message::FLAG_UNDEFINED) - { - (*it)->m_flags |= flags; - } - } - - break; - } - case message::FLAG_MODE_REMOVE: - { - for (std::vector ::iterator it = - m_messages.begin() ; it != m_messages.end() ; ++it) - { - if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) && - (*it)->m_flags != message::FLAG_UNDEFINED) - { - (*it)->m_flags &= ~flags; - } - } - - break; - } - default: - case message::FLAG_MODE_SET: - { - for (std::vector ::iterator it = - m_messages.begin() ; it != m_messages.end() ; ++it) - { - if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) && - (*it)->m_flags != message::FLAG_UNDEFINED) - { - (*it)->m_flags = flags; - } - } - - break; - } - - } - - // Notify message flags changed - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, nums); - - notifyMessageChanged(event); } @@ -1257,10 +1108,10 @@ void IMAPFolder::setMessageFlagsImpl(const string& set, const int flags, const i switch (mode) { - case message::FLAG_MODE_ADD: command << " +FLAGS.SILENT "; break; - case message::FLAG_MODE_REMOVE: command << " -FLAGS.SILENT "; break; + case message::FLAG_MODE_ADD: command << " +FLAGS "; break; + case message::FLAG_MODE_REMOVE: command << " -FLAGS "; break; default: - case message::FLAG_MODE_SET: command << " FLAGS.SILENT "; break; + case message::FLAG_MODE_SET: command << " FLAGS "; break; } const string flagList = IMAPUtils::messageFlagList(flags); @@ -1367,7 +1218,7 @@ void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int if (progress) progress->start(total); - const socket::size_type blockSize = std::min(is.getBlockSize(), + const socket::size_type blockSize = std::min(is.getBlockSize(), static_cast (m_connection->getSocket()->getBlockSize())); std::vector vbuffer(blockSize); @@ -1430,45 +1281,6 @@ void IMAPFolder::expunge() resp->getErrorLog(), "bad response"); } - // Update the numbering of the messages - const std::vector & respDataList = - resp->continue_req_or_response_data(); - - std::vector nums; - - for (std::vector ::const_iterator - it = respDataList.begin() ; it != respDataList.end() ; ++it) - { - if ((*it)->response_data() == NULL) - { - throw exceptions::command_error("EXPUNGE", - resp->getErrorLog(), "invalid response"); - } - - const IMAPParser::message_data* messageData = - (*it)->response_data()->message_data(); - - // We are only interested in responses of type "EXPUNGE" - if (messageData == NULL || - messageData->type() != IMAPParser::message_data::EXPUNGE) - { - continue; - } - - const int number = messageData->number(); - - nums.push_back(number); - - for (std::vector ::iterator jt = - m_messages.begin() ; jt != m_messages.end() ; ++jt) - { - if ((*jt)->m_num == number) - (*jt)->m_expunged = true; - else if ((*jt)->m_num > number) - (*jt)->m_num--; - } - } - processStatusUpdate(resp); } @@ -1515,9 +1327,10 @@ void IMAPFolder::rename(const folder::path& newPath) m_path = newPath; m_name = newPath.getLastComponent(); - events::folderEvent event - (thisRef().dynamicCast (), - events::folderEvent::TYPE_RENAMED, oldPath, newPath); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::folderEvent::TYPE_RENAMED, oldPath, newPath); notifyFolder(event); @@ -1531,9 +1344,10 @@ void IMAPFolder::rename(const folder::path& newPath) (*it)->m_path.renameParent(oldPath, newPath); - events::folderEvent event - ((*it)->thisRef().dynamicCast (), - events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path); + ref event = + vmime::create + ((*it)->thisRef().dynamicCast (), + events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path); (*it)->notifyFolder(event); } @@ -1784,6 +1598,11 @@ std::vector IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& void IMAPFolder::processStatusUpdate(const IMAPParser::response* resp) { + std::vector > events; + + ref oldStatus = m_status->clone().dynamicCast (); + int expungedMessageCount = 0; + // Process tagged response if (resp->response_done() && resp->response_done()->response_tagged() && resp->response_done()->response_tagged() @@ -1813,11 +1632,74 @@ void IMAPFolder::processStatusUpdate(const IMAPParser::response* resp) { m_status->updateFromResponse((*it)->response_data()->mailbox_data()); } + else if ((*it)->response_data() && (*it)->response_data()->message_data()) + { + const IMAPParser::message_data* msgData = (*it)->response_data()->message_data(); + const int msgNumber = msgData->number(); + + if ((*it)->response_data()->message_data()->type() == IMAPParser::message_data::FETCH) + { + // Message changed + for (std::vector ::iterator mit = + m_messages.begin() ; mit != m_messages.end() ; ++mit) + { + if ((*mit)->getNumber() == msgNumber) + (*mit)->processFetchResponse(/* options */ 0, msgData); + } + + events.push_back(vmime::create + (thisRef().dynamicCast (), + events::messageChangedEvent::TYPE_FLAGS, + std::vector (1, msgNumber))); + } + else if ((*it)->response_data()->message_data()->type() == IMAPParser::message_data::EXPUNGE) + { + // A message has been expunged, renumber messages + for (std::vector ::iterator jt = + m_messages.begin() ; jt != m_messages.end() ; ++jt) + { + if ((*jt)->getNumber() == msgNumber) + (*jt)->setExpunged(); + else if ((*jt)->getNumber() > msgNumber) + (*jt)->renumber((*jt)->getNumber() - 1); + } + + events.push_back(vmime::create + (thisRef().dynamicCast (), + events::messageCountEvent::TYPE_REMOVED, + std::vector (1, msgNumber))); + + expungedMessageCount++; + } + } } - // TODO: notification + // New messages arrived + if (m_status->getMessageCount() > oldStatus->getMessageCount() - expungedMessageCount) + { + std::vector newMessageNumbers; + + for (int msgNumber = oldStatus->getMessageCount() - expungedMessageCount ; + msgNumber <= m_status->getMessageCount() ; ++msgNumber) + { + newMessageNumbers.push_back(msgNumber); + } + + events.push_back(vmime::create + (thisRef().dynamicCast (), + events::messageCountEvent::TYPE_ADDED, + newMessageNumbers)); + } + + // Dispatch notifications + for (std::vector >::iterator evit = + events.begin() ; evit != events.end() ; ++evit) + { + notifyEvent(*evit); + } } + } // imap } // net } // vmime diff --git a/src/net/imap/IMAPFolderStatus.cpp b/src/net/imap/IMAPFolderStatus.cpp index 14a77024..c231cb38 100644 --- a/src/net/imap/IMAPFolderStatus.cpp +++ b/src/net/imap/IMAPFolderStatus.cpp @@ -46,6 +46,18 @@ IMAPFolderStatus::IMAPFolderStatus() } +IMAPFolderStatus::IMAPFolderStatus(const IMAPFolderStatus& other) + : folderStatus(), + m_count(other.m_count), + m_unseen(other.m_unseen), + m_recent(other.m_recent), + m_uidValidity(other.m_uidValidity), + m_uidNext(other.m_uidNext), + m_highestModSeq(other.m_highestModSeq) +{ +} + + unsigned int IMAPFolderStatus::getMessageCount() const { return m_count; @@ -82,8 +94,16 @@ vmime_uint64 IMAPFolderStatus::getHighestModSeq() const } -void IMAPFolderStatus::updateFromResponse(const IMAPParser::mailbox_data* resp) +ref IMAPFolderStatus::clone() const { + return vmime::create (*this); +} + + +bool IMAPFolderStatus::updateFromResponse(const IMAPParser::mailbox_data* resp) +{ + bool changed = false; + if (resp->type() == IMAPParser::mailbox_data::STATUS) { const IMAPParser::status_att_list* statusAttList = resp->status_att_list(); @@ -94,81 +114,176 @@ void IMAPFolderStatus::updateFromResponse(const IMAPParser::mailbox_data* resp) switch ((*jt)->type()) { case IMAPParser::status_att_val::MESSAGES: + { + const unsigned int count = (*jt)->value_as_number()->value(); + + if (m_count != count) + { + m_count = count; + changed = true; + } - m_count = (*jt)->value_as_number()->value(); break; - + } case IMAPParser::status_att_val::UNSEEN: + { + const unsigned int unseen = (*jt)->value_as_number()->value(); + + if (m_unseen != unseen) + { + m_unseen = unseen; + changed = true; + } - m_unseen = (*jt)->value_as_number()->value(); break; - + } case IMAPParser::status_att_val::RECENT: + { + const unsigned int recent = (*jt)->value_as_number()->value(); + + if (m_recent != recent) + { + m_recent = recent; + changed = true; + } - m_recent = (*jt)->value_as_number()->value(); break; - + } case IMAPParser::status_att_val::UIDNEXT: + { + const vmime_uint32 uidNext = (*jt)->value_as_number()->value(); + + if (m_uidNext != uidNext) + { + m_uidNext = uidNext; + changed = true; + } - m_uidNext = (*jt)->value_as_number()->value(); break; - + } case IMAPParser::status_att_val::UIDVALIDITY: + { + const vmime_uint32 uidValidity = (*jt)->value_as_number()->value(); + + if (m_uidValidity != uidValidity) + { + m_uidValidity = uidValidity; + changed = true; + } - m_uidValidity = (*jt)->value_as_number()->value(); break; - + } case IMAPParser::status_att_val::HIGHESTMODSEQ: + { + const vmime_uint64 highestModSeq = (*jt)->value_as_mod_sequence_value()->value(); + + if (m_highestModSeq != highestModSeq) + { + m_highestModSeq = highestModSeq; + changed = true; + } - m_highestModSeq = (*jt)->value_as_mod_sequence_value()->value(); break; } + + } } } else if (resp->type() == IMAPParser::mailbox_data::EXISTS) { - m_count = resp->number()->value(); + const unsigned int count = resp->number()->value(); + + if (m_count != count) + { + m_count = count; + changed = true; + } } else if (resp->type() == IMAPParser::mailbox_data::RECENT) { - m_recent = resp->number()->value(); + const unsigned int recent = resp->number()->value(); + + if (m_recent != recent) + { + m_recent = recent; + changed = true; + } } + + return changed; } -void IMAPFolderStatus::updateFromResponse(const IMAPParser::resp_text_code* resp) +bool IMAPFolderStatus::updateFromResponse(const IMAPParser::resp_text_code* resp) { + bool changed = false; + switch (resp->type()) { case IMAPParser::resp_text_code::UIDVALIDITY: + { + const vmime_uint32 uidValidity = resp->nz_number()->value(); + + if (m_uidValidity != uidValidity) + { + m_uidValidity = uidValidity; + changed = true; + } - m_uidValidity = resp->nz_number()->value(); break; - + } case IMAPParser::resp_text_code::UIDNEXT: + { + const vmime_uint32 uidNext = resp->nz_number()->value(); + + if (m_uidNext != uidNext) + { + m_uidNext = uidNext; + changed = true; + } - m_uidNext = resp->nz_number()->value(); break; - + } case IMAPParser::resp_text_code::UNSEEN: + { + const unsigned int unseen = resp->nz_number()->value(); + + if (m_unseen != unseen) + { + m_unseen = unseen; + changed = true; + } - m_unseen = resp->nz_number()->value(); break; - + } case IMAPParser::resp_text_code::HIGHESTMODSEQ: + { + const vmime_uint64 highestModSeq = resp->mod_sequence_value()->value(); + + if (m_highestModSeq != highestModSeq) + { + m_highestModSeq = highestModSeq; + changed = true; + } - m_highestModSeq = resp->mod_sequence_value()->value(); break; - + } case IMAPParser::resp_text_code::NOMODSEQ: + { + if (m_highestModSeq != 0) + { + m_highestModSeq = 0; + changed = true; + } - m_highestModSeq = 0; break; - + } default: break; } + + return changed; } diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index 2b7dba14..9ae264c0 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -365,15 +365,14 @@ void IMAPMessage::extractImpl(ref p, utility::outputStream& } -void IMAPMessage::processFetchResponse +int IMAPMessage::processFetchResponse (const int options, const IMAPParser::message_data* msgData) { ref folder = m_folder.acquire(); // Get message attributes const std::vector atts = msgData->msg_att()->items(); - - int flags = 0; + int changes = 0; for (std::vector ::const_iterator it = atts.begin() ; it != atts.end() ; ++it) @@ -382,7 +381,14 @@ void IMAPMessage::processFetchResponse { case IMAPParser::msg_att_item::FLAGS: { - flags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list()); + int flags = IMAPUtils::messageFlagsFromFlags((*it)->flag_list()); + + if (m_flags != flags) + { + m_flags = flags; + changes |= events::messageChangedEvent::TYPE_FLAGS; + } + break; } case IMAPParser::msg_att_item::UID: @@ -505,8 +511,7 @@ void IMAPMessage::processFetchResponse } } - if (options & folder::FETCH_FLAGS) - m_flags = flags; + return changes; } @@ -525,111 +530,8 @@ void IMAPMessage::setFlags(const int flags, const int mode) if (!folder) throw exceptions::folder_not_found(); - else if (folder->m_mode == folder::MODE_READ_ONLY) - throw exceptions::illegal_state("Folder is read-only"); - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - command << "STORE " << m_num; - - switch (mode) - { - case FLAG_MODE_ADD: command << " +FLAGS"; break; - case FLAG_MODE_REMOVE: command << " -FLAGS"; break; - default: - case FLAG_MODE_SET: command << " FLAGS"; break; - } - - if (m_flags == FLAG_UNDEFINED) // Update local flags only if they - command << ".SILENT "; // have been fetched previously - else - command << " "; - - std::vector flagList; - - if (flags & FLAG_REPLIED) flagList.push_back("\\Answered"); - if (flags & FLAG_MARKED) flagList.push_back("\\Flagged"); - if (flags & FLAG_DELETED) flagList.push_back("\\Deleted"); - if (flags & FLAG_SEEN) flagList.push_back("\\Seen"); - if (flags & FLAG_DRAFT) flagList.push_back("\\Draft"); - - if (!flagList.empty()) - { - command << "("; - - if (flagList.size() >= 2) - { - std::copy(flagList.begin(), flagList.end() - 1, - std::ostream_iterator (command, " ")); - } - - command << *(flagList.end() - 1) << ")"; - - // Send the request - folder->m_connection->send(true, command.str(), true); - - // Get the response - utility::auto_ptr resp(folder->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"); - } - - // Update the local flags for this message - if (m_flags != FLAG_UNDEFINED) - { - const std::vector & respDataList = - resp->continue_req_or_response_data(); - - int newFlags = 0; - - for (std::vector ::const_iterator - it = respDataList.begin() ; it != respDataList.end() ; ++it) - { - if ((*it)->response_data() == NULL) - continue; - - const IMAPParser::message_data* messageData = - (*it)->response_data()->message_data(); - - // We are only interested in responses of type "FETCH" - if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH) - continue; - - // Get message attributes - const std::vector atts = - messageData->msg_att()->items(); - - for (std::vector ::const_iterator - it = atts.begin() ; it != atts.end() ; ++it) - { - if ((*it)->type() == IMAPParser::msg_att_item::FLAGS) - newFlags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list()); - } - } - - m_flags = newFlags; - } - - // Notify message flags changed - std::vector nums; - nums.push_back(m_num); - - events::messageChangedEvent event - (folder, events::messageChangedEvent::TYPE_FLAGS, nums); - - for (std::list ::iterator it = folder->m_store.acquire()->m_folders.begin() ; - it != folder->m_store.acquire()->m_folders.end() ; ++it) - { - if ((*it)->getFullPath() == folder->m_path) - (*it)->notifyMessageChanged(event); - } - } + folder->setMessageFlags(m_num, m_num, flags, mode); } @@ -711,6 +613,18 @@ ref IMAPMessage::getParsedMessage() } +void IMAPMessage::renumber(const int number) +{ + m_num = number; +} + + +void IMAPMessage::setExpunged() +{ + m_expunged = true; +} + + } // imap } // net } // vmime diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp index 3ea1093e..3042d4b5 100644 --- a/src/net/maildir/maildirFolder.cpp +++ b/src/net/maildir/maildirFolder.cpp @@ -215,9 +215,10 @@ void maildirFolder::create(const int /* type */) } // Notify folder created - events::folderEvent event - (thisRef().dynamicCast (), - events::folderEvent::TYPE_CREATED, m_path, m_path); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::folderEvent::TYPE_CREATED, m_path, m_path); notifyFolder(event); } @@ -243,9 +244,10 @@ void maildirFolder::destroy() } // Notify folder deleted - events::folderEvent event - (thisRef().dynamicCast (), - events::folderEvent::TYPE_DELETED, m_path, m_path); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::folderEvent::TYPE_DELETED, m_path, m_path); notifyFolder(event); } @@ -548,9 +550,10 @@ void maildirFolder::rename(const folder::path& newPath) m_path = newPath; m_name = newPath.getLastComponent(); - events::folderEvent event - (thisRef().dynamicCast (), - events::folderEvent::TYPE_RENAMED, oldPath, newPath); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::folderEvent::TYPE_RENAMED, oldPath, newPath); notifyFolder(event); @@ -563,9 +566,10 @@ void maildirFolder::rename(const folder::path& newPath) (*it)->m_path = newPath; (*it)->m_name = newPath.getLastComponent(); - events::folderEvent event - ((*it)->thisRef().dynamicCast (), - events::folderEvent::TYPE_RENAMED, oldPath, newPath); + ref event = + vmime::create + ((*it)->thisRef().dynamicCast (), + events::folderEvent::TYPE_RENAMED, oldPath, newPath); (*it)->notifyFolder(event); } @@ -575,9 +579,10 @@ void maildirFolder::rename(const folder::path& newPath) (*it)->m_path.renameParent(oldPath, newPath); - events::folderEvent event - ((*it)->thisRef().dynamicCast (), - events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path); + ref event = + vmime::create + ((*it)->thisRef().dynamicCast (), + events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path); (*it)->notifyFolder(event); } @@ -684,9 +689,10 @@ void maildirFolder::setMessageFlags } // Notify message flags changed - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); @@ -767,9 +773,10 @@ void maildirFolder::setMessageFlags } // Notify message flags changed - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); @@ -906,9 +913,10 @@ void maildirFolder::addMessage(utility::inputStream& is, const int size, std::vector nums; nums.push_back(m_messageCount); - events::messageCountEvent event - (thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageCountEvent::TYPE_ADDED, nums); notifyMessageCount(event); @@ -924,9 +932,10 @@ void maildirFolder::addMessage(utility::inputStream& is, const int size, (*it)->m_messageInfos.resize(m_messageInfos.size()); std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin()); - events::messageCountEvent event - ((*it)->thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); + ref event = + vmime::create + ((*it)->thisRef().dynamicCast (), + events::messageCountEvent::TYPE_ADDED, nums); (*it)->notifyMessageCount(event); } @@ -1196,9 +1205,10 @@ ref maildirFolder::getStatus() for (int i = oldCount + 1, j = 0 ; i <= m_messageCount ; ++i, ++j) nums[j] = i; - events::messageCountEvent event - (thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageCountEvent::TYPE_ADDED, nums); notifyMessageCount(event); @@ -1214,9 +1224,10 @@ ref maildirFolder::getStatus() (*it)->m_messageInfos.resize(m_messageInfos.size()); std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin()); - events::messageCountEvent event - ((*it)->thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); + ref event = + vmime::create + ((*it)->thisRef().dynamicCast (), + events::messageCountEvent::TYPE_ADDED, nums); (*it)->notifyMessageCount(event); } @@ -1289,9 +1300,10 @@ void maildirFolder::expunge() m_unreadMessageCount -= unreadCount; // Notify message expunged - events::messageCountEvent event - (thisRef().dynamicCast (), - events::messageCountEvent::TYPE_REMOVED, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageCountEvent::TYPE_REMOVED, nums); notifyMessageCount(event); @@ -1307,9 +1319,10 @@ void maildirFolder::expunge() (*it)->m_messageInfos.resize(m_messageInfos.size()); std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin()); - events::messageCountEvent event - ((*it)->thisRef().dynamicCast (), - events::messageCountEvent::TYPE_REMOVED, nums); + ref event = + vmime::create + ((*it)->thisRef().dynamicCast (), + events::messageCountEvent::TYPE_REMOVED, nums); (*it)->notifyMessageCount(event); } diff --git a/src/net/maildir/maildirFolderStatus.cpp b/src/net/maildir/maildirFolderStatus.cpp index 49425e59..bc00ba28 100644 --- a/src/net/maildir/maildirFolderStatus.cpp +++ b/src/net/maildir/maildirFolderStatus.cpp @@ -35,6 +35,21 @@ namespace net { namespace maildir { +maildirFolderStatus::maildirFolderStatus() + : m_count(0), + m_unseen(0) +{ +} + + +maildirFolderStatus::maildirFolderStatus(const maildirFolderStatus& other) + : folderStatus(), + m_count(other.m_count), + m_unseen(other.m_unseen) +{ +} + + unsigned int maildirFolderStatus::getMessageCount() const { return m_count; @@ -59,6 +74,12 @@ void maildirFolderStatus::setUnseenCount(const unsigned int unseen) } +ref maildirFolderStatus::clone() const +{ + return vmime::create (*this); +} + + } // maildir } // net } // vmime diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp index 1b7b3df6..9dc4589b 100644 --- a/src/net/pop3/POP3Folder.cpp +++ b/src/net/pop3/POP3Folder.cpp @@ -591,9 +591,10 @@ void POP3Folder::deleteMessage(const int num) std::vector nums; nums.push_back(num); - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); } @@ -640,9 +641,10 @@ void POP3Folder::deleteMessages(const int from, const int to) for (int i = from ; i <= to2 ; ++i) nums.push_back(i); - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); } @@ -691,9 +693,10 @@ void POP3Folder::deleteMessages(const std::vector & nums) } // Notify message flags changed - events::messageChangedEvent event - (thisRef().dynamicCast (), - events::messageChangedEvent::TYPE_FLAGS, list); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageChangedEvent::TYPE_FLAGS, list); notifyMessageChanged(event); } @@ -805,9 +808,10 @@ ref POP3Folder::getStatus() nums[j] = i; // Notify message count changed - events::messageCountEvent event - (thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); + ref event = + vmime::create + (thisRef().dynamicCast (), + events::messageCountEvent::TYPE_ADDED, nums); notifyMessageCount(event); @@ -819,9 +823,10 @@ ref POP3Folder::getStatus() { (*it)->m_messageCount = count; - events::messageCountEvent event - ((*it)->thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); + ref event = + vmime::create + ((*it)->thisRef().dynamicCast (), + events::messageCountEvent::TYPE_ADDED, nums); (*it)->notifyMessageCount(event); } diff --git a/src/net/pop3/POP3FolderStatus.cpp b/src/net/pop3/POP3FolderStatus.cpp index e8c5face..64c8d9d1 100644 --- a/src/net/pop3/POP3FolderStatus.cpp +++ b/src/net/pop3/POP3FolderStatus.cpp @@ -35,6 +35,21 @@ namespace net { namespace pop3 { +POP3FolderStatus::POP3FolderStatus() + : m_count(0), + m_unseen(0) +{ +} + + +POP3FolderStatus::POP3FolderStatus(const POP3FolderStatus& other) + : folderStatus(), + m_count(other.m_count), + m_unseen(other.m_unseen) +{ +} + + unsigned int POP3FolderStatus::getMessageCount() const { return m_count; @@ -59,6 +74,12 @@ void POP3FolderStatus::setUnseenCount(const unsigned int unseen) } +ref POP3FolderStatus::clone() const +{ + return vmime::create (*this); +} + + } // pop3 } // net } // vmime diff --git a/vmime/net/events.hpp b/vmime/net/events.hpp index d42f3c1c..1e5fd7f3 100644 --- a/vmime/net/events.hpp +++ b/vmime/net/events.hpp @@ -44,13 +44,30 @@ class folder; namespace events { +/** Event occurring on folders or messages. + */ + +class VMIME_EXPORT event : public object +{ +public: + + event(); + virtual ~event(); + + virtual const char* getClass() const = 0; +}; + + /** Event about the message count in a folder. */ -class VMIME_EXPORT messageCountEvent +class VMIME_EXPORT messageCountEvent : public event { public: + static const char* EVENT_CLASS; + + enum Types { TYPE_ADDED, /**< New messages have been added. */ @@ -82,7 +99,10 @@ public: * * @param listener listener to notify */ - void dispatch(class messageCountListener* listener) const; + void dispatch(class messageCountListener* listener); + + + const char* getClass() const; private: @@ -103,18 +123,21 @@ protected: public: - virtual void messagesAdded(const messageCountEvent& event) = 0; - virtual void messagesRemoved(const messageCountEvent& event) = 0; + virtual void messagesAdded(ref event) = 0; + virtual void messagesRemoved(ref event) = 0; }; /** Event occuring on a message. */ -class VMIME_EXPORT messageChangedEvent +class VMIME_EXPORT messageChangedEvent : public event { public: + static const char* EVENT_CLASS; + + enum Types { TYPE_FLAGS // flags changed @@ -145,7 +168,10 @@ public: * * @param listener listener to notify */ - void dispatch(class messageChangedListener* listener) const; + void dispatch(class messageChangedListener* listener); + + + const char* getClass() const; private: @@ -166,17 +192,20 @@ protected: public: - virtual void messageChanged(const messageChangedEvent& event) = 0; + virtual void messageChanged(ref event) = 0; }; /** Event occuring on a folder. */ -class VMIME_EXPORT folderEvent +class VMIME_EXPORT folderEvent : public event { public: + static const char* EVENT_CLASS; + + enum Types { TYPE_CREATED, /**< A folder was created. */ @@ -203,7 +232,10 @@ public: * * @param listener listener to notify */ - void dispatch(class folderListener* listener) const; + void dispatch(class folderListener* listener); + + + const char* getClass() const; private: @@ -225,9 +257,9 @@ protected: public: - virtual void folderCreated(const folderEvent& event) = 0; - virtual void folderRenamed(const folderEvent& event) = 0; - virtual void folderDeleted(const folderEvent& event) = 0; + virtual void folderCreated(ref event) = 0; + virtual void folderRenamed(ref event) = 0; + virtual void folderDeleted(ref event) = 0; }; diff --git a/vmime/net/folder.hpp b/vmime/net/folder.hpp index 6c0a3a3a..4cb6bb1a 100644 --- a/vmime/net/folder.hpp +++ b/vmime/net/folder.hpp @@ -437,9 +437,10 @@ public: protected: - void notifyMessageChanged(const events::messageChangedEvent& event); - void notifyMessageCount(const events::messageCountEvent& event); - void notifyFolder(const events::folderEvent& event); + void notifyMessageChanged(ref event); + void notifyMessageCount(ref event); + void notifyFolder(ref event); + void notifyEvent(ref event); private: diff --git a/vmime/net/folderStatus.hpp b/vmime/net/folderStatus.hpp index 90beea66..f4cc62fc 100644 --- a/vmime/net/folderStatus.hpp +++ b/vmime/net/folderStatus.hpp @@ -56,6 +56,12 @@ public: * @return number of unseen messages */ virtual unsigned int getUnseenCount() const = 0; + + /** Clones this object. + * + * @return a copy of this object + */ + virtual ref clone() const = 0; }; diff --git a/vmime/net/imap/IMAPFolder.hpp b/vmime/net/imap/IMAPFolder.hpp index 88a52523..d7ad19ee 100644 --- a/vmime/net/imap/IMAPFolder.hpp +++ b/vmime/net/imap/IMAPFolder.hpp @@ -138,6 +138,15 @@ public: int getFetchCapabilities() const; + /** Returns the highest modification sequence of this folder, ie the + * modification sequence of the last message that changed in this + * folder. + * + * @return modification sequence, or zero if not supported by + * the underlying protocol + */ + vmime_uint64 getHighestModSequence() const; + private: void registerMessage(IMAPMessage* msg); diff --git a/vmime/net/imap/IMAPFolderStatus.hpp b/vmime/net/imap/IMAPFolderStatus.hpp index 77373f1a..bcd6415e 100644 --- a/vmime/net/imap/IMAPFolderStatus.hpp +++ b/vmime/net/imap/IMAPFolderStatus.hpp @@ -49,11 +49,14 @@ class VMIME_EXPORT IMAPFolderStatus : public folderStatus public: IMAPFolderStatus(); + IMAPFolderStatus(const IMAPFolderStatus& other); // Inherited from folderStatus unsigned int getMessageCount() const; unsigned int getUnseenCount() const; + ref clone() const; + /** Returns the the number of messages with the Recent flag set. * * @return number of messages flagged Recent @@ -89,14 +92,16 @@ public: /** Reads the folder status from the specified IMAP response. * * @param resp parsed IMAP response + * @return true if the status changed, or false otherwise */ - void updateFromResponse(const IMAPParser::mailbox_data* resp); + bool updateFromResponse(const IMAPParser::mailbox_data* resp); /** Reads the folder status from the specified IMAP response. * * @param resp parsed IMAP response + * @return true if the status changed, or false otherwise */ - void updateFromResponse(const IMAPParser::resp_text_code* resp); + bool updateFromResponse(const IMAPParser::resp_text_code* resp); private: diff --git a/vmime/net/imap/IMAPMessage.hpp b/vmime/net/imap/IMAPMessage.hpp index fdda139a..59f9fc83 100644 --- a/vmime/net/imap/IMAPMessage.hpp +++ b/vmime/net/imap/IMAPMessage.hpp @@ -101,7 +101,25 @@ public: private: - void processFetchResponse(const int options, const IMAPParser::message_data* msgData); + /** Renumbers the message. + * + * @param number new sequence number + */ + void renumber(const int number); + + /** Marks the message as expunged. + */ + void setExpunged(); + + /** Processes the parsed response to fill in the attributes + * and metadata of this message. + * + * @param options one or more fetch options (see folder::FetchOptions) + * @param msgData pointer to message_data component of the parsed response + * @return a combination of flags that specify what changed exactly on + * this message (see events::messageChangedEvent::Types) + */ + int processFetchResponse(const int options, const IMAPParser::message_data* msgData); /** Recursively fetch part header for all parts in the structure. * diff --git a/vmime/net/maildir/maildirFolderStatus.hpp b/vmime/net/maildir/maildirFolderStatus.hpp index 80018b14..28e01f11 100644 --- a/vmime/net/maildir/maildirFolderStatus.hpp +++ b/vmime/net/maildir/maildirFolderStatus.hpp @@ -46,10 +46,16 @@ class VMIME_EXPORT maildirFolderStatus : public folderStatus { public: + maildirFolderStatus(); + maildirFolderStatus(const maildirFolderStatus& other); + // Inherited from folderStatus unsigned int getMessageCount() const; unsigned int getUnseenCount() const; + ref clone() const; + + void setMessageCount(const unsigned int count); void setUnseenCount(const unsigned int unseen); diff --git a/vmime/net/message.hpp b/vmime/net/message.hpp index a0f79aae..76bfb16b 100644 --- a/vmime/net/message.hpp +++ b/vmime/net/message.hpp @@ -217,8 +217,8 @@ public: */ virtual int getSize() const = 0; - /** Check whether this message has been expunged - * (ie: definitively deleted). + /** Check whether this message has been expunged (ie: definitively + * deleted) and does not exist in the folder anymore. * * @return true if the message is expunged, false otherwise */ diff --git a/vmime/net/pop3/POP3FolderStatus.hpp b/vmime/net/pop3/POP3FolderStatus.hpp index 3e5d15a1..d074ff47 100644 --- a/vmime/net/pop3/POP3FolderStatus.hpp +++ b/vmime/net/pop3/POP3FolderStatus.hpp @@ -46,10 +46,16 @@ class VMIME_EXPORT POP3FolderStatus : public folderStatus { public: + POP3FolderStatus(); + POP3FolderStatus(const POP3FolderStatus& other); + // Inherited from folderStatus unsigned int getMessageCount() const; unsigned int getUnseenCount() const; + ref clone() const; + + void setMessageCount(const unsigned int count); void setUnseenCount(const unsigned int unseen);