aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/imap/IMAPFolder.cpp
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2013-12-29 10:02:12 +0100
committerVincent Richard <[email protected]>2013-12-29 10:02:12 +0100
commit152c6bed75598a6ca5efb7914701157270155833 (patch)
tree8faced1d75a45c819630323da256248415992ed0 /src/net/imap/IMAPFolder.cpp
parentMerge branch 'master' of https://github.com/kisli/vmime (diff)
downloadvmime-152c6bed75598a6ca5efb7914701157270155833.tar.gz
vmime-152c6bed75598a6ca5efb7914701157270155833.zip
Merged source and header files in directory structure. Got rid of SConstruct build.
Diffstat (limited to 'src/net/imap/IMAPFolder.cpp')
-rw-r--r--src/net/imap/IMAPFolder.cpp1511
1 files changed, 0 insertions, 1511 deletions
diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp
deleted file mode 100644
index fb98887c..00000000
--- a/src/net/imap/IMAPFolder.cpp
+++ /dev/null
@@ -1,1511 +0,0 @@
-//
-// VMime library (http://www.vmime.org)
-// Copyright (C) 2002-2013 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 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/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, shared_ptr <IMAPStore> store, const int type, const int flags)
- : 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_type(type), m_flags(flags)
-{
- store->registerFolder(this);
-
- m_status = make_shared <IMAPFolderStatus>();
-}
-
-
-IMAPFolder::~IMAPFolder()
-{
- 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();
- }
-}
-
-
-int IMAPFolder::getMode() const
-{
- if (!isOpen())
- throw exceptions::illegal_state("Folder not open");
-
- return (m_mode);
-}
-
-
-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);
- }
-}
-
-
-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)
-{
- 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::ostringstream oss;
-
- if (mode == MODE_READ_ONLY)
- oss << "EXAMINE ";
- else
- oss << "SELECT ";
-
- oss << IMAPUtils::quoteString(IMAPUtils::pathToString
- (connection->hierarchySeparator(), getFullPath()));
-
- if (m_connection->hasCapability("CONDSTORE"))
- oss << " (CONDSTORE)";
-
- connection->send(true, oss.str(), true);
-
- // Read the response
- std::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",
- resp->getErrorLog(), "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",
- resp->getErrorLog(), "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::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:
- {
- m_type = IMAPUtils::folderTypeFromFlags
- (responseData->mailbox_data()->mailbox_flag_list());
-
- m_flags = IMAPUtils::folderFlagsFromFlags
- (responseData->mailbox_data()->mailbox_flag_list());
-
- break;
- }
-
- }
- }
- }
-
- processStatusUpdate(resp.get());
-
- // 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&)
- {
- 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();
-
- oldConnection->send(true, "CLOSE", true);
- }
-
- // 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 int type)
-{
- 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 (type & TYPE_CONTAINS_FOLDERS)
- mailbox += m_connection->hierarchySeparator();
-
- std::ostringstream oss;
- oss << "CREATE " << IMAPUtils::quoteString(mailbox);
-
- m_connection->send(true, oss.str(), true);
-
-
- std::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",
- 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());
-
- std::ostringstream oss;
- oss << "DELETE " << IMAPUtils::quoteString(mailbox);
-
- m_connection->send(true, oss.str(), true);
-
-
- std::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("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() != TYPE_UNDEFINED);
-}
-
-
-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);
-
-
- std::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",
- resp->getErrorLog(), "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",
- resp->getErrorLog(), "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);
-}
-
-
-bool IMAPFolder::isOpen() const
-{
- return (m_open);
-}
-
-
-shared_ptr <message> IMAPFolder::getMessage(const int 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;
-
- if (msgs.isNumberSet())
- {
- const std::vector <int> numbers = IMAPUtils::messageSetToNumberList(msgs);
-
- shared_ptr <IMAPFolder> thisFolder = dynamicCast <IMAPFolder>(shared_from_this());
-
- for (std::vector <int>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it)
- messages.push_back(make_shared <IMAPMessage>(thisFolder, *it));
- }
- else if (msgs.isUIDSet())
- {
- // C: . UID FETCH uuuu1,uuuu2,uuuu3 UID
- // S: * nnnn1 FETCH (UID uuuu1)
- // S: * nnnn2 FETCH (UID uuuu2)
- // S: * nnnn3 FETCH (UID uuuu3)
- // S: . OK UID FETCH completed
-
- // Prepare command and arguments
- std::ostringstream cmd;
- cmd.imbue(std::locale::classic());
-
- cmd << "UID FETCH " << IMAPUtils::messageSetToSequenceSet(msgs) << " UID";
-
- // Send the request
- m_connection->send(true, cmd.str(), true);
-
- // Get the response
- std::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
-
- if (resp->isBad() || resp->response_done()->response_tagged()->
- resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
- {
- throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "bad response");
- }
-
- // Process the response
- const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
- resp->continue_req_or_response_data();
-
- 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("UID FETCH ... UID",
- resp->getErrorLog(), "invalid response");
- }
-
- 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 Process fetch response for this message
- const int msgNum = static_cast <int>(messageData->number());
- message::uid msgUID;
-
- // Find UID in message attributes
- const std::vector <IMAPParser::msg_att_item*> atts = messageData->msg_att()->items();
-
- for (std::vector <IMAPParser::msg_att_item*>::const_iterator
- it = atts.begin() ; it != atts.end() ; ++it)
- {
- if ((*it)->type() == IMAPParser::msg_att_item::UID)
- {
- msgUID = (*it)->unique_id()->value();
- break;
- }
- }
-
- if (!msgUID.empty())
- {
- shared_ptr <IMAPFolder> thisFolder = dynamicCast <IMAPFolder>(shared_from_this());
- messages.push_back(make_shared <IMAPMessage>(thisFolder, msgNum, msgUID));
- }
- }
- }
-
- return messages;
-}
-
-
-int 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);
-}
-
-
-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
-
- std::ostringstream oss;
- oss << "LIST ";
-
- const string pathString = IMAPUtils::pathToString
- (m_connection->hierarchySeparator(), getFullPath());
-
- if (recursive)
- {
- oss << IMAPUtils::quoteString(pathString);
- oss << " *";
- }
- else
- {
- if (pathString.empty()) // don't add sep for root folder
- oss << "\"\"";
- else
- oss << IMAPUtils::quoteString(pathString + m_connection->hierarchySeparator());
-
- oss << " %";
- }
-
- m_connection->send(true, oss.str(), true);
-
-
- std::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", resp->getErrorLog(), "bad response");
- }
-
- const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
- resp->continue_req_or_response_data();
-
-
- std::vector <shared_ptr <folder> > v;
-
- 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",
- resp->getErrorLog(), "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(make_shared <IMAPFolder>(path, store,
- IMAPUtils::folderTypeFromFlags(mailbox_flag_list),
- IMAPUtils::folderFlagsFromFlags(mailbox_flag_list)));
- }
- }
-
- 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");
-
- // Build message numbers list
- std::vector <int> list;
- list.reserve(msg.size());
-
- std::map <int, 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
- const string command = IMAPUtils::buildFetchRequest
- (m_connection, messageSet::byNumber(list), options);
-
- m_connection->send(true, command, true);
-
- // Get the response
- std::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("FETCH",
- resp->getErrorLog(), "bad response");
- }
-
- const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
- resp->continue_req_or_response_data();
-
- const size_t total = msg.size();
- size_t current = 0;
-
- if (progress)
- progress->start(total);
-
- 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("FETCH",
- resp->getErrorLog(), "invalid response");
- }
-
- 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;
-
- // Process fetch response for this message
- const int num = static_cast <int>(messageData->number());
-
- std::map <int, 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(shared_ptr <message> msg, const fetchAttributes& options)
-{
- std::vector <shared_ptr <message> > msgs;
- msgs.push_back(msg);
-
- fetchMessages(msgs, options, /* progress */ NULL);
-}
-
-
-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 <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");
-
- // Build the request text
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- if (msgs.isUIDSet())
- command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
- else
- command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
-
- command << " +FLAGS (\\Deleted)";
-
- // Send the request
- m_connection->send(true, command.str(), true);
-
- // Get the response
- std::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
-
- if (resp->isBad() || resp->response_done()->response_tagged()->
- resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
- {
- throw exceptions::command_error("STORE",
- resp->getErrorLog(), "bad response");
- }
-
- processStatusUpdate(resp.get());
-}
-
-
-void IMAPFolder::setMessageFlags(const messageSet& msgs, const int flags, const int mode)
-{
- // Build the request text
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- if (msgs.isUIDSet())
- command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
- else
- command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
-
- switch (mode)
- {
- case message::FLAG_MODE_ADD: command << " +FLAGS "; break;
- case message::FLAG_MODE_REMOVE: command << " -FLAGS "; break;
- default:
- case message::FLAG_MODE_SET: command << " FLAGS "; 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
- std::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
-
- if (resp->isBad() || resp->response_done()->response_tagged()->
- resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
- {
- throw exceptions::command_error("STORE",
- resp->getErrorLog(), "bad response");
- }
-
- processStatusUpdate(resp.get());
- }
-}
-
-
-void IMAPFolder::addMessage(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);
-
- addMessage(strAdapter, str.length(), flags, date, progress);
-}
-
-
-void 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");
-
- // Build the request text
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- 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
- std::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",
- resp->getErrorLog(), "bad response");
- }
-
- // 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, sizeof(buffer));
- current += read;
-
- // Put read data into socket output stream
- m_connection->sendRaw(buffer, read);
-
- // Notify progress
- if (progress)
- progress->progress(current, total);
- }
-
- m_connection->send(false, "", true);
-
- if (progress)
- progress->stop(total);
-
- // Get the response
- std::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",
- resp->getErrorLog(), "bad response");
- }
-
- processStatusUpdate(resp.get());
-}
-
-
-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
- m_connection->send(true, "EXPUNGE", true);
-
- // Get the response
- std::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",
- 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();
-
- // Build the request text
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- 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
- std::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",
- 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());
-}
-
-
-void 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");
-
- // Build the request text
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- command << "COPY " << IMAPUtils::messageSetToSequenceSet(set) << " ";
- command << IMAPUtils::quoteString(IMAPUtils::pathToString
- (m_connection->hierarchySeparator(), dest));
-
- // Send the request
- m_connection->send(true, command.str(), true);
-
- // Get the response
- std::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",
- resp->getErrorLog(), "bad response");
- }
-
- processStatusUpdate(resp.get());
-}
-
-
-void IMAPFolder::status(int& count, int& 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 request text
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- command << "STATUS ";
- command << IMAPUtils::quoteString(IMAPUtils::pathToString
- (m_connection->hierarchySeparator(), getFullPath()));
- command << " (";
-
- command << "MESSAGES" << ' ' << "UNSEEN" << ' ' << "UIDNEXT" << ' ' << "UIDVALIDITY";
-
- if (m_connection->hasCapability("CONDSTORE"))
- command << ' ' << "HIGHESTMODSEQ";
-
- command << ")";
-
- // Send the request
- m_connection->send(true, command.str(), true);
-
- // Get the response
- std::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("STATUS",
- resp->getErrorLog(), "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)
- {
- const IMAPParser::response_data* 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");
-
- m_connection->send(true, "NOOP", true);
-
- std::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("NOOP", resp->getErrorLog());
- }
-
- processStatusUpdate(resp.get());
-}
-
-
-std::vector <int> IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& uid)
-{
- std::vector<int> v;
-
- std::ostringstream command;
- command.imbue(std::locale::classic());
-
- command << "SEARCH UID " << uid << ":*";
-
- // Send the request
- m_connection->send(true, command.str(), true);
-
- // Get the response
- std::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("SEARCH",
- resp->getErrorLog(), "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("SEARCH",
- resp->getErrorLog(), "invalid response");
- }
-
- const IMAPParser::mailbox_data* mailboxData =
- (*it)->response_data()->mailbox_data();
-
- // We are only interested in responses of type "SEARCH"
- if (mailboxData == NULL ||
- mailboxData->type() != IMAPParser::mailbox_data::SEARCH)
- {
- continue;
- }
-
- for (std::vector <IMAPParser::nz_number*>::const_iterator
- it = mailboxData->search_nz_number_list().begin() ;
- it != mailboxData->search_nz_number_list().end();
- ++it)
- {
- v.push_back((*it)->value());
- }
- }
-
- processStatusUpdate(resp.get());
-
- return v;
-}
-
-
-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())
- {
- const IMAPParser::resp_text_code* code =
- resp->response_done()->response_tagged()
- ->resp_cond_state()->resp_text()->resp_text_code();
-
- m_status->updateFromResponse(code);
- }
-
- // Process untagged responses
- for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
- 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())
- {
- const IMAPParser::resp_text_code* code =
- (*it)->response_data()->resp_cond_state()->resp_text()->resp_text_code();
-
- m_status->updateFromResponse(code);
- }
- else if ((*it)->response_data() && (*it)->response_data()->mailbox_data())
- {
- 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 <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 <int>(1, msgNumber)));
- }
- else if ((*it)->response_data()->message_data()->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 <int>(1, msgNumber)));
-
- expungedMessageCount++;
- }
- }
- }
-
- // New messages arrived
- if (m_status->getMessageCount() > oldStatus->getMessageCount() - expungedMessageCount)
- {
- std::vector <int> newMessageNumbers;
-
- for (int 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
-