// // VMime library (http://www.vmime.org) // Copyright (C) 2002-2013 Vincent Richard // // 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_POP3 #include "vmime/net/pop3/POP3Folder.hpp" #include "vmime/net/pop3/POP3Store.hpp" #include "vmime/net/pop3/POP3Message.hpp" #include "vmime/net/pop3/POP3Command.hpp" #include "vmime/net/pop3/POP3Response.hpp" #include "vmime/net/pop3/POP3FolderStatus.hpp" #include "vmime/net/pop3/POP3Utils.hpp" #include "vmime/exception.hpp" namespace vmime { namespace net { namespace pop3 { POP3Folder::POP3Folder(const folder::path& path, shared_ptr store) : m_store(store), m_path(path), m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()), m_mode(-1), m_open(false) { store->registerFolder(this); } POP3Folder::~POP3Folder() { shared_ptr store = m_store.lock(); if (store) { if (m_open) close(false); store->unregisterFolder(this); } else if (m_open) { onClose(); } } int POP3Folder::getMode() const { if (!isOpen()) throw exceptions::illegal_state("Folder not open"); return (m_mode); } int POP3Folder::getType() { if (!isOpen()) throw exceptions::illegal_state("Folder not open"); if (m_path.isEmpty()) return (TYPE_CONTAINS_FOLDERS); else if (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX") return (TYPE_CONTAINS_MESSAGES); else throw exceptions::folder_not_found(); } int POP3Folder::getFlags() { return (0); } const folder::path::component POP3Folder::getName() const { return (m_name); } const folder::path POP3Folder::getFullPath() const { return (m_path); } void POP3Folder::open(const int mode, bool failIfModeIsNotAvailable) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); if (m_path.isEmpty()) { if (mode != MODE_READ_ONLY && failIfModeIsNotAvailable) throw exceptions::operation_not_supported(); m_open = true; m_mode = mode; m_messageCount = 0; } else if (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX") { POP3Command::STAT()->send(store->getConnection()); shared_ptr response = POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("STAT", response->getFirstLine()); std::istringstream iss(response->getText()); iss >> m_messageCount; if (iss.fail()) throw exceptions::invalid_response("STAT", response->getFirstLine()); m_open = true; m_mode = mode; } else { throw exceptions::folder_not_found(); } } void POP3Folder::close(const bool expunge) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); if (!isOpen()) throw exceptions::illegal_state("Folder not open"); if (!expunge) { POP3Command::RSET()->send(store->getConnection()); POP3Response::readResponse(store->getConnection()); } m_open = false; m_mode = -1; onClose(); } void POP3Folder::onClose() { for (MessageMap::iterator it = m_messages.begin() ; it != m_messages.end() ; ++it) (*it).first->onFolderClosed(); m_messages.clear(); } void POP3Folder::create(const int /* type */) { throw exceptions::operation_not_supported(); } void POP3Folder::destroy() { throw exceptions::operation_not_supported(); } bool POP3Folder::exists() { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); return (m_path.isEmpty() || (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX")); } bool POP3Folder::isOpen() const { return (m_open); } shared_ptr POP3Folder::getMessage(const int num) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); else if (num < 1 || num > m_messageCount) throw exceptions::message_not_found(); return make_shared (dynamicCast (shared_from_this()), num); } std::vector > POP3Folder::getMessages(const messageSet& msgs) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); if (msgs.isNumberSet()) { const std::vector numbers = POP3Utils::messageSetToNumberList(msgs); std::vector > messages; shared_ptr thisFolder(dynamicCast (shared_from_this())); for (std::vector ::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 (thisFolder, *it)); } return messages; } else { throw exceptions::operation_not_supported(); } } int POP3Folder::getMessageCount() { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); return (m_messageCount); } shared_ptr POP3Folder::getFolder(const folder::path::component& name) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); return make_shared (m_path / name, store); } std::vector > POP3Folder::getFolders(const bool /* recursive */) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); if (m_path.isEmpty()) { std::vector > v; v.push_back(make_shared (folder::path::component("INBOX"), store)); return (v); } else { std::vector > v; return (v); } } void POP3Folder::fetchMessages(std::vector >& msg, const fetchAttributes& options, utility::progressListener* progress) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); const int total = msg.size(); int current = 0; if (progress) progress->start(total); for (std::vector >::iterator it = msg.begin() ; it != msg.end() ; ++it) { dynamicCast (*it)->fetch (dynamicCast (shared_from_this()), options); if (progress) progress->progress(++current, total); } if (options.has(fetchAttributes::SIZE)) { // Send the "LIST" command POP3Command::LIST()->send(store->getConnection()); // Get the response shared_ptr response = POP3Response::readMultilineResponse(store->getConnection()); if (response->isSuccess()) { // C: LIST // S: +OK // S: 1 47548 // S: 2 12653 // S: . std::map result; POP3Utils::parseMultiListOrUidlResponse(response, result); for (std::vector >::iterator it = msg.begin() ; it != msg.end() ; ++it) { shared_ptr m = dynamicCast (*it); std::map ::const_iterator x = result.find(m->m_num); if (x != result.end()) { int size = 0; std::istringstream iss((*x).second); iss >> size; m->m_size = size; } } } } if (options.has(fetchAttributes::UID)) { // Send the "UIDL" command POP3Command::UIDL()->send(store->getConnection()); // Get the response shared_ptr response = POP3Response::readMultilineResponse(store->getConnection()); if (response->isSuccess()) { // C: UIDL // S: +OK // S: 1 whqtswO00WBw418f9t5JxYwZ // S: 2 QhdPYR:00WBw1Ph7x7 // S: . std::map result; POP3Utils::parseMultiListOrUidlResponse(response, result); for (std::vector >::iterator it = msg.begin() ; it != msg.end() ; ++it) { shared_ptr m = dynamicCast (*it); std::map ::const_iterator x = result.find(m->m_num); if (x != result.end()) m->m_uid = (*x).second; } } } if (progress) progress->stop(total); } void POP3Folder::fetchMessage(shared_ptr msg, const fetchAttributes& options) { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); dynamicCast (msg)->fetch (dynamicCast (shared_from_this()), options); if (options.has(fetchAttributes::SIZE)) { // Send the "LIST" command POP3Command::LIST(msg->getNumber())->send(store->getConnection()); // Get the response shared_ptr response = POP3Response::readResponse(store->getConnection()); if (response->isSuccess()) { string responseText = response->getText(); // C: LIST 2 // S: +OK 2 4242 string::iterator it = responseText.begin(); while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; if (it != responseText.end()) { int size = 0; std::istringstream iss(string(it, responseText.end())); iss >> size; dynamicCast (msg)->m_size = size; } } } if (options.has(fetchAttributes::UID)) { // Send the "UIDL" command POP3Command::UIDL(msg->getNumber())->send(store->getConnection()); // Get the response shared_ptr response = POP3Response::readResponse(store->getConnection()); if (response->isSuccess()) { string responseText = response->getText(); // C: UIDL 2 // S: +OK 2 QhdPYR:00WBw1Ph7x7 string::iterator it = responseText.begin(); while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it; while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; if (it != responseText.end()) { dynamicCast (msg)->m_uid = string(it, responseText.end()); } } } } int POP3Folder::getFetchCapabilities() const { return fetchAttributes::ENVELOPE | fetchAttributes::CONTENT_INFO | fetchAttributes::SIZE | fetchAttributes::FULL_HEADER | fetchAttributes::UID | fetchAttributes::IMPORTANCE; } shared_ptr POP3Folder::getParent() { if (m_path.isEmpty()) return null; else return make_shared (m_path.getParent(), m_store.lock()); } shared_ptr POP3Folder::getStore() const { return m_store.lock(); } shared_ptr POP3Folder::getStore() { return m_store.lock(); } void POP3Folder::registerMessage(POP3Message* msg) { m_messages.insert(MessageMap::value_type(msg, msg->getNumber())); } void POP3Folder::unregisterMessage(POP3Message* msg) { m_messages.erase(msg); } void POP3Folder::onStoreDisconnected() { m_store.reset(); } void POP3Folder::deleteMessages(const messageSet& msgs) { shared_ptr store = m_store.lock(); const std::vector nums = POP3Utils::messageSetToNumberList(msgs); if (nums.empty()) throw exceptions::invalid_argument(); if (!store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); for (std::vector ::const_iterator it = nums.begin() ; it != nums.end() ; ++it) { POP3Command::DELE(*it)->send(store->getConnection()); shared_ptr response = POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("DELE", response->getFirstLine()); } // Sort message list std::vector list; list.resize(nums.size()); std::copy(nums.begin(), nums.end(), list.begin()); std::sort(list.begin(), list.end()); // Update local flags for (std::map ::iterator it = m_messages.begin() ; it != m_messages.end() ; ++it) { POP3Message* msg = (*it).first; if (std::binary_search(list.begin(), list.end(), msg->getNumber())) msg->m_deleted = true; } // Notify message flags changed shared_ptr event = make_shared (dynamicCast (shared_from_this()), events::messageChangedEvent::TYPE_FLAGS, list); notifyMessageChanged(event); } void POP3Folder::setMessageFlags(const messageSet& /* msgs */, const int /* flags */, const int /* mode */) { throw exceptions::operation_not_supported(); } void POP3Folder::rename(const folder::path& /* newPath */) { throw exceptions::operation_not_supported(); } void POP3Folder::addMessage(shared_ptr /* msg */, const int /* flags */, vmime::datetime* /* date */, utility::progressListener* /* progress */) { throw exceptions::operation_not_supported(); } void POP3Folder::addMessage(utility::inputStream& /* is */, const int /* size */, const int /* flags */, vmime::datetime* /* date */, utility::progressListener* /* progress */) { throw exceptions::operation_not_supported(); } void POP3Folder::copyMessages(const folder::path& /* dest */, const messageSet& /* msgs */) { throw exceptions::operation_not_supported(); } void POP3Folder::status(int& count, int& unseen) { count = 0; unseen = 0; shared_ptr status = getStatus(); count = status->getMessageCount(); unseen = status->getUnseenCount(); } shared_ptr POP3Folder::getStatus() { shared_ptr store = m_store.lock(); if (!store) throw exceptions::illegal_state("Store disconnected"); POP3Command::STAT()->send(store->getConnection()); shared_ptr response = POP3Response::readResponse(store->getConnection()); if (!response->isSuccess()) throw exceptions::command_error("STAT", response->getFirstLine()); unsigned int count = 0; std::istringstream iss(response->getText()); iss >> count; shared_ptr status = make_shared (); status->setMessageCount(count); status->setUnseenCount(count); // Update local message count if (m_messageCount != count) { const int oldCount = m_messageCount; m_messageCount = count; if (count > oldCount) { std::vector nums; nums.reserve(count - oldCount); for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) nums[j] = i; // Notify message count changed shared_ptr event = make_shared (dynamicCast (shared_from_this()), events::messageCountEvent::TYPE_ADDED, nums); notifyMessageCount(event); // Notify folders with the same path for (std::list ::iterator it = store->m_folders.begin() ; it != store->m_folders.end() ; ++it) { if ((*it) != this && (*it)->getFullPath() == m_path) { (*it)->m_messageCount = count; shared_ptr event = make_shared (dynamicCast ((*it)->shared_from_this()), events::messageCountEvent::TYPE_ADDED, nums); (*it)->notifyMessageCount(event); } } } } return status; } void POP3Folder::expunge() { // Not supported by POP3 protocol (deleted messages are automatically // expunged at the end of the session...). } std::vector POP3Folder::getMessageNumbersStartingOnUID(const message::uid& /* uid */) { throw exceptions::operation_not_supported(); } } // pop3 } // net } // vmime #endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3