diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/src/vmime/net/maildir/maildirFolder.cpp | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/src/vmime/net/maildir/maildirFolder.cpp')
-rw-r--r-- | vmime-master/src/vmime/net/maildir/maildirFolder.cpp | 1365 |
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 + |