aboutsummaryrefslogtreecommitdiffstats
path: root/src/messaging/imap/IMAPFolder.cpp
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2005-04-03 08:00:52 +0000
committerVincent Richard <[email protected]>2005-04-03 08:00:52 +0000
commit0bba2794fc73785a07ca7990c615a77f8a3065eb (patch)
treeafa695e782412aa2449a0ffec9d300541d76865f /src/messaging/imap/IMAPFolder.cpp
parentAdded constant for 'text/directory' MIME type. (diff)
downloadvmime-0bba2794fc73785a07ca7990c615a77f8a3065eb.tar.gz
vmime-0bba2794fc73785a07ca7990c615a77f8a3065eb.zip
Moved IMAP, POP3, maildir and SMTP files to separate namespaces.
Diffstat (limited to 'src/messaging/imap/IMAPFolder.cpp')
-rw-r--r--src/messaging/imap/IMAPFolder.cpp1561
1 files changed, 1561 insertions, 0 deletions
diff --git a/src/messaging/imap/IMAPFolder.cpp b/src/messaging/imap/IMAPFolder.cpp
new file mode 100644
index 00000000..5bbfa148
--- /dev/null
+++ b/src/messaging/imap/IMAPFolder.cpp
@@ -0,0 +1,1561 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/messaging/imap/IMAPFolder.hpp"
+
+#include "vmime/messaging/imap/IMAPStore.hpp"
+#include "vmime/messaging/imap/IMAPParser.hpp"
+#include "vmime/messaging/imap/IMAPMessage.hpp"
+#include "vmime/messaging/imap/IMAPUtils.hpp"
+#include "vmime/messaging/imap/IMAPConnection.hpp"
+
+#include "vmime/message.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/utility/smartPtr.hpp"
+
+#include <algorithm>
+#include <sstream>
+
+
+namespace vmime {
+namespace messaging {
+namespace imap {
+
+
+IMAPFolder::IMAPFolder(const folder::path& path, IMAPStore* store, const int type, const int flags)
+ : m_store(store), m_connection(m_store->connection()), m_path(path),
+ m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()), m_mode(-1),
+ m_open(false), m_type(type), m_flags(flags), m_messageCount(0), m_uidValidity(0)
+{
+ m_store->registerFolder(this);
+}
+
+
+IMAPFolder::~IMAPFolder()
+{
+ if (m_store)
+ {
+ if (m_open)
+ close(false);
+
+ m_store->unregisterFolder(this);
+ }
+ else if (m_open)
+ {
+ delete (m_connection);
+ onClose();
+ }
+}
+
+
+const int IMAPFolder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+const int IMAPFolder::getType()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Root folder
+ if (m_path.isEmpty())
+ {
+ return (TYPE_CONTAINS_FOLDERS);
+ }
+ else
+ {
+ if (m_type == TYPE_UNDEFINED)
+ testExistAndGetType();
+
+ return (m_type);
+ }
+}
+
+
+const int IMAPFolder::getFlags()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Root folder
+ if (m_path.isEmpty())
+ {
+ return (FLAG_CHILDREN | FLAG_NO_OPEN);
+ }
+ else
+ {
+ if (m_flags == FLAG_UNDEFINED)
+ testExistAndGetType();
+
+ return (m_flags);
+ }
+}
+
+
+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)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ // Open a connection for this folder
+ IMAPConnection* connection =
+ new IMAPConnection(m_store, m_store->oneTimeAuthenticator());
+
+ 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::ostringstream oss;
+
+ if (mode == MODE_READ_ONLY)
+ oss << "EXAMINE ";
+ else
+ oss << "SELECT ";
+
+ oss << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (connection->hierarchySeparator(), getFullPath()));
+
+ connection->send(true, oss.str(), true);
+
+ // Read the response
+ utility::auto_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",
+ connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("SELECT",
+ connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::response_data* responseData = (*it)->response_data();
+
+ // OK Untagged responses: UNSEEN, PERMANENTFLAGS, UIDVALIDITY (optional)
+ if (responseData->resp_cond_state())
+ {
+ const IMAPParser::resp_text_code* code =
+ responseData->resp_cond_state()->resp_text()->resp_text_code();
+
+ if (code != NULL)
+ {
+ switch (code->type())
+ {
+ case IMAPParser::resp_text_code::UIDVALIDITY:
+
+ m_uidValidity = code->nz_number()->value();
+ 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:
+ {
+ m_type = IMAPUtils::folderTypeFromFlags
+ (responseData->mailbox_data()->mailbox_flag_list());
+
+ m_flags = IMAPUtils::folderFlagsFromFlags
+ (responseData->mailbox_data()->mailbox_flag_list());
+
+ break;
+ }
+ case IMAPParser::mailbox_data::EXISTS:
+ {
+ m_messageCount = responseData->mailbox_data()->number()->value();
+ break;
+ }
+ case IMAPParser::mailbox_data::RECENT:
+ {
+ // TODO
+ break;
+ }
+
+ }
+ }
+ }
+
+ // Check for access mode (read-only or read-write)
+ const IMAPParser::resp_text_code* respTextCode = resp->response_done()->
+ response_tagged()->resp_cond_state()->resp_text()->resp_text_code();
+
+ 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&)
+ {
+ delete (connection);
+ throw;
+ }
+}
+
+
+void IMAPFolder::close(const bool expunge)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ 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();
+
+ oldConnection->send(true, "CLOSE", true);
+ }
+
+ // Close this folder connection
+ oldConnection->disconnect();
+
+ // Now use default store connection
+ m_connection = m_store->connection();
+
+ m_open = false;
+ m_mode = -1;
+
+ m_uidValidity = 0;
+
+ onClose();
+
+ delete (oldConnection);
+}
+
+
+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 int type)
+{
+ if (!m_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 (!m_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 (type & TYPE_CONTAINS_FOLDERS)
+ mailbox += m_connection->hierarchySeparator();
+
+ std::ostringstream oss;
+ oss << "CREATE " << IMAPUtils::quoteString(mailbox);
+
+ m_connection->send(true, oss.str(), true);
+
+
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("CREATE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify folder created
+ events::folderEvent event(this, events::folderEvent::TYPE_CREATED, m_path, m_path);
+ notifyFolder(event);
+}
+
+
+const bool IMAPFolder::exists()
+{
+ if (!isOpen() && !m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return (testExistAndGetType() != TYPE_UNDEFINED);
+}
+
+
+const int IMAPFolder::testExistAndGetType()
+{
+ m_type = TYPE_UNDEFINED;
+
+ // 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
+
+ std::ostringstream oss;
+ oss << "LIST \"\" ";
+ oss << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath()));
+
+ m_connection->send(true, oss.str(), true);
+
+
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("LIST",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Check whether the result mailbox list contains this folder
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("LIST",
+ m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ (*it)->response_data()->mailbox_data();
+
+ // We are only interested in responses of type "LIST"
+ if (mailboxData != NULL && mailboxData->type() == IMAPParser::mailbox_data::LIST)
+ {
+ // Get the folder type/flags at the same time
+ m_type = IMAPUtils::folderTypeFromFlags
+ (mailboxData->mailbox_list()->mailbox_flag_list());
+
+ m_flags = IMAPUtils::folderFlagsFromFlags
+ (mailboxData->mailbox_list()->mailbox_flag_list());
+ }
+ }
+
+ return (m_type);
+}
+
+
+const bool IMAPFolder::isOpen() const
+{
+ return (m_open);
+}
+
+
+message* IMAPFolder::getMessage(const int num)
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ if (num < 1 || num > m_messageCount)
+ throw exceptions::message_not_found();
+
+ return new IMAPMessage(this, num);
+}
+
+
+std::vector <message*> IMAPFolder::getMessages(const int from, const int to)
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ std::vector <message*> v;
+
+ for (int i = from ; i <= to ; ++i)
+ v.push_back(new IMAPMessage(this, i));
+
+ return (v);
+}
+
+
+std::vector <message*> IMAPFolder::getMessages(const std::vector <int>& nums)
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ std::vector <message*> v;
+
+ for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
+ v.push_back(new IMAPMessage(this, *it));
+
+ return (v);
+}
+
+
+const int IMAPFolder::getMessageCount()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_messageCount);
+}
+
+
+folder* IMAPFolder::getFolder(const folder::path::component& name)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return new IMAPFolder(m_path / name, m_store);
+}
+
+
+std::vector <folder*> IMAPFolder::getFolders(const bool recursive)
+{
+ if (!isOpen() && !m_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
+
+ std::ostringstream oss;
+ oss << "LIST ";
+ oss << IMAPUtils::quoteString
+ (IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()));
+
+ if (recursive)
+ oss << " *";
+ else
+ oss << " %";
+
+ m_connection->send(true, oss.str(), true);
+
+
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("LIST", m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+
+ std::vector <folder*> v;
+
+ try
+ {
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("LIST",
+ m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ (*it)->response_data()->mailbox_data();
+
+ if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST)
+ continue;
+
+ // Get folder path
+ const class IMAPParser::mailbox* 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
+ const class IMAPParser::mailbox_flag_list* mailbox_flag_list =
+ mailboxData->mailbox_list()->mailbox_flag_list();
+
+ v.push_back(new IMAPFolder(path, m_store,
+ IMAPUtils::folderTypeFromFlags(mailbox_flag_list),
+ IMAPUtils::folderFlagsFromFlags(mailbox_flag_list)));
+ }
+ }
+ }
+ catch (std::exception&)
+ {
+ for (std::vector <folder*>::iterator it = v.begin() ; it != v.end() ; ++it)
+ delete (*it);
+
+ throw;
+ }
+
+ return (v);
+}
+
+
+void IMAPFolder::fetchMessages(std::vector <message*>& msg, const int options,
+ progressionListener* progress)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ const int total = msg.size();
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ for (std::vector <message*>::iterator it = msg.begin() ;
+ it != msg.end() ; ++it)
+ {
+ dynamic_cast <IMAPMessage*>(*it)->fetch(this, options);
+
+ if (progress)
+ progress->progress(++current, total);
+ }
+
+ if (progress)
+ progress->stop(total);
+}
+
+
+void IMAPFolder::fetchMessage(message* msg, const int options)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ dynamic_cast <IMAPMessage*>(msg)->fetch(this, options);
+}
+
+
+const int IMAPFolder::getFetchCapabilities() const
+{
+ return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | FETCH_STRUCTURE |
+ FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID);
+}
+
+
+folder* IMAPFolder::getParent()
+{
+ return (m_path.isEmpty() ? NULL : new IMAPFolder(m_path.getParent(), m_store));
+}
+
+
+const store* IMAPFolder::getStore() const
+{
+ return (m_store);
+}
+
+
+store* IMAPFolder::getStore()
+{
+ return (m_store);
+}
+
+
+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 = NULL;
+}
+
+
+void IMAPFolder::deleteMessage(const int num)
+{
+ if (!m_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");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << num << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() == num &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= message::FLAG_DELETED;
+ }
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.push_back(num);
+
+ events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::deleteMessages(const int from, const int to)
+{
+ if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ if (!m_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");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << from << ":";
+
+ if (to == -1) command << m_messageCount;
+ else command << to;
+
+ command << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ for (std::vector <IMAPMessage*>::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 <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::deleteMessages(const std::vector <int>& nums)
+{
+ if (nums.empty())
+ throw exceptions::invalid_argument();
+
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Sort the list of message numbers
+ std::vector <int> list;
+
+ list.resize(nums.size());
+ std::copy(nums.begin(), nums.end(), list.begin());
+
+ std::sort(list.begin(), list.end());
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE ";
+ command << IMAPUtils::listToSet(list, m_messageCount, true);
+ command << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ for (std::vector <IMAPMessage*>::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(this, events::messageChangedEvent::TYPE_FLAGS, list);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const int from, const int to, const int flags, const int mode)
+{
+ if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ std::ostringstream oss;
+
+ if (to == -1)
+ oss << from << ":*";
+ else
+ oss << from << ":" << to;
+
+ setMessageFlags(oss.str(), flags, mode);
+
+ // Update local flags
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD:
+ {
+ for (std::vector <IMAPMessage*>::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 <IMAPMessage*>::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 <IMAPMessage*>::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 <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const std::vector <int>& nums, const int flags, const int mode)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Sort the list of message numbers
+ std::vector <int> list;
+
+ list.resize(nums.size());
+ std::copy(nums.begin(), nums.end(), list.begin());
+
+ std::sort(list.begin(), list.end());
+
+ // Delegates call
+ setMessageFlags(IMAPUtils::listToSet(list, m_messageCount, true), flags, mode);
+
+ // Update local flags
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD:
+ {
+ for (std::vector <IMAPMessage*>::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 <IMAPMessage*>::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 <IMAPMessage*>::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(this, events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const string& set, const int flags, const int mode)
+{
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << set;
+
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD: command << " +FLAGS.SILENT "; break;
+ case message::FLAG_MODE_REMOVE: command << " -FLAGS.SILENT "; break;
+ default:
+ case message::FLAG_MODE_SET: command << " FLAGS.SILENT "; break;
+ }
+
+ const string flagList = IMAPUtils::messageFlagList(flags);
+
+ if (!flagList.empty())
+ {
+ command << flagList;
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+ }
+}
+
+
+void IMAPFolder::addMessage(vmime::message* msg, const int flags,
+ vmime::datetime* date, progressionListener* progress)
+{
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ msg->generate(ossAdapter);
+
+ const std::string& str = oss.str();
+ utility::inputStreamStringAdapter strAdapter(str);
+
+ addMessage(strAdapter, str.length(), flags, date, progress);
+}
+
+
+void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int flags,
+ vmime::datetime* date, progressionListener* progress)
+{
+ if (!m_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");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "APPEND " << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath())) << ' ';
+
+ const string flagList = IMAPUtils::messageFlagList(flags);
+
+ if (flags != message::FLAG_UNDEFINED && !flagList.empty())
+ {
+ command << flagList;
+ command << ' ';
+ }
+
+ if (date != NULL)
+ {
+ command << IMAPUtils::dateTime(*date);
+ command << ' ';
+ }
+
+ command << '{' << size << '}';
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ bool ok = false;
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respList
+ = resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respList.begin() ; !ok && (it != respList.end()) ; ++it)
+ {
+ if ((*it)->continue_req())
+ ok = true;
+ }
+
+ if (!ok)
+ {
+ throw exceptions::command_error("APPEND",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Send message data
+ const int total = size;
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ char buffer[65536];
+
+ while (!is.eof())
+ {
+ // Read some data from the input stream
+ const int read = is.read(buffer, sizeof(buffer));
+ current += read;
+
+ // Put read data into socket output stream
+ m_connection->sendRaw(buffer, read);
+
+ // Notify progression
+ if (progress)
+ progress->progress(current, total);
+ }
+
+ m_connection->send(false, "", true);
+
+ if (progress)
+ progress->stop(total);
+
+ // Get the response
+ utility::auto_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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify message added
+ std::vector <int> nums;
+ nums.push_back(m_messageCount + 1);
+
+ events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
+
+ m_messageCount++;
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ events::messageCountEvent event(*it, events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount++;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::expunge()
+{
+ if (!m_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
+ m_connection->send(true, "EXPUNGE", true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("EXPUNGE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update the numbering of the messages
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ std::vector <int> nums;
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("EXPUNGE",
+ m_connection->getParser()->lastLine(), "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 <IMAPMessage*>::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--;
+ }
+ }
+
+ m_messageCount -= nums.size();
+
+ // Notify message expunged
+ events::messageCountEvent event(this, events::messageCountEvent::TYPE_REMOVED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ (*it)->m_messageCount = m_messageCount;
+
+ events::messageCountEvent event(*it, events::messageCountEvent::TYPE_REMOVED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::rename(const folder::path& newPath)
+{
+ if (!m_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 (!m_store->isValidFolderName(newPath.getLastComponent()))
+ throw exceptions::invalid_folder_name();
+
+ // Build the request text
+ std::ostringstream command;
+ command << "RENAME ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath())) << " ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), newPath));
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("RENAME",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify folder renamed
+ folder::path oldPath(m_path);
+
+ m_path = newPath;
+ m_name = newPath.getLastComponent();
+
+ events::folderEvent event(this, events::folderEvent::TYPE_RENAMED, oldPath, newPath);
+ notifyFolder(event);
+
+ // Notify folders with the same path and sub-folders
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == oldPath)
+ {
+ (*it)->m_path = newPath;
+ (*it)->m_name = newPath.getLastComponent();
+
+ events::folderEvent event(*it, events::folderEvent::TYPE_RENAMED, oldPath, newPath);
+ (*it)->notifyFolder(event);
+ }
+ else if ((*it) != this && oldPath.isParentOf((*it)->getFullPath()))
+ {
+ folder::path oldPath((*it)->m_path);
+
+ (*it)->m_path.renameParent(oldPath, newPath);
+
+ events::folderEvent event(*it, events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path);
+ (*it)->notifyFolder(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessage(const folder::path& dest, const int num)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Construct set
+ std::ostringstream set;
+ set << num;
+
+ // Delegate message copy
+ copyMessages(set.str(), dest);
+
+ // Notify message count changed
+ std::vector <int> nums;
+ nums.push_back(num);
+
+ events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ (*it)->m_messageCount++;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const folder::path& dest, const int from, const int to)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ // Construct set
+ std::ostringstream set;
+
+ if (to == -1)
+ set << from << ":*";
+ else
+ set << from << ":" << to;
+
+ // Delegate message copy
+ copyMessages(set.str(), dest);
+
+ // Notify message count changed
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ (*it)->m_messageCount += count;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Delegate message copy
+ copyMessages(IMAPUtils::listToSet(nums, m_messageCount), dest);
+
+ // Notify message count changed
+ const int count = nums.size();
+
+ events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ (*it)->m_messageCount += count;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const string& set, const folder::path& dest)
+{
+ // Build the request text
+ std::ostringstream command;
+ command << "COPY " << set << " ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), dest));
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("COPY",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+}
+
+
+void IMAPFolder::status(int& count, int& unseen)
+{
+ count = 0;
+ unseen = 0;
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STATUS ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath()));
+ command << "(MESSAGES UNSEEN)";
+
+ // Send the request
+ m_store->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_store->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",
+ m_store->m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("STATUS",
+ m_store->m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::response_data* responseData = (*it)->response_data();
+
+ if (responseData->mailbox_data() &&
+ responseData->mailbox_data()->type() == IMAPParser::mailbox_data::STATUS)
+ {
+ const std::vector <IMAPParser::status_info*>& statusList =
+ responseData->mailbox_data()->status_info_list();
+
+ for (std::vector <IMAPParser::status_info*>::const_iterator
+ jt = statusList.begin() ; jt != statusList.end() ; ++jt)
+ {
+ switch ((*jt)->status_att()->type())
+ {
+ case IMAPParser::status_att::MESSAGES:
+
+ count = (*jt)->number()->value();
+ break;
+
+ case IMAPParser::status_att::UNSEEN:
+
+ unseen = (*jt)->number()->value();
+ break;
+
+ default:
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Notify message count changed (new messages)
+ if (m_messageCount != count)
+ {
+ const int oldCount = m_messageCount;
+
+ m_messageCount = count;
+
+ if (count > oldCount)
+ {
+ std::vector <int> nums;
+ nums.reserve(count - oldCount);
+
+ for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ (*it)->m_messageCount = count;
+
+ events::messageCountEvent event(*it, events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+ }
+ }
+}
+
+
+} // imap
+} // messaging
+} // vmime