aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/net/imap/IMAPFolder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/net/imap/IMAPFolder.cpp')
-rw-r--r--vmime-master/src/vmime/net/imap/IMAPFolder.cpp1622
1 files changed, 1622 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/imap/IMAPFolder.cpp b/vmime-master/src/vmime/net/imap/IMAPFolder.cpp
new file mode 100644
index 0000000..98bc05d
--- /dev/null
+++ b/vmime-master/src/vmime/net/imap/IMAPFolder.cpp
@@ -0,0 +1,1622 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002 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/config.hpp"
+
+
+#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP
+
+
+#include "vmime/net/imap/IMAPFolder.hpp"
+
+#include "vmime/net/imap/IMAPStore.hpp"
+#include "vmime/net/imap/IMAPParser.hpp"
+#include "vmime/net/imap/IMAPMessage.hpp"
+#include "vmime/net/imap/IMAPUtils.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+#include "vmime/net/imap/IMAPFolderStatus.hpp"
+#include "vmime/net/imap/IMAPCommand.hpp"
+
+#include "vmime/message.hpp"
+
+#include "vmime/exception.hpp"
+
+#include "vmime/utility/outputStreamAdapter.hpp"
+
+#include <algorithm>
+#include <sstream>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPFolder::IMAPFolder(
+ const folder::path& path,
+ const shared_ptr <IMAPStore>& store,
+ const shared_ptr <folderAttributes>& attribs
+)
+ : m_store(store),
+ m_connection(store->connection()),
+ m_path(path),
+ m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()),
+ m_mode(-1),
+ m_open(false),
+ m_attribs(attribs) {
+
+ store->registerFolder(this);
+
+ m_status = make_shared <IMAPFolderStatus>();
+}
+
+
+IMAPFolder::~IMAPFolder() {
+
+ try {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (store) {
+
+ if (m_open) {
+ close(false);
+ }
+
+ store->unregisterFolder(this);
+
+ } else if (m_open) {
+
+ m_connection = null;
+ onClose();
+ }
+
+ } catch (...) {
+
+ // Don't throw in destructor
+ }
+}
+
+
+int IMAPFolder::getMode() const {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ return m_mode;
+}
+
+
+const folderAttributes IMAPFolder::getAttributes() {
+
+ // Root folder
+ if (m_path.isEmpty()) {
+
+ folderAttributes attribs;
+ attribs.setType(folderAttributes::TYPE_CONTAINS_FOLDERS);
+ attribs.setFlags(folderAttributes::FLAG_HAS_CHILDREN | folderAttributes::FLAG_NO_OPEN);
+
+ return attribs;
+
+ } else {
+
+ if (!m_attribs) {
+ testExistAndGetType();
+ }
+
+ return *m_attribs;
+ }
+}
+
+
+const folder::path::component IMAPFolder::getName() const {
+
+ return m_name;
+}
+
+
+const folder::path IMAPFolder::getFullPath() const {
+
+ return m_path;
+}
+
+
+void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ // Ensure this folder is not already open in the same session
+ for (std::list <IMAPFolder*>::iterator it = store->m_folders.begin() ;
+ it != store->m_folders.end() ; ++it) {
+
+ if ((*it) != this && (*it)->getFullPath() == m_path) {
+ throw exceptions::folder_already_open();
+ }
+ }
+
+ // Open a connection for this folder
+ shared_ptr <IMAPConnection> connection =
+ make_shared <IMAPConnection>(store, store->getAuthenticator());
+
+ try {
+
+ connection->connect();
+
+ // Emit the "SELECT" command
+ //
+ // Example: C: A142 SELECT INBOX
+ // S: * 172 EXISTS
+ // S: * 1 RECENT
+ // S: * OK [UNSEEN 12] Message 12 is first unseen
+ // S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ // S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ // S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ // S: A142 OK [READ-WRITE] SELECT completed
+
+ std::vector <string> selectParams;
+
+ if (m_connection->hasCapability("CONDSTORE")) {
+ selectParams.push_back("CONDSTORE");
+ }
+
+ IMAPCommand::SELECT(
+ mode == MODE_READ_ONLY,
+ IMAPUtils::pathToString(connection->hierarchySeparator(), getFullPath()),
+ selectParams
+ )->send(connection);
+
+ // Read the response
+ scoped_ptr <IMAPParser::response> resp(connection->readResponse());
+
+ if (resp->isBad() || resp->response_done->response_tagged->
+ resp_cond_state->status != IMAPParser::resp_cond_state::OK) {
+
+ throw exceptions::command_error("SELECT", resp->getErrorLog(), "bad response");
+ }
+
+ auto &respDataList = resp->continue_req_or_response_data;
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ auto *responseData = (*it)->response_data.get();
+
+ if (!responseData) {
+ throw exceptions::command_error("SELECT", resp->getErrorLog(), "invalid response");
+ }
+
+ // OK Untagged responses: UNSEEN, PERMANENTFLAGS, UIDVALIDITY (optional)
+ if (responseData->resp_cond_state) {
+
+ auto *code = responseData->resp_cond_state->resp_text->resp_text_code.get();
+
+ if (code) {
+
+ switch (code->type) {
+
+ case IMAPParser::resp_text_code::NOMODSEQ:
+
+ connection->disableMODSEQ();
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+ // Untagged responses: FLAGS, EXISTS, RECENT (required)
+ } else if (responseData->mailbox_data) {
+
+ switch (responseData->mailbox_data->type) {
+
+ default: break;
+
+ case IMAPParser::mailbox_data::FLAGS: {
+
+ if (!m_attribs) {
+ m_attribs = make_shared <folderAttributes>();
+ }
+
+ IMAPUtils::mailboxFlagsToFolderAttributes(
+ connection,
+ m_path,
+ *responseData->mailbox_data->mailbox_flag_list,
+ *m_attribs
+ );
+
+ break;
+ }
+ }
+ }
+ }
+
+ processStatusUpdate(resp.get());
+
+ // Check for access mode (read-only or read-write)
+ auto *respTextCode = resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code.get();
+
+ if (respTextCode) {
+
+ const int openMode =
+ (respTextCode->type == IMAPParser::resp_text_code::READ_WRITE)
+ ? MODE_READ_WRITE
+ : MODE_READ_ONLY;
+
+ if (failIfModeIsNotAvailable &&
+ mode == MODE_READ_WRITE && openMode == MODE_READ_ONLY) {
+
+ throw exceptions::operation_not_supported();
+ }
+ }
+
+ m_connection = connection;
+ m_open = true;
+ m_mode = mode;
+
+ } catch (std::exception&) {
+
+ throw;
+ }
+}
+
+
+void IMAPFolder::close(const bool expunge) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ shared_ptr <IMAPConnection> oldConnection = m_connection;
+
+ // Emit the "CLOSE" command to expunge messages marked
+ // as deleted (this is fastest than "EXPUNGE")
+ if (expunge) {
+
+ if (m_mode == MODE_READ_ONLY) {
+ throw exceptions::operation_not_supported();
+ }
+
+ IMAPCommand::CLOSE()->send(oldConnection);
+ }
+
+ // Close this folder connection
+ oldConnection->disconnect();
+
+ // Now use default store connection
+ m_connection = m_store.lock()->connection();
+
+ m_open = false;
+ m_mode = -1;
+
+ m_status = make_shared <IMAPFolderStatus>();
+
+ onClose();
+}
+
+
+void IMAPFolder::onClose() {
+
+ for (std::vector <IMAPMessage*>::iterator it = m_messages.begin() ;
+ it != m_messages.end() ; ++it) {
+
+ (*it)->onFolderClosed();
+ }
+
+ m_messages.clear();
+}
+
+
+void IMAPFolder::create(const folderAttributes& attribs) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (isOpen()) {
+ throw exceptions::illegal_state("Folder is open");
+ } else if (exists()) {
+ throw exceptions::illegal_state("Folder already exists");
+ } else if (!store->isValidFolderName(m_name)) {
+ throw exceptions::invalid_folder_name();
+ }
+
+ // Emit the "CREATE" command
+ //
+ // Example: C: A003 CREATE owatagusiam/
+ // S: A003 OK CREATE completed
+ // C: A004 CREATE owatagusiam/blurdybloop
+ // S: A004 OK CREATE completed
+
+ string mailbox = IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath());
+
+ if (attribs.getType() & folderAttributes::TYPE_CONTAINS_FOLDERS) {
+ mailbox += m_connection->hierarchySeparator();
+ }
+
+ std::vector <string> createParams;
+
+ if (attribs.getSpecialUse() != folderAttributes::SPECIALUSE_NONE) {
+
+ if (!m_connection->hasCapability("CREATE-SPECIAL-USE")) {
+ throw exceptions::operation_not_supported();
+ }
+
+ // C: t2 CREATE MySpecial (USE (\Drafts \Sent))
+ std::ostringstream oss;
+ oss << "USE (";
+
+ switch (attribs.getSpecialUse()) {
+
+ case folderAttributes::SPECIALUSE_NONE: // should not happen
+ case folderAttributes::SPECIALUSE_ALL: oss << "\\All"; break;
+ case folderAttributes::SPECIALUSE_ARCHIVE: oss << "\\Archive"; break;
+ case folderAttributes::SPECIALUSE_DRAFTS: oss << "\\Drafts"; break;
+ case folderAttributes::SPECIALUSE_FLAGGED: oss << "\\Flagged"; break;
+ case folderAttributes::SPECIALUSE_JUNK: oss << "\\Junk"; break;
+ case folderAttributes::SPECIALUSE_SENT: oss << "\\Sent"; break;
+ case folderAttributes::SPECIALUSE_TRASH: oss << "\\Trash"; break;
+ case folderAttributes::SPECIALUSE_IMPORTANT: oss << "\\Important"; break;
+ }
+
+ oss << ")";
+
+ createParams.push_back(oss.str());
+ }
+
+ IMAPCommand::CREATE(mailbox, createParams)->send(m_connection);
+
+
+ scoped_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("CREATE", resp->getErrorLog(), "bad response");
+ }
+
+ // Notify folder created
+ shared_ptr <events::folderEvent> event =
+ make_shared <events::folderEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::folderEvent::TYPE_CREATED,
+ m_path, m_path
+ );
+
+ notifyFolder(event);
+}
+
+
+void IMAPFolder::destroy() {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ if (isOpen()) {
+ throw exceptions::illegal_state("Folder is open");
+ }
+
+ const string mailbox = IMAPUtils::pathToString(
+ m_connection->hierarchySeparator(), getFullPath()
+ );
+
+ IMAPCommand::DELETE(mailbox)->send(m_connection);
+
+
+ scoped_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("DELETE", resp->getErrorLog(), "bad response");
+ }
+
+ // Notify folder deleted
+ shared_ptr <events::folderEvent> event =
+ make_shared <events::folderEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::folderEvent::TYPE_DELETED,
+ m_path, m_path
+ );
+
+ notifyFolder(event);
+}
+
+
+bool IMAPFolder::exists() {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!isOpen() && !store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ return testExistAndGetType() != -1;
+}
+
+
+int IMAPFolder::testExistAndGetType() {
+
+ // To test whether a folder exists, we simple list it using
+ // the "LIST" command, and there should be one unique mailbox
+ // with this name...
+ //
+ // Eg. Test whether '/foo/bar' exists
+ //
+ // C: a005 list "" foo/bar
+ // S: * LIST (\NoSelect) "/" foo/bar
+ // S: a005 OK LIST completed
+ //
+ // ==> OK, exists
+ //
+ // Test whether '/foo/bar/zap' exists
+ //
+ // C: a005 list "" foo/bar/zap
+ // S: a005 OK LIST completed
+ //
+ // ==> NO, does not exist
+
+ IMAPCommand::LIST(
+ "",
+ IMAPUtils::pathToString(
+ m_connection->hierarchySeparator(),
+ getFullPath()
+ )
+ )->send(m_connection);
+
+
+ scoped_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("LIST", resp->getErrorLog(), "bad response");
+ }
+
+ // Check whether the result mailbox list contains this folder
+ auto& respDataList = resp->continue_req_or_response_data;
+
+ folderAttributes attribs;
+ attribs.setType(-1);
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ if (!(*it)->response_data) {
+ throw exceptions::command_error("LIST", resp->getErrorLog(), "invalid response");
+ }
+
+ auto *mailboxData = (*it)->response_data->mailbox_data.get();
+
+ // We are only interested in responses of type "LIST"
+ if (mailboxData &&
+ mailboxData->type == IMAPParser::mailbox_data::LIST) {
+
+ // Get the folder type/flags at the same time
+ IMAPUtils::mailboxFlagsToFolderAttributes(
+ m_connection,
+ m_path,
+ *mailboxData->mailbox_list->mailbox_flag_list,
+ attribs
+ );
+ }
+ }
+
+ m_attribs = make_shared <folderAttributes>(attribs);
+
+ return m_attribs->getType();
+}
+
+
+bool IMAPFolder::isOpen() const {
+
+ return m_open;
+}
+
+
+shared_ptr <message> IMAPFolder::getMessage(const size_t num) {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ if (num < 1 || num > m_status->getMessageCount()) {
+ throw exceptions::message_not_found();
+ }
+
+ return make_shared <IMAPMessage>(dynamicCast <IMAPFolder>(shared_from_this()), num);
+}
+
+
+std::vector <shared_ptr <message> > IMAPFolder::getMessages(const messageSet& msgs) {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ if (msgs.isEmpty()) {
+ return std::vector <shared_ptr <message> >();
+ }
+
+ std::vector <shared_ptr <message> > messages;
+
+ // Sequence number message set:
+ // C: . FETCH uuuu1,uuuu2,uuuu3 UID
+ // S: * nnnn1 FETCH (UID uuuu1)
+ // S: * nnnn2 FETCH (UID uuuu2)
+ // S: * nnnn3 FETCH (UID uuuu3)
+ // S: . OK FETCH completed
+
+ // UID message set:
+ // C: . UID FETCH uuuu1,uuuu2,uuuu3 UID
+ // S: * nnnn1 FETCH (UID uuuu1)
+ // S: * nnnn2 FETCH (UID uuuu2)
+ // S: * nnnn3 FETCH (UID uuuu3)
+ // S: . OK UID FETCH completed
+
+ std::vector <string> params;
+ params.push_back("UID");
+
+ IMAPCommand::FETCH(msgs, params)->send(m_connection);
+
+ // Get the response
+ scoped_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
+ auto &respDataList = resp->continue_req_or_response_data;
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ if (!(*it)->response_data) {
+ throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "invalid response");
+ }
+
+ auto *messageData = (*it)->response_data->message_data.get();
+
+ // We are only interested in responses of type "FETCH"
+ if (!messageData || messageData->type != IMAPParser::message_data::FETCH) {
+ continue;
+ }
+
+ // Find UID in message attributes
+ const size_t msgNum = messageData->number;
+ message::uid msgUID;
+
+ for (auto &att : messageData->msg_att->items) {
+
+ if (att->type == IMAPParser::msg_att_item::UID) {
+ msgUID = att->uniqueid->value;
+ break;
+ }
+ }
+
+ if (!msgUID.empty()) {
+ shared_ptr <IMAPFolder> thisFolder = dynamicCast <IMAPFolder>(shared_from_this());
+ messages.push_back(make_shared <IMAPMessage>(thisFolder, msgNum, msgUID));
+ }
+ }
+
+ return messages;
+}
+
+
+size_t IMAPFolder::getMessageCount() {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ return m_status->getMessageCount();
+}
+
+
+vmime_uint32 IMAPFolder::getUIDValidity() const {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ return m_status->getUIDValidity();
+}
+
+
+vmime_uint64 IMAPFolder::getHighestModSequence() const {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ return m_status->getHighestModSeq();
+}
+
+
+shared_ptr <folder> IMAPFolder::getFolder(const folder::path::component& name) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ return make_shared <IMAPFolder>(m_path / name, store, shared_ptr <folderAttributes>());
+}
+
+
+std::vector <shared_ptr <folder> > IMAPFolder::getFolders(const bool recursive) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!isOpen() && !store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ // Eg. List folders in '/foo/bar'
+ //
+ // C: a005 list "foo/bar" *
+ // S: * LIST (\NoSelect) "/" foo/bar
+ // S: * LIST (\NoInferiors) "/" foo/bar/zap
+ // S: a005 OK LIST completed
+
+ shared_ptr <IMAPCommand> cmd;
+
+ const string pathString = IMAPUtils::pathToString(
+ m_connection->hierarchySeparator(), getFullPath()
+ );
+
+ if (recursive) {
+
+ cmd = IMAPCommand::LIST(pathString, "*");
+
+ } else {
+
+ cmd = IMAPCommand::LIST(
+ pathString.empty()
+ ? ""
+ : (pathString + m_connection->hierarchySeparator()),
+ "%"
+ );
+ }
+
+ cmd->send(m_connection);
+
+
+ scoped_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("LIST", resp->getErrorLog(), "bad response");
+ }
+
+ auto &respDataList = resp->continue_req_or_response_data;
+
+ std::vector <shared_ptr <folder> > v;
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ if (!(*it)->response_data) {
+ throw exceptions::command_error("LIST", resp->getErrorLog(), "invalid response");
+ }
+
+ auto *mailboxData = (*it)->response_data->mailbox_data.get();
+
+ if (!mailboxData || mailboxData->type != IMAPParser::mailbox_data::LIST) {
+ continue;
+ }
+
+ // Get folder path
+ auto &mailbox = mailboxData->mailbox_list->mailbox;
+
+ folder::path path = IMAPUtils::stringToPath(
+ mailboxData->mailbox_list->quoted_char, mailbox->name
+ );
+
+ if (recursive || m_path.isDirectParentOf(path)) {
+
+ // Append folder to list
+ shared_ptr <folderAttributes> attribs = make_shared <folderAttributes>();
+
+ IMAPUtils::mailboxFlagsToFolderAttributes(
+ m_connection,
+ path,
+ *mailboxData->mailbox_list->mailbox_flag_list,
+ *attribs
+ );
+
+ v.push_back(make_shared <IMAPFolder>(path, store, attribs));
+ }
+ }
+
+ return v;
+}
+
+
+void IMAPFolder::fetchMessages(
+ std::vector <shared_ptr <message> >& msg,
+ const fetchAttributes& options,
+ utility::progressListener* progress
+) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ if (msg.empty()) {
+ return;
+ }
+
+ // Build message numbers list
+ std::vector <size_t> list;
+ list.reserve(msg.size());
+
+ std::map <size_t, shared_ptr <IMAPMessage> > numberToMsg;
+
+ for (std::vector <shared_ptr <message> >::iterator it = msg.begin() ; it != msg.end() ; ++it) {
+
+ list.push_back((*it)->getNumber());
+ numberToMsg[(*it)->getNumber()] = dynamicCast <IMAPMessage>(*it);
+ }
+
+ // Send the request
+ IMAPUtils::buildFetchCommand(
+ m_connection, messageSet::byNumber(list), options
+ )->send(m_connection);
+
+ // Get the response
+ scoped_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done->response_tagged->
+ resp_cond_state->status != IMAPParser::resp_cond_state::OK) {
+
+ throw exceptions::command_error("FETCH", resp->getErrorLog(), "bad response");
+ }
+
+ auto &respDataList = resp->continue_req_or_response_data;
+
+ const size_t total = msg.size();
+ size_t current = 0;
+
+ if (progress) {
+ progress->start(total);
+ }
+
+ try {
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ if (!(*it)->response_data) {
+ throw exceptions::command_error("FETCH", resp->getErrorLog(), "invalid response");
+ }
+
+ auto *messageData = (*it)->response_data->message_data.get();
+
+ // We are only interested in responses of type "FETCH"
+ if (!messageData || messageData->type != IMAPParser::message_data::FETCH) {
+ continue;
+ }
+
+ // Process fetch response for this message
+ const size_t num = messageData->number;
+
+ std::map <size_t, shared_ptr <IMAPMessage> >::iterator msg = numberToMsg.find(num);
+
+ if (msg != numberToMsg.end()) {
+
+ (*msg).second->processFetchResponse(options, *messageData);
+
+ if (progress) {
+ progress->progress(++current, total);
+ }
+ }
+ }
+
+ } catch (...) {
+
+ if (progress) {
+ progress->stop(total);
+ }
+
+ throw;
+ }
+
+ if (progress) {
+ progress->stop(total);
+ }
+
+ processStatusUpdate(resp.get());
+}
+
+
+void IMAPFolder::fetchMessage(const shared_ptr <message>& msg, const fetchAttributes& options) {
+
+ std::vector <shared_ptr <message> > msgs;
+ msgs.push_back(msg);
+
+ fetchMessages(msgs, options, /* progress */ NULL);
+}
+
+
+std::vector <shared_ptr <message> > IMAPFolder::getAndFetchMessages(
+ const messageSet& msgs,
+ const fetchAttributes& attribs
+) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ if (msgs.isEmpty()) {
+ return std::vector <shared_ptr <message> >();
+ }
+
+ // Ensure we also get the UID for each message
+ fetchAttributes attribsWithUID(attribs);
+ attribsWithUID.add(fetchAttributes::UID);
+
+ // Send the request
+ IMAPUtils::buildFetchCommand(m_connection, msgs, attribsWithUID)->send(m_connection);
+
+ // Get the response
+ scoped_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done->response_tagged->
+ resp_cond_state->status != IMAPParser::resp_cond_state::OK) {
+
+ throw exceptions::command_error("FETCH", resp->getErrorLog(), "bad response");
+ }
+
+ auto &respDataList = resp->continue_req_or_response_data;
+
+ std::vector <shared_ptr <message> > messages;
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ if (!(*it)->response_data) {
+ throw exceptions::command_error("FETCH", resp->getErrorLog(), "invalid response");
+ }
+
+ auto *messageData = (*it)->response_data->message_data.get();
+
+ // We are only interested in responses of type "FETCH"
+ if (!messageData || messageData->type != IMAPParser::message_data::FETCH) {
+ continue;
+ }
+
+ // Get message number
+ const size_t msgNum = messageData->number;
+
+ // Get message UID
+ message::uid msgUID;
+
+ for (auto &att : messageData->msg_att->items) {
+
+ if (att->type == IMAPParser::msg_att_item::UID) {
+ msgUID = att->uniqueid->value;
+ break;
+ }
+ }
+
+ // Create a new message reference
+ shared_ptr <IMAPFolder> thisFolder = dynamicCast <IMAPFolder>(shared_from_this());
+ shared_ptr <IMAPMessage> msg = make_shared <IMAPMessage>(thisFolder, msgNum, msgUID);
+
+ messages.push_back(msg);
+
+ // Process fetch response for this message
+ msg->processFetchResponse(attribsWithUID, *messageData);
+ }
+
+ processStatusUpdate(resp.get());
+
+ return messages;
+}
+
+
+int IMAPFolder::getFetchCapabilities() const {
+
+ return fetchAttributes::ENVELOPE | fetchAttributes::CONTENT_INFO |
+ fetchAttributes::STRUCTURE | fetchAttributes::FLAGS |
+ fetchAttributes::SIZE | fetchAttributes::FULL_HEADER |
+ fetchAttributes::UID | fetchAttributes::IMPORTANCE;
+}
+
+
+shared_ptr <folder> IMAPFolder::getParent() {
+
+ if (m_path.isEmpty()) {
+
+ return null;
+
+ } else {
+
+ return make_shared <IMAPFolder>(
+ m_path.getParent(), m_store.lock(), shared_ptr <folderAttributes>()
+ );
+ }
+}
+
+
+shared_ptr <const store> IMAPFolder::getStore() const {
+
+ return m_store.lock();
+}
+
+
+shared_ptr <store> IMAPFolder::getStore() {
+
+ return m_store.lock();
+}
+
+
+void IMAPFolder::registerMessage(IMAPMessage* msg) {
+
+ m_messages.push_back(msg);
+}
+
+
+void IMAPFolder::unregisterMessage(IMAPMessage* msg) {
+
+ std::vector <IMAPMessage*>::iterator it =
+ std::find(m_messages.begin(), m_messages.end(), msg);
+
+ if (it != m_messages.end()) {
+ m_messages.erase(it);
+ }
+}
+
+
+void IMAPFolder::onStoreDisconnected() {
+
+ m_store.reset();
+}
+
+
+void IMAPFolder::deleteMessages(const messageSet& msgs) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (msgs.isEmpty()) {
+ 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");
+ }
+
+ // Send the request
+ IMAPCommand::STORE(
+ msgs, message::FLAG_MODE_ADD,
+ IMAPUtils::messageFlagList(message::FLAG_DELETED)
+ )->send(m_connection);
+
+ // Get the response
+ scoped_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.get());
+}
+
+
+void IMAPFolder::setMessageFlags(
+ const messageSet& msgs,
+ const int flags,
+ const int mode
+) {
+
+ const std::vector <string> flagList = IMAPUtils::messageFlagList(flags);
+
+ if ((mode == message::FLAG_MODE_SET) || !flagList.empty()) {
+
+ // Send the request
+ IMAPCommand::STORE(msgs, mode, flagList)->send(m_connection);
+
+ // Get the response
+ scoped_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.get());
+ }
+}
+
+
+messageSet IMAPFolder::addMessage(
+ const shared_ptr <vmime::message>& msg,
+ const int flags,
+ vmime::datetime* date,
+ utility::progressListener* progress
+) {
+
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ msg->generate(ossAdapter);
+
+ const string& str = oss.str();
+ utility::inputStreamStringAdapter strAdapter(str);
+
+ return addMessage(strAdapter, str.length(), flags, date, progress);
+}
+
+
+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();
+
+ 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");
+ }
+
+ // Send the request
+ IMAPCommand::APPEND(
+ IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()),
+ IMAPUtils::messageFlagList(flags), date, size
+ )->send(m_connection);
+
+ // Get the response
+ scoped_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ bool ok = false;
+ auto &respList = resp->continue_req_or_response_data;
+
+ for (auto it = respList.begin() ; !ok && (it != respList.end()) ; ++it) {
+
+ if ((*it)->continue_req) {
+ ok = true;
+ }
+ }
+
+ if (!ok) {
+ throw exceptions::command_error("APPEND", resp->getErrorLog(), "bad response");
+ }
+
+ processStatusUpdate(resp.get());
+
+ // Send message data
+ const size_t total = size;
+ size_t current = 0;
+
+ if (progress) {
+ progress->start(total);
+ }
+
+ const size_t blockSize = std::min(
+ is.getBlockSize(),
+ static_cast <size_t>(m_connection->getSocket()->getBlockSize())
+ );
+
+ std::vector <byte_t> vbuffer(blockSize);
+ byte_t* buffer = &vbuffer.front();
+
+ while (!is.eof()) {
+
+ // Read some data from the input stream
+ const size_t read = is.read(buffer, blockSize);
+ current += read;
+
+ // Put read data into socket output stream
+ m_connection->sendRaw(buffer, read);
+
+ // Notify progress
+ if (progress) {
+ progress->progress(current, total);
+ }
+ }
+
+ m_connection->sendRaw(utility::stringUtils::bytesFromString("\r\n"), 2);
+
+ if (m_connection->getTracer()) {
+ m_connection->getTracer()->traceSendBytes(current);
+ }
+
+ if (progress) {
+ progress->stop(total);
+ }
+
+ // Get the response
+ scoped_ptr <IMAPParser::response> finalResp(m_connection->readResponse());
+
+ if (finalResp->isBad() || finalResp->response_done->response_tagged->
+ resp_cond_state->status != IMAPParser::resp_cond_state::OK) {
+
+ throw exceptions::command_error("APPEND", resp->getErrorLog(), "bad response");
+ }
+
+ processStatusUpdate(finalResp.get());
+
+ auto *respTextCode =
+ finalResp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code.get();
+
+ if (respTextCode && respTextCode->type == IMAPParser::resp_text_code::APPENDUID) {
+ return IMAPUtils::buildMessageSet(*respTextCode->uid_set);
+ }
+
+ return messageSet::empty();
+}
+
+
+void IMAPFolder::expunge() {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ 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");
+ }
+
+ // Send the request
+ IMAPCommand::EXPUNGE()->send(m_connection);
+
+ // Get the response
+ scoped_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("EXPUNGE", resp->getErrorLog(), "bad response");
+ }
+
+ processStatusUpdate(resp.get());
+}
+
+
+void IMAPFolder::rename(const folder::path& newPath) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (m_path.isEmpty() || newPath.isEmpty()) {
+ throw exceptions::illegal_operation("Cannot rename root folder");
+ } else if (m_path.getSize() == 1 && m_name.getBuffer() == "INBOX") {
+ throw exceptions::illegal_operation("Cannot rename 'INBOX' folder");
+ } else if (!store->isValidFolderName(newPath.getLastComponent())) {
+ throw exceptions::invalid_folder_name();
+ }
+
+ // Send the request
+ IMAPCommand::RENAME(
+ IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()),
+ IMAPUtils::pathToString(m_connection->hierarchySeparator(), newPath)
+ )->send(m_connection);
+
+ // Get the response
+ scoped_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("RENAME", resp->getErrorLog(), "bad response");
+ }
+
+ // Notify folder renamed
+ folder::path oldPath(m_path);
+
+ m_path = newPath;
+ m_name = newPath.getLastComponent();
+
+ shared_ptr <events::folderEvent> event =
+ make_shared <events::folderEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::folderEvent::TYPE_RENAMED,
+ oldPath,
+ newPath
+ );
+
+ notifyFolder(event);
+
+ // Notify sub-folders
+ for (std::list <IMAPFolder*>::iterator it = store->m_folders.begin() ;
+ it != store->m_folders.end() ; ++it) {
+
+ if ((*it) != this && oldPath.isParentOf((*it)->getFullPath())) {
+
+ folder::path oldPath((*it)->m_path);
+
+ (*it)->m_path.renameParent(oldPath, newPath);
+
+ shared_ptr <events::folderEvent> event =
+ make_shared <events::folderEvent>(
+ dynamicCast <folder>((*it)->shared_from_this()),
+ events::folderEvent::TYPE_RENAMED,
+ oldPath, (*it)->m_path
+ );
+
+ (*it)->notifyFolder(event);
+ }
+ }
+
+ processStatusUpdate(resp.get());
+}
+
+
+messageSet IMAPFolder::copyMessages(const folder::path& dest, const messageSet& set) {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ // Send the request
+ IMAPCommand::COPY(
+ set,
+ IMAPUtils::pathToString(m_connection->hierarchySeparator(), dest)
+ )->send(m_connection);
+
+ // Get the response
+ scoped_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("COPY", resp->getErrorLog(), "bad response");
+ }
+
+ processStatusUpdate(resp.get());
+
+ auto *respTextCode =
+ resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code.get();
+
+ if (respTextCode && respTextCode->type == IMAPParser::resp_text_code::COPYUID) {
+ return IMAPUtils::buildMessageSet(*respTextCode->uid_set2);
+ }
+
+ return messageSet::empty();
+}
+
+
+void IMAPFolder::status(size_t& count, size_t& unseen) {
+
+ count = 0;
+ unseen = 0;
+
+ shared_ptr <folderStatus> status = getStatus();
+
+ count = status->getMessageCount();
+ unseen = status->getUnseenCount();
+}
+
+
+shared_ptr <folderStatus> IMAPFolder::getStatus() {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ // Build the attributes list
+ std::vector <string> attribs;
+
+ attribs.push_back("MESSAGES");
+ attribs.push_back("UNSEEN");
+ attribs.push_back("UIDNEXT");
+ attribs.push_back("UIDVALIDITY");
+
+ if (m_connection->hasCapability("CONDSTORE")) {
+ attribs.push_back("HIGHESTMODSEQ");
+ }
+
+ // Send the request
+ IMAPCommand::STATUS(
+ IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()),
+ attribs
+ )->send(m_connection);
+
+ // Get the response
+ scoped_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("STATUS", resp->getErrorLog(), "bad response");
+ }
+
+ auto &respDataList = resp->continue_req_or_response_data;
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ if ((*it)->response_data) {
+
+ auto &responseData = (*it)->response_data;
+
+ if (responseData->mailbox_data &&
+ responseData->mailbox_data->type == IMAPParser::mailbox_data::STATUS) {
+
+ shared_ptr <IMAPFolderStatus> status = make_shared <IMAPFolderStatus>();
+ status->updateFromResponse(*responseData->mailbox_data);
+
+ m_status->updateFromResponse(*responseData->mailbox_data);
+
+ return status;
+ }
+ }
+ }
+
+ throw exceptions::command_error("STATUS", resp->getErrorLog(), "invalid response");
+}
+
+
+void IMAPFolder::noop() {
+
+ shared_ptr <IMAPStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ IMAPCommand::NOOP()->send(m_connection);
+
+ scoped_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("NOOP", resp->getErrorLog());
+ }
+
+ processStatusUpdate(resp.get());
+}
+
+
+std::vector <size_t> IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& uid) {
+
+ // Send the request
+ std::ostringstream uidSearchKey;
+ uidSearchKey.imbue(std::locale::classic());
+ uidSearchKey << "UID " << uid << ":*";
+
+ std::vector <string> searchKeys;
+ searchKeys.push_back(uidSearchKey.str());
+
+ IMAPCommand::SEARCH(searchKeys, /* charset */ NULL)->send(m_connection);
+
+ // Get the response
+ scoped_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("SEARCH", resp->getErrorLog(), "bad response");
+ }
+
+ auto& respDataList = resp->continue_req_or_response_data;
+
+ std::vector <size_t> seqNumbers;
+
+ for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) {
+
+ if (!(*it)->response_data) {
+ throw exceptions::command_error("SEARCH", resp->getErrorLog(), "invalid response");
+ }
+
+ auto *mailboxData = (*it)->response_data->mailbox_data.get();
+
+ // We are only interested in responses of type "SEARCH"
+ if (!mailboxData ||
+ mailboxData->type != IMAPParser::mailbox_data::SEARCH) {
+
+ continue;
+ }
+
+ for (auto &nzn : mailboxData->search_nz_number_list) {
+ seqNumbers.push_back(nzn->value);
+ }
+ }
+
+ processStatusUpdate(resp.get());
+
+ return seqNumbers;
+}
+
+
+void IMAPFolder::processStatusUpdate(const IMAPParser::response* resp) {
+
+ std::vector <shared_ptr <events::event> > events;
+
+ shared_ptr <IMAPFolderStatus> oldStatus = vmime::clone(m_status);
+ int expungedMessageCount = 0;
+
+ // Process tagged response
+ if (resp->response_done &&
+ resp->response_done->response_tagged &&
+ resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code) {
+
+ m_status->updateFromResponse(
+ *resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code
+ );
+ }
+
+ // Process untagged responses
+ for (auto it = resp->continue_req_or_response_data.begin() ;
+ it != resp->continue_req_or_response_data.end() ; ++it) {
+
+ if ((*it)->response_data &&
+ (*it)->response_data->resp_cond_state &&
+ (*it)->response_data->resp_cond_state->resp_text->resp_text_code) {
+
+ m_status->updateFromResponse(
+ *(*it)->response_data->resp_cond_state->resp_text->resp_text_code
+ );
+
+ } else if ((*it)->response_data &&
+ (*it)->response_data->mailbox_data) {
+
+ m_status->updateFromResponse(*(*it)->response_data->mailbox_data);
+
+ // Update folder attributes, if available
+ if ((*it)->response_data->mailbox_data->type == IMAPParser::mailbox_data::LIST) {
+
+ folderAttributes attribs;
+ IMAPUtils::mailboxFlagsToFolderAttributes(
+ m_connection,
+ m_path,
+ *(*it)->response_data->mailbox_data->mailbox_list->mailbox_flag_list,
+ attribs
+ );
+
+ m_attribs = make_shared <folderAttributes>(attribs);
+ }
+
+ } else if ((*it)->response_data && (*it)->response_data->message_data) {
+
+ auto* msgData = (*it)->response_data->message_data.get();
+ const size_t msgNumber = msgData->number;
+
+ if (msgData->type == IMAPParser::message_data::FETCH) {
+
+ // Message changed
+ for (std::vector <IMAPMessage*>::iterator mit =
+ m_messages.begin() ; mit != m_messages.end() ; ++mit) {
+
+ if ((*mit)->getNumber() == msgNumber) {
+ (*mit)->processFetchResponse(/* options */ 0, *msgData);
+ }
+ }
+
+ events.push_back(
+ make_shared <events::messageChangedEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::messageChangedEvent::TYPE_FLAGS,
+ std::vector <size_t>(1, msgNumber)
+ )
+ );
+
+ } else if (msgData->type == IMAPParser::message_data::EXPUNGE) {
+
+ // A message has been expunged, renumber messages
+ for (std::vector <IMAPMessage*>::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(
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::messageCountEvent::TYPE_REMOVED,
+ std::vector <size_t>(1, msgNumber)
+ )
+ );
+
+ expungedMessageCount++;
+ }
+ }
+ }
+
+ // New messages arrived
+ if (m_status->getMessageCount() > oldStatus->getMessageCount() - expungedMessageCount) {
+
+ std::vector <size_t> newMessageNumbers;
+
+ for (size_t msgNumber = oldStatus->getMessageCount() - expungedMessageCount ;
+ msgNumber <= m_status->getMessageCount() ; ++msgNumber) {
+
+ newMessageNumbers.push_back(msgNumber);
+ }
+
+ events.push_back(
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::messageCountEvent::TYPE_ADDED,
+ newMessageNumbers
+ )
+ );
+ }
+
+ // Dispatch notifications
+ for (std::vector <shared_ptr <events::event> >::iterator evit =
+ events.begin() ; evit != events.end() ; ++evit) {
+
+ notifyEvent(*evit);
+ }
+}
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP
+