// // VMime library (http://www.vmime.org) // Copyright (C) 2002-2005 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 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "vmime/messaging/pop3/POP3Folder.hpp" #include "vmime/messaging/pop3/POP3Store.hpp" #include "vmime/messaging/pop3/POP3Message.hpp" #include "vmime/exception.hpp" namespace vmime { namespace messaging { namespace pop3 { POP3Folder::POP3Folder(const folder::path& path, POP3Store* store) : m_store(store), m_path(path), m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()), m_mode(-1), m_open(false) { m_store->registerFolder(this); } POP3Folder::~POP3Folder() { if (m_store) { if (m_open) close(false); m_store->unregisterFolder(this); } else if (m_open) { onClose(); } } const int POP3Folder::getMode() const { if (!isOpen()) throw exceptions::illegal_state("Folder not open"); return (m_mode); } const 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(); } const 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) { if (!m_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") { m_store->sendRequest("STAT"); string response; m_store->readResponse(response, false); if (!m_store->isSuccessResponse(response)) throw exceptions::command_error("STAT", response); m_store->stripResponseCode(response, response); std::istringstream iss(response); iss >> m_messageCount; if (iss.fail()) throw exceptions::invalid_response("STAT", response); m_open = true; m_mode = mode; } else { throw exceptions::folder_not_found(); } } void POP3Folder::close(const bool expunge) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); if (!isOpen()) throw exceptions::illegal_state("Folder not open"); if (!expunge) { m_store->sendRequest("RSET"); string response; m_store->readResponse(response, false); } 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(); } const bool POP3Folder::exists() { if (!m_store) throw exceptions::illegal_state("Store disconnected"); return (m_path.isEmpty() || (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX")); } const bool POP3Folder::isOpen() const { return (m_open); } message* POP3Folder::getMessage(const int num) { if (!m_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 new POP3Message(this, num); } std::vector POP3Folder::getMessages(const int from, const int to) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); else if (to < from || from < 1 || to < 1 || from > m_messageCount || to > m_messageCount) throw exceptions::message_not_found(); std::vector v; for (int i = from ; i <= to ; ++i) v.push_back(new POP3Message(this, i)); return (v); } std::vector POP3Folder::getMessages(const std::vector & nums) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); std::vector v; try { for (std::vector ::const_iterator it = nums.begin() ; it != nums.end() ; ++it) { if (*it < 1|| *it > m_messageCount) throw exceptions::message_not_found(); v.push_back(new POP3Message(this, *it)); } } catch (std::exception& e) { for (std::vector ::iterator it = v.begin() ; it != v.end() ; ++it) delete (*it); throw; } return (v); } const int POP3Folder::getMessageCount() { if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); return (m_messageCount); } folder* POP3Folder::getFolder(const folder::path::component& name) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); return new POP3Folder(m_path / name, m_store); } std::vector POP3Folder::getFolders(const bool /* recursive */) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); if (m_path.isEmpty()) { std::vector v; v.push_back(new POP3Folder(folder::path::component("INBOX"), m_store)); return (v); } else { std::vector v; return (v); } } void POP3Folder::fetchMessages(std::vector & msg, const int options, utility::progressionListener* progress) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); const int total = msg.size(); int current = 0; if (progress) progress->start(total); for (std::vector ::iterator it = msg.begin() ; it != msg.end() ; ++it) { dynamic_cast (*it)->fetch(this, options); if (progress) progress->progress(++current, total); } if (options & FETCH_SIZE) { // Send the "LIST" command std::ostringstream command; command << "LIST"; m_store->sendRequest(command.str()); // Get the response string response; m_store->readResponse(response, true, NULL); if (m_store->isSuccessResponse(response)) { m_store->stripFirstLine(response, response, NULL); // C: LIST // S: +OK // S: 1 47548 // S: 2 12653 // S: . std::map result; parseMultiListOrUidlResponse(response, result); for (std::vector ::iterator it = msg.begin() ; it != msg.end() ; ++it) { POP3Message* m = dynamic_cast (*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 & FETCH_UID) { // Send the "UIDL" command std::ostringstream command; command << "UIDL"; m_store->sendRequest(command.str()); // Get the response string response; m_store->readResponse(response, true, NULL); if (m_store->isSuccessResponse(response)) { m_store->stripFirstLine(response, response, NULL); // C: UIDL // S: +OK // S: 1 whqtswO00WBw418f9t5JxYwZ // S: 2 QhdPYR:00WBw1Ph7x7 // S: . std::map result; parseMultiListOrUidlResponse(response, result); for (std::vector ::iterator it = msg.begin() ; it != msg.end() ; ++it) { POP3Message* m = dynamic_cast (*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(message* msg, const int options) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); dynamic_cast (msg)->fetch(this, options); if (options & FETCH_SIZE) { // Send the "LIST" command std::ostringstream command; command << "LIST " << msg->getNumber(); m_store->sendRequest(command.str()); // Get the response string response; m_store->readResponse(response, false, NULL); if (m_store->isSuccessResponse(response)) { m_store->stripResponseCode(response, response); // C: LIST 2 // S: +OK 2 4242 string::iterator it = response.begin(); while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it; while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; if (it != response.end()) { int size = 0; std::istringstream iss(string(it, response.end())); iss >> size; dynamic_cast (msg)->m_size = size; } } } if (options & FETCH_UID) { // Send the "UIDL" command std::ostringstream command; command << "UIDL " << msg->getNumber(); m_store->sendRequest(command.str()); // Get the response string response; m_store->readResponse(response, false, NULL); if (m_store->isSuccessResponse(response)) { m_store->stripResponseCode(response, response); // C: UIDL 2 // S: +OK 2 QhdPYR:00WBw1Ph7x7 string::iterator it = response.begin(); while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it; while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; if (it != response.end()) { dynamic_cast (msg)->m_uid = string(it, response.end()); } } } } const int POP3Folder::getFetchCapabilities() const { return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID); } folder* POP3Folder::getParent() { return (m_path.isEmpty() ? NULL : new POP3Folder(m_path.getParent(), m_store)); } const store* POP3Folder::getStore() const { return (m_store); } store* POP3Folder::getStore() { return (m_store); } 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 = NULL; } void POP3Folder::deleteMessage(const int num) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); std::ostringstream command; command << "DELE " << num; m_store->sendRequest(command.str()); string response; m_store->readResponse(response, false); if (!m_store->isSuccessResponse(response)) throw exceptions::command_error("DELE", response); // Update local flags for (std::map ::iterator it = m_messages.begin() ; it != m_messages.end() ; ++it) { POP3Message* msg = (*it).first; if (msg->getNumber() == num) msg->m_deleted = true; } // Notify message flags changed std::vector nums; nums.push_back(num); events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); } void POP3Folder::deleteMessages(const int from, const int to) { if (from < 1 || (to < from && to != -1)) throw exceptions::invalid_argument(); if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); const int to2 = (to == -1 ? m_messageCount : to); for (int i = from ; i <= to2 ; ++i) { std::ostringstream command; command << "DELE " << i; m_store->sendRequest(command.str()); string response; m_store->readResponse(response, false); if (!m_store->isSuccessResponse(response)) throw exceptions::command_error("DELE", response); } // Update local flags for (std::map ::iterator it = m_messages.begin() ; it != m_messages.end() ; ++it) { POP3Message* msg = (*it).first; if (msg->getNumber() >= from && msg->getNumber() <= to2) msg->m_deleted = true; } // Notify message flags changed std::vector nums; for (int i = from ; i <= to2 ; ++i) nums.push_back(i); events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); } void POP3Folder::deleteMessages(const std::vector & nums) { if (nums.empty()) throw exceptions::invalid_argument(); if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); for (std::vector ::const_iterator it = nums.begin() ; it != nums.end() ; ++it) { std::ostringstream command; command << "DELE " << (*it); m_store->sendRequest(command.str()); string response; m_store->readResponse(response, false); if (!m_store->isSuccessResponse(response)) throw exceptions::command_error("DELE", response); } // 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 events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, list); notifyMessageChanged(event); } void POP3Folder::setMessageFlags(const int /* from */, const int /* to */, const int /* flags */, const int /* mode */) { throw exceptions::operation_not_supported(); } void POP3Folder::setMessageFlags(const std::vector & /* nums */, 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(vmime::message* /* msg */, const int /* flags */, vmime::datetime* /* date */, utility::progressionListener* /* progress */) { throw exceptions::operation_not_supported(); } void POP3Folder::addMessage(utility::inputStream& /* is */, const int /* size */, const int /* flags */, vmime::datetime* /* date */, utility::progressionListener* /* progress */) { throw exceptions::operation_not_supported(); } void POP3Folder::copyMessage(const folder::path& /* dest */, const int /* num */) { throw exceptions::operation_not_supported(); } void POP3Folder::copyMessages(const folder::path& /* dest */, const int /* from */, const int /* to */) { throw exceptions::operation_not_supported(); } void POP3Folder::copyMessages(const folder::path& /* dest */, const std::vector & /* nums */) { throw exceptions::operation_not_supported(); } void POP3Folder::status(int& count, int& unseen) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); m_store->sendRequest("STAT"); string response; m_store->readResponse(response, false); if (!m_store->isSuccessResponse(response)) throw exceptions::command_error("STAT", response); m_store->stripResponseCode(response, response); std::istringstream iss(response); iss >> count; unseen = 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 events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); notifyMessageCount(event); // Notify folders with the same path for (std::list ::iterator it = m_store->m_folders.begin() ; it != m_store->m_folders.end() ; ++it) { if ((*it) != this && (*it)->getFullPath() == m_path) { (*it)->m_messageCount = count; events::messageCountEvent event(*it, events::messageCountEvent::TYPE_ADDED, nums); (*it)->notifyMessageCount(event); } } } } } void POP3Folder::expunge() { // Not supported by POP3 protocol (deleted messages are automatically // expunged at the end of the session...). } void POP3Folder::parseMultiListOrUidlResponse(const string& response, std::map & result) { std::istringstream iss(response); std::map ids; string line; while (std::getline(iss, line)) { string::iterator it = line.begin(); while (it != line.end() && (*it == ' ' || *it == '\t')) ++it; if (it != line.end()) { int number = 0; while (it != line.end() && (*it >= '0' && *it <= '9')) { number = (number * 10) + (*it - '0'); ++it; } while (it != line.end() && !(*it == ' ' || *it == '\t')) ++it; while (it != line.end() && (*it == ' ' || *it == '\t')) ++it; if (it != line.end()) { result.insert(std::map ::value_type(number, string(it, line.end()))); } } } } } // pop3 } // messaging } // vmime