aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/net/maildir/maildirFolder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/net/maildir/maildirFolder.cpp')
-rw-r--r--vmime-master/src/vmime/net/maildir/maildirFolder.cpp1365
1 files changed, 1365 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/maildir/maildirFolder.cpp b/vmime-master/src/vmime/net/maildir/maildirFolder.cpp
new file mode 100644
index 0000000..8c02025
--- /dev/null
+++ b/vmime-master/src/vmime/net/maildir/maildirFolder.cpp
@@ -0,0 +1,1365 @@
+//
+// 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_MAILDIR
+
+
+#include "vmime/net/maildir/maildirFolder.hpp"
+
+#include "vmime/net/maildir/maildirStore.hpp"
+#include "vmime/net/maildir/maildirMessage.hpp"
+#include "vmime/net/maildir/maildirUtils.hpp"
+#include "vmime/net/maildir/maildirFormat.hpp"
+#include "vmime/net/maildir/maildirFolderStatus.hpp"
+
+#include "vmime/message.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platform.hpp"
+
+#include "vmime/utility/outputStreamAdapter.hpp"
+#include "vmime/utility/inputStreamStringAdapter.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+maildirFolder::maildirFolder(
+ const folder::path& path,
+ const shared_ptr <maildirStore>& store
+)
+ : m_store(store),
+ m_path(path),
+ m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()),
+ m_mode(-1),
+ m_open(false),
+ m_unreadMessageCount(0),
+ m_messageCount(0) {
+
+ store->registerFolder(this);
+}
+
+
+maildirFolder::~maildirFolder() {
+
+ try {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (store) {
+
+ if (m_open) {
+ close(false);
+ }
+
+ store->unregisterFolder(this);
+
+ } else if (m_open) {
+
+ close(false);
+ }
+
+ } catch (...) {
+
+ // Don't throw in destructor
+ }
+}
+
+
+void maildirFolder::onStoreDisconnected() {
+
+ m_store.reset();
+}
+
+
+int maildirFolder::getMode() const {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ return m_mode;
+}
+
+
+const folderAttributes maildirFolder::getAttributes() {
+
+ folderAttributes attribs;
+
+ if (m_path.isEmpty()) {
+ attribs.setType(folderAttributes::TYPE_CONTAINS_FOLDERS);
+ } else {
+ attribs.setType(folderAttributes::TYPE_CONTAINS_FOLDERS | folderAttributes::TYPE_CONTAINS_MESSAGES);
+ }
+
+ if (m_store.lock()->getFormat()->folderHasSubfolders(m_path)) {
+ attribs.setFlags(folderAttributes::FLAG_HAS_CHILDREN); // contains at least one sub-folder
+ }
+
+ return attribs;
+}
+
+
+const folder::path::component maildirFolder::getName() const {
+
+ return m_name;
+}
+
+
+const folder::path maildirFolder::getFullPath() const {
+
+ return m_path;
+}
+
+
+void maildirFolder::open(const int mode, bool /* failIfModeIsNotAvailable */) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (isOpen()) {
+ throw exceptions::illegal_state("Folder is already open");
+ } else if (!exists()) {
+ throw exceptions::illegal_state("Folder does not exist");
+ }
+
+ scanFolder();
+
+ m_open = true;
+ m_mode = mode;
+}
+
+
+void maildirFolder::close(const bool expunge) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ if (expunge) {
+ this->expunge();
+ }
+
+ m_open = false;
+ m_mode = -1;
+
+ onClose();
+}
+
+
+void maildirFolder::onClose() {
+
+ for (std::vector <maildirMessage*>::iterator it = m_messages.begin() ;
+ it != m_messages.end() ; ++it) {
+
+ (*it)->onFolderClosed();
+ }
+
+ m_messages.clear();
+}
+
+
+void maildirFolder::registerMessage(maildirMessage* msg) {
+
+ m_messages.push_back(msg);
+}
+
+
+void maildirFolder::unregisterMessage(maildirMessage* msg) {
+
+ std::vector <maildirMessage*>::iterator it =
+ std::find(m_messages.begin(), m_messages.end(), msg);
+
+ if (it != m_messages.end()) {
+ m_messages.erase(it);
+ }
+}
+
+
+void maildirFolder::create(const folderAttributes& /* attribs */) {
+
+ shared_ptr <maildirStore> 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();
+ }
+
+ // Create directory on file system
+ try {
+ store->getFormat()->createFolder(m_path);
+ } catch (exceptions::filesystem_exception& e) {
+ throw exceptions::command_error("CREATE", "", "File system exception", e);
+ }
+
+ // 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 maildirFolder::destroy() {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (isOpen()) {
+ throw exceptions::illegal_state("Folder is open");
+ }
+
+ // Delete folder
+ try {
+ store->getFormat()->destroyFolder(m_path);
+ } catch (std::exception&) {
+ // Ignore exception: anyway, we can't recover from this...
+ }
+
+ // 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 maildirFolder::exists() {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ return store->getFormat()->folderExists(m_path);
+}
+
+
+bool maildirFolder::isOpen() const {
+
+ return m_open;
+}
+
+
+void maildirFolder::scanFolder() {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ try {
+
+ m_messageCount = 0;
+ m_unreadMessageCount = 0;
+
+ shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
+
+ utility::file::path newDirPath =
+ store->getFormat()->folderPathToFileSystemPath(m_path, maildirFormat::NEW_DIRECTORY);
+ shared_ptr <utility::file> newDir = fsf->create(newDirPath);
+
+ utility::file::path curDirPath =
+ store->getFormat()->folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY);
+ shared_ptr <utility::file> curDir = fsf->create(curDirPath);
+
+ // New received messages (new/)
+ shared_ptr <utility::fileIterator> nit = newDir->getFiles();
+ std::vector <utility::file::path::component> newMessageFilenames;
+
+ while (nit->hasMoreElements()) {
+
+ shared_ptr <utility::file> file = nit->nextElement();
+
+ if (maildirUtils::isMessageFile(*file)) {
+ newMessageFilenames.push_back(file->getFullPath().getLastComponent());
+ }
+ }
+
+ // Current messages (cur/)
+ shared_ptr <utility::fileIterator> cit = curDir->getFiles();
+ std::vector <utility::file::path::component> curMessageFilenames;
+
+ while (cit->hasMoreElements()) {
+
+ shared_ptr <utility::file> file = cit->nextElement();
+
+ if (maildirUtils::isMessageFile(*file)) {
+ curMessageFilenames.push_back(file->getFullPath().getLastComponent());
+ }
+ }
+
+ // Update/delete existing messages (found in previous scan)
+ for (size_t i = 0 ; i < m_messageInfos.size() ; ++i) {
+
+ messageInfos& msgInfos = m_messageInfos[i];
+
+ // NOTE: the flags may have changed (eg. moving from 'new' to 'cur'
+ // may imply the 'S' flag) and so the filename. That's why we use
+ // "maildirUtils::messageIdComparator" to compare only the 'unique'
+ // portion of the filename...
+
+ if (msgInfos.type == messageInfos::TYPE_CUR) {
+
+ const std::vector <utility::file::path::component>::iterator pos =
+ std::find_if(
+ curMessageFilenames.begin(),
+ curMessageFilenames.end(),
+ maildirUtils::messageIdComparator(msgInfos.path)
+ );
+
+ // If we cannot find this message in the 'cur' directory,
+ // it means it has been deleted (and expunged).
+ if (pos == curMessageFilenames.end()) {
+
+ msgInfos.type = messageInfos::TYPE_DELETED;
+
+ // Otherwise, update its information.
+ } else {
+
+ msgInfos.path = *pos;
+ curMessageFilenames.erase(pos);
+ }
+ }
+ }
+
+ m_messageInfos.reserve(m_messageInfos.size()
+ + newMessageFilenames.size() + curMessageFilenames.size());
+
+ // Add new messages from 'new': we are responsible to move the files
+ // from the 'new' directory to the 'cur' directory, and append them
+ // to our message list.
+ for (std::vector <utility::file::path::component>::const_iterator
+ it = newMessageFilenames.begin() ; it != newMessageFilenames.end() ; ++it) {
+
+ const utility::file::path::component newFilename =
+ maildirUtils::buildFilename(maildirUtils::extractId(*it), 0);
+
+ // Move messages from 'new' to 'cur'
+ shared_ptr <utility::file> file = fsf->create(newDirPath / *it);
+ file->rename(curDirPath / newFilename);
+
+ // Append to message list
+ messageInfos msgInfos;
+ msgInfos.path = newFilename;
+
+ if (maildirUtils::extractFlags(msgInfos.path) & message::FLAG_DELETED) {
+ msgInfos.type = messageInfos::TYPE_DELETED;
+ } else {
+ msgInfos.type = messageInfos::TYPE_CUR;
+ }
+
+ m_messageInfos.push_back(msgInfos);
+ }
+
+ // Add new messages from 'cur': the files have already been moved
+ // from 'new' to 'cur'. Just append them to our message list.
+ for (std::vector <utility::file::path::component>::const_iterator
+ it = curMessageFilenames.begin() ; it != curMessageFilenames.end() ; ++it) {
+
+ // Append to message list
+ messageInfos msgInfos;
+ msgInfos.path = *it;
+
+ if (maildirUtils::extractFlags(msgInfos.path) & message::FLAG_DELETED) {
+ msgInfos.type = messageInfos::TYPE_DELETED;
+ } else {
+ msgInfos.type = messageInfos::TYPE_CUR;
+ }
+
+ m_messageInfos.push_back(msgInfos);
+ }
+
+ // Update message count
+ size_t unreadMessageCount = 0;
+
+ for (std::vector <messageInfos>::const_iterator
+ it = m_messageInfos.begin() ; it != m_messageInfos.end() ; ++it) {
+
+ if ((maildirUtils::extractFlags((*it).path) & message::FLAG_SEEN) == 0) {
+ ++unreadMessageCount;
+ }
+ }
+
+ m_unreadMessageCount = unreadMessageCount;
+ m_messageCount = static_cast <size_t>(m_messageInfos.size());
+
+ } catch (exceptions::filesystem_exception&) {
+
+ // Should not happen...
+ }
+}
+
+
+shared_ptr <message> maildirFolder::getMessage(const size_t num) {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ if (num < 1 || num > m_messageCount) {
+ throw exceptions::message_not_found();
+ }
+
+ return make_shared <maildirMessage>(dynamicCast <maildirFolder>(shared_from_this()), num);
+}
+
+
+std::vector <shared_ptr <message> > maildirFolder::getMessages(const messageSet& msgs) {
+
+ if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ if (msgs.isNumberSet()) {
+
+ const std::vector <size_t> numbers = maildirUtils::messageSetToNumberList(msgs, m_messageCount);
+
+ std::vector <shared_ptr <message> > messages;
+ shared_ptr <maildirFolder> thisFolder = dynamicCast <maildirFolder>(shared_from_this());
+
+ for (std::vector <size_t>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it) {
+
+ if (*it < 1|| *it > m_messageCount) {
+ throw exceptions::message_not_found();
+ }
+
+ messages.push_back(make_shared <maildirMessage>(thisFolder, *it));
+ }
+
+ return messages;
+
+ } else {
+
+ throw exceptions::operation_not_supported();
+ }
+}
+
+
+size_t maildirFolder::getMessageCount() {
+
+ return m_messageCount;
+}
+
+
+shared_ptr <folder> maildirFolder::getFolder(const folder::path::component& name) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ return shared_ptr <maildirFolder>(new maildirFolder(m_path / name, store));
+}
+
+
+std::vector <shared_ptr <folder> > maildirFolder::getFolders(const bool recursive) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (!isOpen() && !store) {
+ throw exceptions::illegal_state("Store disconnected");
+ }
+
+ std::vector <shared_ptr <folder> > list;
+
+ listFolders(list, recursive);
+
+ return list;
+}
+
+
+void maildirFolder::listFolders(std::vector <shared_ptr <folder> >& list, const bool recursive) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ try {
+
+ std::vector <folder::path> pathList =
+ store->getFormat()->listFolders(m_path, recursive);
+
+ list.reserve(pathList.size());
+
+ for (std::vector <folder::path>::size_type i = 0, n = pathList.size() ; i < n ; ++i) {
+
+ shared_ptr <maildirFolder> subFolder(
+ new maildirFolder(pathList[i], store)
+ );
+
+ list.push_back(subFolder);
+ }
+
+ } catch (exceptions::filesystem_exception& e) {
+
+ throw exceptions::command_error("LIST", "", "", e);
+ }
+}
+
+
+void maildirFolder::rename(const folder::path& newPath) {
+
+ shared_ptr <maildirStore> 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 (!store->isValidFolderName(newPath.getLastComponent())) {
+ throw exceptions::invalid_folder_name();
+ }
+
+ // Rename the directory on the file system
+ try {
+ store->getFormat()->renameFolder(m_path, newPath);
+ } catch (vmime::exception& e) {
+ throw exceptions::command_error("RENAME", "", "", e);
+ }
+
+ // 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 folders with the same path
+ for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ;
+ it != store->m_folders.end() ; ++it) {
+
+ if ((*it) != this && (*it)->getFullPath() == oldPath) {
+
+ (*it)->m_path = newPath;
+ (*it)->m_name = newPath.getLastComponent();
+
+ shared_ptr <events::folderEvent> event =
+ make_shared <events::folderEvent>(
+ dynamicCast <folder>((*it)->shared_from_this()),
+ 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);
+
+ 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);
+ }
+ }
+}
+
+
+void maildirFolder::deleteMessages(const messageSet& msgs) {
+
+ // Mark messages as deleted
+ setMessageFlags(msgs, message::FLAG_DELETED, message::FLAG_MODE_ADD);
+}
+
+
+void maildirFolder::setMessageFlags(
+ const messageSet& msgs,
+ const int flags,
+ const int mode
+) {
+
+ shared_ptr <maildirStore> 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");
+ }
+
+ if (msgs.isNumberSet()) {
+
+ const std::vector <size_t> nums = maildirUtils::messageSetToNumberList(msgs, m_messageCount);
+
+ // Change message flags
+ shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
+
+ utility::file::path curDirPath = store->getFormat()->
+ folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY);
+
+ for (std::vector <size_t>::const_iterator it =
+ nums.begin() ; it != nums.end() ; ++it) {
+
+ const size_t num = *it - 1;
+
+ try {
+
+ const utility::file::path::component path = m_messageInfos[num].path;
+ shared_ptr <utility::file> file = fsf->create(curDirPath / path);
+
+ int newFlags = maildirUtils::extractFlags(path);
+
+ switch (mode) {
+ case message::FLAG_MODE_ADD: newFlags |= flags; break;
+ case message::FLAG_MODE_REMOVE: newFlags &= ~flags; break;
+ default:
+ case message::FLAG_MODE_SET: newFlags = flags; break;
+ }
+
+ const utility::file::path::component newPath =
+ maildirUtils::buildFilename(maildirUtils::extractId(path), newFlags);
+
+ file->rename(curDirPath / newPath);
+
+ if (flags & message::FLAG_DELETED) {
+ m_messageInfos[num].type = messageInfos::TYPE_DELETED;
+ } else {
+ m_messageInfos[num].type = messageInfos::TYPE_CUR;
+ }
+
+ m_messageInfos[num].path = newPath;
+
+ } catch (exceptions::filesystem_exception& e) {
+
+ // Ignore (not important)
+ }
+ }
+
+ // Update local flags
+ switch (mode) {
+
+ case message::FLAG_MODE_ADD: {
+
+ for (std::vector <maildirMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it) {
+
+ if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != maildirMessage::FLAG_UNDEFINED) {
+
+ (*it)->m_flags |= flags;
+ }
+ }
+
+ break;
+ }
+ case message::FLAG_MODE_REMOVE: {
+
+ for (std::vector <maildirMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it) {
+
+ if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != maildirMessage::FLAG_UNDEFINED) {
+
+ (*it)->m_flags &= ~flags;
+ }
+ }
+
+ break;
+ }
+ default:
+ case message::FLAG_MODE_SET: {
+
+ for (std::vector <maildirMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it) {
+
+ if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != maildirMessage::FLAG_UNDEFINED) {
+
+ (*it)->m_flags = flags;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ // Notify message flags changed
+ shared_ptr <events::messageChangedEvent> event =
+ make_shared <events::messageChangedEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::messageChangedEvent::TYPE_FLAGS,
+ nums
+ );
+
+ notifyMessageChanged(event);
+
+ // TODO: notify other folders with the same path
+
+ } else {
+
+ throw exceptions::operation_not_supported();
+ }
+}
+
+
+messageSet maildirFolder::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 maildirFolder::addMessage(
+ utility::inputStream& is,
+ const size_t size,
+ const int flags,
+ vmime::datetime* /* date */,
+ utility::progressListener* progress
+) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ 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");
+ }
+
+ shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
+
+ utility::file::path tmpDirPath = store->getFormat()->
+ folderPathToFileSystemPath(m_path,maildirFormat::TMP_DIRECTORY);
+ utility::file::path dstDirPath = store->getFormat()->
+ folderPathToFileSystemPath(
+ m_path,
+ flags == message::FLAG_RECENT ?
+ maildirFormat::NEW_DIRECTORY :
+ maildirFormat::CUR_DIRECTORY
+ );
+
+ const utility::file::path::component filename =
+ maildirUtils::buildFilename(maildirUtils::generateId(), ((flags == -1) ? 0 : flags));
+
+ try {
+ shared_ptr <utility::file> tmpDir = fsf->create(tmpDirPath);
+ tmpDir->createDirectory(true);
+ } catch (exceptions::filesystem_exception&) {
+ // Don't throw now, it will fail later...
+ }
+
+ try {
+ shared_ptr <utility::file> curDir = fsf->create(dstDirPath);
+ curDir->createDirectory(true);
+ } catch (exceptions::filesystem_exception&) {
+ // Don't throw now, it will fail later...
+ }
+
+ // Actually add the message
+ copyMessageImpl(tmpDirPath, dstDirPath, filename, is, size, progress);
+
+ // Append the message to the cache list
+ messageInfos msgInfos;
+ msgInfos.path = filename;
+ msgInfos.type = messageInfos::TYPE_CUR;
+
+ m_messageInfos.push_back(msgInfos);
+ m_messageCount++;
+
+ if ((flags == -1) || !(flags & message::FLAG_SEEN)) {
+ m_unreadMessageCount++;
+ }
+
+ // Notification
+ std::vector <size_t> nums;
+ nums.push_back(m_messageCount);
+
+ shared_ptr <events::messageCountEvent> event =
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::messageCountEvent::TYPE_ADDED,
+ nums
+ );
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ;
+ it != store->m_folders.end() ; ++it) {
+
+ if ((*it) != this && (*it)->getFullPath() == m_path) {
+
+ (*it)->m_messageCount = m_messageCount;
+ (*it)->m_unreadMessageCount = m_unreadMessageCount;
+
+ (*it)->m_messageInfos.resize(m_messageInfos.size());
+ std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin());
+
+ shared_ptr <events::messageCountEvent> event =
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>((*it)->shared_from_this()),
+ events::messageCountEvent::TYPE_ADDED,
+ nums
+ );
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+
+ return messageSet::empty();
+}
+
+
+void maildirFolder::copyMessageImpl(
+ const utility::file::path& tmpDirPath,
+ const utility::file::path& dstDirPath,
+ const utility::file::path::component& filename,
+ utility::inputStream& is, const size_t size,
+ utility::progressListener* progress
+) {
+
+ shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
+
+ shared_ptr <utility::file> file = fsf->create(tmpDirPath / filename);
+
+ if (progress) {
+ progress->start(size);
+ }
+
+ // First, write the message into 'tmp'...
+ try {
+
+ file->createFile();
+
+ shared_ptr <utility::fileWriter> fw = file->getFileWriter();
+ shared_ptr <utility::outputStream> os = fw->getOutputStream();
+
+ byte_t buffer[65536];
+ size_t total = 0;
+
+ while (!is.eof()) {
+
+ const size_t read = is.read(buffer, sizeof(buffer));
+
+ if (read != 0) {
+ os->write(buffer, read);
+ total += read;
+ }
+
+ if (progress) {
+ progress->progress(total, size);
+ }
+ }
+
+ os->flush();
+
+ } catch (exception& e) {
+
+ if (progress) {
+ progress->stop(size);
+ }
+
+ // Delete temporary file
+ try {
+ shared_ptr <utility::file> file = fsf->create(tmpDirPath / filename);
+ file->remove();
+ } catch (exceptions::filesystem_exception&) {
+ // Ignore
+ }
+
+ throw exceptions::command_error("ADD", "", "", e);
+ }
+
+ // ...then, move it to 'cur'
+ try {
+
+ file->rename(dstDirPath / filename);
+
+ } catch (exception& e) {
+
+ if (progress) {
+ progress->stop(size);
+ }
+
+ // Delete temporary file
+ try {
+
+ file->remove();
+ shared_ptr <utility::file> file = fsf->create(dstDirPath / filename);
+ file->remove();
+
+ } catch (exceptions::filesystem_exception&) {
+
+ // Ignore
+ }
+
+ throw exceptions::command_error("ADD", "", "", e);
+ }
+
+ if (progress) {
+ progress->stop(size);
+ }
+}
+
+
+messageSet maildirFolder::copyMessages(const folder::path& dest, const messageSet& msgs) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
+
+ utility::file::path curDirPath =
+ store->getFormat()->folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY);
+
+ utility::file::path destCurDirPath = store->getFormat()->
+ folderPathToFileSystemPath(dest, maildirFormat::CUR_DIRECTORY);
+ utility::file::path destTmpDirPath = store->getFormat()->
+ folderPathToFileSystemPath(dest, maildirFormat::TMP_DIRECTORY);
+
+ // Create destination directories
+ try {
+
+ shared_ptr <utility::file> destTmpDir = fsf->create(destTmpDirPath);
+ destTmpDir->createDirectory(true);
+
+ } catch (exceptions::filesystem_exception&) {
+
+ // Don't throw now, it will fail later...
+ }
+
+ try {
+
+ shared_ptr <utility::file> destCurDir = fsf->create(destCurDirPath);
+ destCurDir->createDirectory(true);
+
+ } catch (exceptions::filesystem_exception&) {
+
+ // Don't throw now, it will fail later...
+ }
+
+ // Copy messages
+ const std::vector <size_t> nums = maildirUtils::messageSetToNumberList(msgs, m_messageCount);
+
+ try {
+
+ for (std::vector <size_t>::const_iterator it =
+ nums.begin() ; it != nums.end() ; ++it) {
+
+ const size_t num = *it;
+ const messageInfos& msg = m_messageInfos[num - 1];
+ const int flags = maildirUtils::extractFlags(msg.path);
+
+ const utility::file::path::component filename =
+ maildirUtils::buildFilename(maildirUtils::generateId(), flags);
+
+ shared_ptr <utility::file> file = fsf->create(curDirPath / msg.path);
+ shared_ptr <utility::fileReader> fr = file->getFileReader();
+ shared_ptr <utility::inputStream> is = fr->getInputStream();
+
+ copyMessageImpl(destTmpDirPath, destCurDirPath,
+ filename, *is, file->getLength(), NULL);
+ }
+
+ } catch (exception& e) {
+
+ notifyMessagesCopied(dest);
+ throw exceptions::command_error("COPY", "", "", e);
+ }
+
+ notifyMessagesCopied(dest);
+
+ return messageSet::empty();
+}
+
+
+void maildirFolder::notifyMessagesCopied(const folder::path& dest) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ;
+ it != store->m_folders.end() ; ++it) {
+
+ if ((*it) != this && (*it)->getFullPath() == dest) {
+
+ // We only need to update the first folder we found as calling
+ // status() will notify all the folders with the same path.
+ size_t count, unseen;
+ (*it)->status(count, unseen);
+
+ return;
+ }
+ }
+}
+
+
+void maildirFolder::status(size_t& count, size_t& unseen) {
+
+ count = 0;
+ unseen = 0;
+
+ shared_ptr <folderStatus> status = getStatus();
+
+ count = status->getMessageCount();
+ unseen = status->getUnseenCount();
+
+ m_messageCount = count;
+}
+
+
+shared_ptr <folderStatus> maildirFolder::getStatus() {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ const size_t oldCount = m_messageCount;
+
+ scanFolder();
+
+ shared_ptr <maildirFolderStatus> status = make_shared <maildirFolderStatus>();
+
+ status->setMessageCount(m_messageCount);
+ status->setUnseenCount(m_unreadMessageCount);
+
+ // Notify message count changed (new messages)
+ if (m_messageCount > oldCount) {
+
+ std::vector <size_t> nums;
+ nums.reserve(m_messageCount - oldCount);
+
+ for (size_t i = oldCount + 1, j = 0 ; i <= m_messageCount ; ++i, ++j) {
+ nums[j] = i;
+ }
+
+ shared_ptr <events::messageCountEvent> event =
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::messageCountEvent::TYPE_ADDED,
+ nums
+ );
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ;
+ it != store->m_folders.end() ; ++it) {
+
+ if ((*it) != this && (*it)->getFullPath() == m_path) {
+
+ (*it)->m_messageCount = m_messageCount;
+ (*it)->m_unreadMessageCount = m_unreadMessageCount;
+
+ (*it)->m_messageInfos.resize(m_messageInfos.size());
+ std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin());
+
+ shared_ptr <events::messageCountEvent> event =
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>((*it)->shared_from_this()),
+ events::messageCountEvent::TYPE_ADDED,
+ nums
+ );
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+ }
+
+ return status;
+}
+
+
+void maildirFolder::expunge() {
+
+ shared_ptr <maildirStore> 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");
+ }
+
+ shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();
+
+ utility::file::path curDirPath = store->getFormat()->
+ folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY);
+
+ std::vector <size_t> nums;
+ size_t unreadCount = 0;
+
+ for (size_t num = 1 ; num <= m_messageCount ; ++num) {
+
+ messageInfos& infos = m_messageInfos[num - 1];
+
+ if (infos.type == messageInfos::TYPE_DELETED) {
+
+ nums.push_back(num);
+
+ for (std::vector <maildirMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it) {
+
+ if ((*it)->m_num == num) {
+ (*it)->m_expunged = true;
+ } else if ((*it)->m_num > num) {
+ (*it)->m_num--;
+ }
+ }
+
+ if (maildirUtils::extractFlags(infos.path) & message::FLAG_SEEN) {
+ ++unreadCount;
+ }
+
+ // Delete file from file system
+ try {
+ shared_ptr <utility::file> file = fsf->create(curDirPath / infos.path);
+ file->remove();
+ } catch (exceptions::filesystem_exception& e) {
+ // Ignore (not important)
+ }
+ }
+ }
+
+ if (!nums.empty()) {
+
+ for (std::vector <size_t>::size_type i = nums.size() ; i != 0 ; --i) {
+ m_messageInfos.erase(m_messageInfos.begin() + (i - 1));
+ }
+ }
+
+ m_messageCount -= static_cast <size_t>(nums.size());
+ m_unreadMessageCount -= unreadCount;
+
+ // Notify message expunged
+ shared_ptr <events::messageCountEvent> event =
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>(shared_from_this()),
+ events::messageCountEvent::TYPE_REMOVED,
+ nums
+ );
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ;
+ it != store->m_folders.end() ; ++it) {
+
+ if ((*it) != this && (*it)->getFullPath() == m_path) {
+
+ (*it)->m_messageCount = m_messageCount;
+ (*it)->m_unreadMessageCount = m_unreadMessageCount;
+
+ (*it)->m_messageInfos.resize(m_messageInfos.size());
+ std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin());
+
+ shared_ptr <events::messageCountEvent> event =
+ make_shared <events::messageCountEvent>(
+ dynamicCast <folder>((*it)->shared_from_this()),
+ events::messageCountEvent::TYPE_REMOVED,
+ nums
+ );
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+shared_ptr <folder> maildirFolder::getParent() {
+
+ if (m_path.isEmpty()) {
+ return null;
+ } else {
+ return shared_ptr <maildirFolder>(new maildirFolder(m_path.getParent(), m_store.lock()));
+ }
+}
+
+
+shared_ptr <const store> maildirFolder::getStore() const {
+
+ return m_store.lock();
+}
+
+
+shared_ptr <store> maildirFolder::getStore() {
+
+ return m_store.lock();
+}
+
+
+void maildirFolder::fetchMessages(
+ std::vector <shared_ptr <message> >& msg,
+ const fetchAttributes& options,
+ utility::progressListener* progress
+) {
+
+ shared_ptr <maildirStore> 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;
+ }
+
+ const size_t total = msg.size();
+ size_t current = 0;
+
+ if (progress) {
+ progress->start(total);
+ }
+
+ shared_ptr <maildirFolder> thisFolder = dynamicCast <maildirFolder>(shared_from_this());
+
+ for (std::vector <shared_ptr <message> >::iterator it = msg.begin() ;
+ it != msg.end() ; ++it) {
+
+ dynamicCast <maildirMessage>(*it)->fetch(thisFolder, options);
+
+ if (progress) {
+ progress->progress(++current, total);
+ }
+ }
+
+ if (progress) {
+ progress->stop(total);
+ }
+}
+
+
+void maildirFolder::fetchMessage(
+ const shared_ptr <message>& msg,
+ const fetchAttributes& options
+) {
+
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (!store) {
+ throw exceptions::illegal_state("Store disconnected");
+ } else if (!isOpen()) {
+ throw exceptions::illegal_state("Folder not open");
+ }
+
+ dynamicCast <maildirMessage>(msg)->fetch(
+ dynamicCast <maildirFolder>(shared_from_this()),
+ options
+ );
+}
+
+
+std::vector <shared_ptr <message> > maildirFolder::getAndFetchMessages(
+ const messageSet& msgs,
+ const fetchAttributes& attribs
+) {
+
+ if (msgs.isEmpty()) {
+ return std::vector <shared_ptr <message> >();
+ }
+
+ std::vector <shared_ptr <message> > messages = getMessages(msgs);
+ fetchMessages(messages, attribs);
+
+ return messages;
+}
+
+
+int maildirFolder::getFetchCapabilities() const {
+
+ return fetchAttributes::ENVELOPE |
+ fetchAttributes::STRUCTURE |
+ fetchAttributes::CONTENT_INFO |
+ fetchAttributes::FLAGS |
+ fetchAttributes::SIZE |
+ fetchAttributes::FULL_HEADER |
+ fetchAttributes::UID |
+ fetchAttributes::IMPORTANCE;
+}
+
+
+const utility::file::path maildirFolder::getMessageFSPath(const size_t number) const {
+
+ utility::file::path curDirPath = m_store.lock()->getFormat()->
+ folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY);
+
+ return curDirPath / m_messageInfos[number - 1].path;
+}
+
+
+std::vector <size_t> maildirFolder::getMessageNumbersStartingOnUID(const message::uid& /* uid */) {
+
+ throw exceptions::operation_not_supported();
+}
+
+
+} // maildir
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR
+