aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/imap
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/imap')
-rw-r--r--src/net/imap/IMAPConnection.cpp313
-rw-r--r--src/net/imap/IMAPFolder.cpp1608
-rw-r--r--src/net/imap/IMAPMessage.cpp859
-rw-r--r--src/net/imap/IMAPStore.cpp308
-rw-r--r--src/net/imap/IMAPTag.cpp99
-rw-r--r--src/net/imap/IMAPUtils.cpp555
6 files changed, 3742 insertions, 0 deletions
diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp
new file mode 100644
index 00000000..5191dc94
--- /dev/null
+++ b/src/net/imap/IMAPConnection.cpp
@@ -0,0 +1,313 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 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/net/imap/IMAPTag.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+#include "vmime/net/imap/IMAPUtils.hpp"
+#include "vmime/net/imap/IMAPStore.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+
+#include <sstream>
+
+
+// Helpers for service properties
+#define GET_PROPERTY(type, prop) \
+ (m_store->getInfos().getPropertyValue <type>(getSession(), \
+ dynamic_cast <const IMAPStore::_infos&>(m_store->getInfos()).getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (m_store->getInfos().hasProperty(getSession(), \
+ dynamic_cast <const IMAPStore::_infos&>(m_store->getInfos()).getProperties().prop))
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> auth)
+ : m_store(store), m_auth(auth), m_socket(NULL), m_parser(NULL), m_tag(NULL),
+ m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(NULL)
+{
+}
+
+
+IMAPConnection::~IMAPConnection()
+{
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+}
+
+
+void IMAPConnection::connect()
+{
+ if (isConnected())
+ throw exceptions::already_connected();
+
+ m_state = STATE_NONE;
+ m_hierarchySeparator = '\0';
+
+ const string address = GET_PROPERTY(string, PROPERTY_SERVER_ADDRESS);
+ const port_t port = GET_PROPERTY(port_t, PROPERTY_SERVER_PORT);
+
+ // Create the time-out handler
+ if (HAS_PROPERTY(PROPERTY_TIMEOUT_FACTORY))
+ {
+ timeoutHandlerFactory* tof = platformDependant::getHandler()->
+ getTimeoutHandlerFactory(GET_PROPERTY(string, PROPERTY_TIMEOUT_FACTORY));
+
+ m_timeoutHandler = tof->create();
+ }
+
+ // Create and connect the socket
+ socketFactory* sf = platformDependant::getHandler()->
+ getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY));
+
+ m_socket = sf->create();
+ m_socket->connect(address, port);
+
+
+ m_tag = vmime::create <IMAPTag>();
+ m_parser = vmime::create <IMAPParser>(m_tag, m_socket, m_timeoutHandler);
+
+
+ setState(STATE_NON_AUTHENTICATED);
+
+
+ // Connection greeting
+ //
+ // eg: C: <connection to server>
+ // --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready
+
+ utility::auto_ptr <IMAPParser::greeting> greet(m_parser->readGreeting());
+
+ if (greet->resp_cond_bye())
+ {
+ internalDisconnect();
+ throw exceptions::connection_greeting_error(m_parser->lastLine());
+ }
+ else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH)
+ {
+ const authenticationInfos auth = m_auth->requestAuthInfos();
+
+ // TODO: other authentication methods
+
+ send(true, "LOGIN " + IMAPUtils::quoteString(auth.getUsername())
+ + " " + IMAPUtils::quoteString(auth.getPassword()), true);
+
+ utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ if (resp->isBad())
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LOGIN", m_parser->lastLine());
+ }
+ else if (resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ internalDisconnect();
+ throw exceptions::authentication_error(m_parser->lastLine());
+ }
+ }
+
+ // Get the hierarchy separator character
+ initHierarchySeparator();
+
+ // Switch to state "Authenticated"
+ setState(STATE_AUTHENTICATED);
+}
+
+
+const bool IMAPConnection::isConnected() const
+{
+ return (m_socket && m_socket->isConnected() &&
+ (m_state == STATE_AUTHENTICATED || m_state == STATE_SELECTED));
+}
+
+
+void IMAPConnection::disconnect()
+{
+ if (!isConnected())
+ throw exceptions::not_connected();
+
+ internalDisconnect();
+}
+
+
+void IMAPConnection::internalDisconnect()
+{
+ if (isConnected())
+ {
+ send(true, "LOGOUT", true);
+
+ m_socket->disconnect();
+ m_socket = NULL;
+ }
+
+ m_timeoutHandler = NULL;
+
+ m_state = STATE_LOGOUT;
+}
+
+
+void IMAPConnection::initHierarchySeparator()
+{
+ send(true, "LIST \"\" \"\"", true);
+
+ vmime::utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LIST", m_parser->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ if (respDataList.size() < 1 || respDataList[0]->response_data() == NULL)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LIST", m_parser->lastLine(), "unexpected response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ static_cast <const IMAPParser::response_data*>(respDataList[0]->response_data())->
+ mailbox_data();
+
+ if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LIST", m_parser->lastLine(), "invalid type");
+ }
+
+ if (mailboxData->mailbox_list()->quoted_char() == '\0')
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LIST", m_parser->lastLine(), "no hierarchy separator");
+ }
+
+ m_hierarchySeparator = mailboxData->mailbox_list()->quoted_char();
+}
+
+
+void IMAPConnection::send(bool tag, const string& what, bool end)
+{
+#if VMIME_DEBUG
+ std::ostringstream oss;
+
+ if (tag)
+ {
+ ++(*m_tag);
+
+ oss << string(*m_tag);
+ oss << " ";
+ }
+
+ oss << what;
+
+ if (end)
+ oss << "\r\n";
+
+ m_socket->send(oss.str());
+#else
+ if (tag)
+ {
+ ++(*m_tag);
+
+ m_socket->send(*m_tag);
+ m_socket->send(" ");
+ }
+
+ m_socket->send(what);
+
+ if (end)
+ {
+ m_socket->send("\r\n");
+ }
+#endif
+}
+
+
+void IMAPConnection::sendRaw(const char* buffer, const int count)
+{
+ m_socket->sendRaw(buffer, count);
+}
+
+
+IMAPParser::response* IMAPConnection::readResponse(IMAPParser::literalHandler* lh)
+{
+ return (m_parser->readResponse(lh));
+}
+
+
+const IMAPConnection::ProtocolStates IMAPConnection::state() const
+{
+ return (m_state);
+}
+
+
+void IMAPConnection::setState(const ProtocolStates state)
+{
+ m_state = state;
+}
+
+const char IMAPConnection::hierarchySeparator() const
+{
+ return (m_hierarchySeparator);
+}
+
+
+ref <const IMAPTag> IMAPConnection::getTag() const
+{
+ return (m_tag);
+}
+
+
+ref <const IMAPParser> IMAPConnection::getParser() const
+{
+ return (m_parser);
+}
+
+
+weak_ref <const IMAPStore> IMAPConnection::getStore() const
+{
+ return (m_store);
+}
+
+
+weak_ref <IMAPStore> IMAPConnection::getStore()
+{
+ return (m_store);
+}
+
+
+ref <session> IMAPConnection::getSession()
+{
+ return (m_store->getSession());
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp
new file mode 100644
index 00000000..ea0ddfd9
--- /dev/null
+++ b/src/net/imap/IMAPFolder.cpp
@@ -0,0 +1,1608 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 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/net/imap/IMAPFolder.hpp"
+
+#include "vmime/net/imap/IMAPStore.hpp"
+#include "vmime/net/imap/IMAPParser.hpp"
+#include "vmime/net/imap/IMAPMessage.hpp"
+#include "vmime/net/imap/IMAPUtils.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+
+#include "vmime/message.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/utility/smartPtr.hpp"
+
+#include <algorithm>
+#include <sstream>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPFolder::IMAPFolder(const folder::path& path, IMAPStore* store, const int type, const int flags)
+ : m_store(store), m_connection(m_store->connection()), m_path(path),
+ m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()), m_mode(-1),
+ m_open(false), m_type(type), m_flags(flags), m_messageCount(0), m_uidValidity(0)
+{
+ m_store->registerFolder(this);
+}
+
+
+IMAPFolder::~IMAPFolder()
+{
+ if (m_store)
+ {
+ if (m_open)
+ close(false);
+
+ m_store->unregisterFolder(this);
+ }
+ else if (m_open)
+ {
+ m_connection = NULL;
+ onClose();
+ }
+}
+
+
+const int IMAPFolder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+const int IMAPFolder::getType()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Root folder
+ if (m_path.isEmpty())
+ {
+ return (TYPE_CONTAINS_FOLDERS);
+ }
+ else
+ {
+ if (m_type == TYPE_UNDEFINED)
+ testExistAndGetType();
+
+ return (m_type);
+ }
+}
+
+
+const int IMAPFolder::getFlags()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Root folder
+ if (m_path.isEmpty())
+ {
+ return (FLAG_CHILDREN | FLAG_NO_OPEN);
+ }
+ else
+ {
+ if (m_flags == FLAG_UNDEFINED)
+ testExistAndGetType();
+
+ return (m_flags);
+ }
+}
+
+
+const folder::path::component IMAPFolder::getName() const
+{
+ return (m_name);
+}
+
+
+const folder::path IMAPFolder::getFullPath() const
+{
+ return (m_path);
+}
+
+
+void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ // Open a connection for this folder
+ ref <IMAPConnection> connection =
+ vmime::create <IMAPConnection>(m_store, m_store->oneTimeAuthenticator());
+
+ try
+ {
+ connection->connect();
+
+ // Emit the "SELECT" command
+ //
+ // Example: C: A142 SELECT INBOX
+ // S: * 172 EXISTS
+ // S: * 1 RECENT
+ // S: * OK [UNSEEN 12] Message 12 is first unseen
+ // S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ // S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ // S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ // S: A142 OK [READ-WRITE] SELECT completed
+
+ std::ostringstream oss;
+
+ if (mode == MODE_READ_ONLY)
+ oss << "EXAMINE ";
+ else
+ oss << "SELECT ";
+
+ oss << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (connection->hierarchySeparator(), getFullPath()));
+
+ connection->send(true, oss.str(), true);
+
+ // Read the response
+ utility::auto_ptr <IMAPParser::response> resp(connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("SELECT",
+ connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("SELECT",
+ connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::response_data* responseData = (*it)->response_data();
+
+ // OK Untagged responses: UNSEEN, PERMANENTFLAGS, UIDVALIDITY (optional)
+ if (responseData->resp_cond_state())
+ {
+ const IMAPParser::resp_text_code* code =
+ responseData->resp_cond_state()->resp_text()->resp_text_code();
+
+ if (code != NULL)
+ {
+ switch (code->type())
+ {
+ case IMAPParser::resp_text_code::UIDVALIDITY:
+
+ m_uidValidity = code->nz_number()->value();
+ break;
+
+ default:
+
+ break;
+ }
+ }
+ }
+ // Untagged responses: FLAGS, EXISTS, RECENT (required)
+ else if (responseData->mailbox_data())
+ {
+ switch (responseData->mailbox_data()->type())
+ {
+ default: break;
+
+ case IMAPParser::mailbox_data::FLAGS:
+ {
+ m_type = IMAPUtils::folderTypeFromFlags
+ (responseData->mailbox_data()->mailbox_flag_list());
+
+ m_flags = IMAPUtils::folderFlagsFromFlags
+ (responseData->mailbox_data()->mailbox_flag_list());
+
+ break;
+ }
+ case IMAPParser::mailbox_data::EXISTS:
+ {
+ m_messageCount = responseData->mailbox_data()->number()->value();
+ break;
+ }
+ case IMAPParser::mailbox_data::RECENT:
+ {
+ // TODO
+ break;
+ }
+
+ }
+ }
+ }
+
+ // Check for access mode (read-only or read-write)
+ const IMAPParser::resp_text_code* respTextCode = resp->response_done()->
+ response_tagged()->resp_cond_state()->resp_text()->resp_text_code();
+
+ if (respTextCode)
+ {
+ const int openMode =
+ (respTextCode->type() == IMAPParser::resp_text_code::READ_WRITE)
+ ? MODE_READ_WRITE : MODE_READ_ONLY;
+
+ if (failIfModeIsNotAvailable &&
+ mode == MODE_READ_WRITE && openMode == MODE_READ_ONLY)
+ {
+ throw exceptions::operation_not_supported();
+ }
+ }
+
+
+ m_connection = connection;
+ m_open = true;
+ m_mode = mode;
+ }
+ catch (std::exception&)
+ {
+ throw;
+ }
+}
+
+
+void IMAPFolder::close(const bool expunge)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ ref <IMAPConnection> oldConnection = m_connection;
+
+ // Emit the "CLOSE" command to expunge messages marked
+ // as deleted (this is fastest than "EXPUNGE")
+ if (expunge)
+ {
+ if (m_mode == MODE_READ_ONLY)
+ throw exceptions::operation_not_supported();
+
+ oldConnection->send(true, "CLOSE", true);
+ }
+
+ // Close this folder connection
+ oldConnection->disconnect();
+
+ // Now use default store connection
+ m_connection = m_store->connection();
+
+ m_open = false;
+ m_mode = -1;
+
+ m_uidValidity = 0;
+
+ onClose();
+}
+
+
+void IMAPFolder::onClose()
+{
+ for (std::vector <IMAPMessage*>::iterator it = m_messages.begin() ;
+ it != m_messages.end() ; ++it)
+ {
+ (*it)->onFolderClosed();
+ }
+
+ m_messages.clear();
+}
+
+
+void IMAPFolder::create(const int type)
+{
+ if (!m_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 (!m_store->isValidFolderName(m_name))
+ throw exceptions::invalid_folder_name();
+
+ // Emit the "CREATE" command
+ //
+ // Example: C: A003 CREATE owatagusiam/
+ // S: A003 OK CREATE completed
+ // C: A004 CREATE owatagusiam/blurdybloop
+ // S: A004 OK CREATE completed
+
+ string mailbox = IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath());
+
+ if (type & TYPE_CONTAINS_FOLDERS)
+ mailbox += m_connection->hierarchySeparator();
+
+ std::ostringstream oss;
+ oss << "CREATE " << IMAPUtils::quoteString(mailbox);
+
+ m_connection->send(true, oss.str(), true);
+
+
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("CREATE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify folder created
+ events::folderEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_CREATED, m_path, m_path);
+
+ notifyFolder(event);
+}
+
+
+const bool IMAPFolder::exists()
+{
+ if (!isOpen() && !m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return (testExistAndGetType() != TYPE_UNDEFINED);
+}
+
+
+const int IMAPFolder::testExistAndGetType()
+{
+ m_type = TYPE_UNDEFINED;
+
+ // To test whether a folder exists, we simple list it using
+ // the "LIST" command, and there should be one unique mailbox
+ // with this name...
+ //
+ // Eg. Test whether '/foo/bar' exists
+ //
+ // C: a005 list "" foo/bar
+ // S: * LIST (\NoSelect) "/" foo/bar
+ // S: a005 OK LIST completed
+ //
+ // ==> OK, exists
+ //
+ // Test whether '/foo/bar/zap' exists
+ //
+ // C: a005 list "" foo/bar/zap
+ // S: a005 OK LIST completed
+ //
+ // ==> NO, does not exist
+
+ std::ostringstream oss;
+ oss << "LIST \"\" ";
+ oss << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath()));
+
+ m_connection->send(true, oss.str(), true);
+
+
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("LIST",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Check whether the result mailbox list contains this folder
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("LIST",
+ m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ (*it)->response_data()->mailbox_data();
+
+ // We are only interested in responses of type "LIST"
+ if (mailboxData != NULL && mailboxData->type() == IMAPParser::mailbox_data::LIST)
+ {
+ // Get the folder type/flags at the same time
+ m_type = IMAPUtils::folderTypeFromFlags
+ (mailboxData->mailbox_list()->mailbox_flag_list());
+
+ m_flags = IMAPUtils::folderFlagsFromFlags
+ (mailboxData->mailbox_list()->mailbox_flag_list());
+ }
+ }
+
+ return (m_type);
+}
+
+
+const bool IMAPFolder::isOpen() const
+{
+ return (m_open);
+}
+
+
+ref <message> IMAPFolder::getMessage(const int num)
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ if (num < 1 || num > m_messageCount)
+ throw exceptions::message_not_found();
+
+ return vmime::create <IMAPMessage>(this, num);
+}
+
+
+std::vector <ref <message> > IMAPFolder::getMessages(const int from, const int to)
+{
+ const int messageCount = getMessageCount();
+ const int to2 = (to == -1 ? messageCount : to);
+
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (to2 < from || from < 1 || to2 < 1 || from > messageCount || to2 > messageCount)
+ throw exceptions::message_not_found();
+
+ std::vector <ref <message> > v;
+
+ for (int i = from ; i <= to2 ; ++i)
+ v.push_back(vmime::create <IMAPMessage>(this, i));
+
+ return (v);
+}
+
+
+std::vector <ref <message> > IMAPFolder::getMessages(const std::vector <int>& nums)
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ std::vector <ref <message> > v;
+
+ for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
+ v.push_back(vmime::create <IMAPMessage>(this, *it));
+
+ return (v);
+}
+
+
+const int IMAPFolder::getMessageCount()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_messageCount);
+}
+
+
+ref <folder> IMAPFolder::getFolder(const folder::path::component& name)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return vmime::create <IMAPFolder>(m_path / name, m_store);
+}
+
+
+std::vector <ref <folder> > IMAPFolder::getFolders(const bool recursive)
+{
+ if (!isOpen() && !m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ // Eg. List folders in '/foo/bar'
+ //
+ // C: a005 list "foo/bar" *
+ // S: * LIST (\NoSelect) "/" foo/bar
+ // S: * LIST (\NoInferiors) "/" foo/bar/zap
+ // S: a005 OK LIST completed
+
+ std::ostringstream oss;
+ oss << "LIST ";
+
+ const string pathString = IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath());
+
+ if (recursive)
+ {
+ oss << IMAPUtils::quoteString(pathString);
+ oss << " *";
+ }
+ else
+ {
+ if (pathString.empty()) // don't add sep for root folder
+ oss << "\"\"";
+ else
+ oss << IMAPUtils::quoteString(pathString + m_connection->hierarchySeparator());
+
+ oss << " %";
+ }
+
+ m_connection->send(true, oss.str(), true);
+
+
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("LIST", m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+
+ std::vector <ref <folder> > v;
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("LIST",
+ m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ (*it)->response_data()->mailbox_data();
+
+ if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST)
+ continue;
+
+ // Get folder path
+ const class IMAPParser::mailbox* mailbox =
+ mailboxData->mailbox_list()->mailbox();
+
+ folder::path path = IMAPUtils::stringToPath
+ (mailboxData->mailbox_list()->quoted_char(), mailbox->name());
+
+ if (recursive || m_path.isDirectParentOf(path))
+ {
+ // Append folder to list
+ const class IMAPParser::mailbox_flag_list* mailbox_flag_list =
+ mailboxData->mailbox_list()->mailbox_flag_list();
+
+ v.push_back(vmime::create <IMAPFolder>(path, m_store,
+ IMAPUtils::folderTypeFromFlags(mailbox_flag_list),
+ IMAPUtils::folderFlagsFromFlags(mailbox_flag_list)));
+ }
+ }
+
+ return (v);
+}
+
+
+void IMAPFolder::fetchMessages(std::vector <ref <message> >& 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 <ref <message> >::iterator it = msg.begin() ;
+ it != msg.end() ; ++it)
+ {
+ (*it).dynamicCast <IMAPMessage>()->fetch(this, options);
+
+ if (progress)
+ progress->progress(++current, total);
+ }
+
+ if (progress)
+ progress->stop(total);
+}
+
+
+void IMAPFolder::fetchMessage(ref <message> msg, const int options)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ msg.dynamicCast <IMAPMessage>()->fetch(this, options);
+}
+
+
+const int IMAPFolder::getFetchCapabilities() const
+{
+ return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | FETCH_STRUCTURE |
+ FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID |
+ FETCH_IMPORTANCE);
+}
+
+
+ref <folder> IMAPFolder::getParent()
+{
+ if (m_path.isEmpty())
+ return NULL;
+ else
+ return vmime::create <IMAPFolder>(m_path.getParent(), m_store);
+}
+
+
+weak_ref <const store> IMAPFolder::getStore() const
+{
+ return (m_store);
+}
+
+
+weak_ref <store> IMAPFolder::getStore()
+{
+ return (m_store);
+}
+
+
+void IMAPFolder::registerMessage(IMAPMessage* msg)
+{
+ m_messages.push_back(msg);
+}
+
+
+void IMAPFolder::unregisterMessage(IMAPMessage* msg)
+{
+ std::vector <IMAPMessage*>::iterator it =
+ std::find(m_messages.begin(), m_messages.end(), msg);
+
+ if (it != m_messages.end())
+ m_messages.erase(it);
+}
+
+
+void IMAPFolder::onStoreDisconnected()
+{
+ m_store = NULL;
+}
+
+
+void IMAPFolder::deleteMessage(const int num)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << num << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() == num &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= message::FLAG_DELETED;
+ }
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.push_back(num);
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::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");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << from << ":";
+
+ if (to == -1) command << m_messageCount;
+ else command << to;
+
+ command << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= message::FLAG_DELETED;
+ }
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::deleteMessages(const std::vector <int>& 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");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Sort the list of message numbers
+ std::vector <int> list;
+
+ list.resize(nums.size());
+ std::copy(nums.begin(), nums.end(), list.begin());
+
+ std::sort(list.begin(), list.end());
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE ";
+ command << IMAPUtils::listToSet(list, m_messageCount, true);
+ command << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()))
+ {
+ if ((*it)->m_flags != message::FLAG_UNDEFINED)
+ (*it)->m_flags |= message::FLAG_DELETED;
+ }
+ }
+
+ // Notify message flags changed
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, list);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const int from, const int to, const int flags, const int mode)
+{
+ 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");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ std::ostringstream oss;
+
+ if (to == -1)
+ oss << from << ":*";
+ else
+ oss << from << ":" << to;
+
+ setMessageFlags(oss.str(), flags, mode);
+
+ // Update local flags
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= flags;
+ }
+ }
+
+ break;
+ }
+ case message::FLAG_MODE_REMOVE:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags &= ~flags;
+ }
+ }
+
+ break;
+ }
+ default:
+ case message::FLAG_MODE_SET:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags = flags;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const std::vector <int>& nums, const int flags, const int mode)
+{
+ if (!m_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");
+
+ // Sort the list of message numbers
+ std::vector <int> list;
+
+ list.resize(nums.size());
+ std::copy(nums.begin(), nums.end(), list.begin());
+
+ std::sort(list.begin(), list.end());
+
+ // Delegates call
+ setMessageFlags(IMAPUtils::listToSet(list, m_messageCount, true), flags, mode);
+
+ // Update local flags
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= flags;
+ }
+ }
+
+ break;
+ }
+ case message::FLAG_MODE_REMOVE:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags &= ~flags;
+ }
+ }
+
+ break;
+ }
+ default:
+ case message::FLAG_MODE_SET:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags = flags;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ // Notify message flags changed
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const string& set, const int flags, const int mode)
+{
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << set;
+
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD: command << " +FLAGS.SILENT "; break;
+ case message::FLAG_MODE_REMOVE: command << " -FLAGS.SILENT "; break;
+ default:
+ case message::FLAG_MODE_SET: command << " FLAGS.SILENT "; break;
+ }
+
+ const string flagList = IMAPUtils::messageFlagList(flags);
+
+ if (!flagList.empty())
+ {
+ command << flagList;
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+ }
+}
+
+
+void IMAPFolder::addMessage(ref <vmime::message> msg, const int flags,
+ vmime::datetime* date, utility::progressionListener* progress)
+{
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ msg->generate(ossAdapter);
+
+ const std::string& str = oss.str();
+ utility::inputStreamStringAdapter strAdapter(str);
+
+ addMessage(strAdapter, str.length(), flags, date, progress);
+}
+
+
+void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int flags,
+ vmime::datetime* date, utility::progressionListener* progress)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "APPEND " << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath())) << ' ';
+
+ const string flagList = IMAPUtils::messageFlagList(flags);
+
+ if (flags != message::FLAG_UNDEFINED && !flagList.empty())
+ {
+ command << flagList;
+ command << ' ';
+ }
+
+ if (date != NULL)
+ {
+ command << IMAPUtils::dateTime(*date);
+ command << ' ';
+ }
+
+ command << '{' << size << '}';
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ bool ok = false;
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respList
+ = resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respList.begin() ; !ok && (it != respList.end()) ; ++it)
+ {
+ if ((*it)->continue_req())
+ ok = true;
+ }
+
+ if (!ok)
+ {
+ throw exceptions::command_error("APPEND",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Send message data
+ const int total = size;
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ char buffer[65536];
+
+ while (!is.eof())
+ {
+ // Read some data from the input stream
+ const int read = is.read(buffer, sizeof(buffer));
+ current += read;
+
+ // Put read data into socket output stream
+ m_connection->sendRaw(buffer, read);
+
+ // Notify progression
+ if (progress)
+ progress->progress(current, total);
+ }
+
+ m_connection->send(false, "", true);
+
+ if (progress)
+ progress->stop(total);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> finalResp(m_connection->readResponse());
+
+ if (finalResp->isBad() || finalResp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("APPEND",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify message added
+ std::vector <int> nums;
+ nums.push_back(m_messageCount + 1);
+
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ m_messageCount++;
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount++;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::expunge()
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (m_mode == MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Send the request
+ m_connection->send(true, "EXPUNGE", true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("EXPUNGE",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update the numbering of the messages
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ std::vector <int> nums;
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("EXPUNGE",
+ m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::message_data* messageData =
+ (*it)->response_data()->message_data();
+
+ // We are only interested in responses of type "EXPUNGE"
+ if (messageData == NULL ||
+ messageData->type() != IMAPParser::message_data::EXPUNGE)
+ {
+ continue;
+ }
+
+ const int number = messageData->number();
+
+ nums.push_back(number);
+
+ for (std::vector <IMAPMessage*>::iterator jt =
+ m_messages.begin() ; jt != m_messages.end() ; ++jt)
+ {
+ if ((*jt)->m_num == number)
+ (*jt)->m_expunged = true;
+ else if ((*jt)->m_num > number)
+ (*jt)->m_num--;
+ }
+ }
+
+ m_messageCount -= nums.size();
+
+ // Notify message expunged
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_REMOVED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ (*it)->m_messageCount = m_messageCount;
+
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_REMOVED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::rename(const folder::path& newPath)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (m_path.isEmpty() || newPath.isEmpty())
+ throw exceptions::illegal_operation("Cannot rename root folder");
+ else if (m_path.getSize() == 1 && m_name.getBuffer() == "INBOX")
+ throw exceptions::illegal_operation("Cannot rename 'INBOX' folder");
+ else if (!m_store->isValidFolderName(newPath.getLastComponent()))
+ throw exceptions::invalid_folder_name();
+
+ // Build the request text
+ std::ostringstream command;
+ command << "RENAME ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath())) << " ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), newPath));
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("RENAME",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify folder renamed
+ folder::path oldPath(m_path);
+
+ m_path = newPath;
+ m_name = newPath.getLastComponent();
+
+ events::folderEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_RENAMED, oldPath, newPath);
+
+ notifyFolder(event);
+
+ // Notify folders with the same path and sub-folders
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == oldPath)
+ {
+ (*it)->m_path = newPath;
+ (*it)->m_name = newPath.getLastComponent();
+
+ events::folderEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ 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);
+
+ events::folderEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path);
+
+ (*it)->notifyFolder(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessage(const folder::path& dest, const int num)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Construct set
+ std::ostringstream set;
+ set << num;
+
+ // Delegate message copy
+ copyMessages(set.str(), dest);
+
+ // Notify message count changed
+ std::vector <int> nums;
+ nums.push_back(num);
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount++;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const folder::path& dest, 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 (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ // Construct set
+ std::ostringstream set;
+
+ if (to == -1)
+ set << from << ":*";
+ else
+ set << from << ":" << to;
+
+ // Delegate message copy
+ copyMessages(set.str(), dest);
+
+ // Notify message count changed
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount += count;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Delegate message copy
+ copyMessages(IMAPUtils::listToSet(nums, m_messageCount), dest);
+
+ // Notify message count changed
+ const int count = nums.size();
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount += count;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const string& set, const folder::path& dest)
+{
+ // Build the request text
+ std::ostringstream command;
+ command << "COPY " << set << " ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), dest));
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("COPY",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+}
+
+
+void IMAPFolder::status(int& count, int& unseen)
+{
+ count = 0;
+ unseen = 0;
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STATUS ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath()));
+ command << " (MESSAGES UNSEEN)";
+
+ // Send the request
+ m_store->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_store->m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STATUS",
+ m_store->m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("STATUS",
+ m_store->m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::response_data* responseData = (*it)->response_data();
+
+ if (responseData->mailbox_data() &&
+ responseData->mailbox_data()->type() == IMAPParser::mailbox_data::STATUS)
+ {
+ const std::vector <IMAPParser::status_info*>& statusList =
+ responseData->mailbox_data()->status_info_list();
+
+ for (std::vector <IMAPParser::status_info*>::const_iterator
+ jt = statusList.begin() ; jt != statusList.end() ; ++jt)
+ {
+ switch ((*jt)->status_att()->type())
+ {
+ case IMAPParser::status_att::MESSAGES:
+
+ count = (*jt)->number()->value();
+ break;
+
+ case IMAPParser::status_att::UNSEEN:
+
+ unseen = (*jt)->number()->value();
+ break;
+
+ default:
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Notify message count changed (new messages)
+ if (m_messageCount != count)
+ {
+ const int oldCount = m_messageCount;
+
+ m_messageCount = count;
+
+ if (count > oldCount)
+ {
+ std::vector <int> nums;
+ nums.reserve(count - oldCount);
+
+ for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::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)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+ }
+ }
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp
new file mode 100644
index 00000000..0d9e5e00
--- /dev/null
+++ b/src/net/imap/IMAPMessage.cpp
@@ -0,0 +1,859 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 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/net/imap/IMAPParser.hpp"
+#include "vmime/net/imap/IMAPMessage.hpp"
+#include "vmime/net/imap/IMAPFolder.hpp"
+#include "vmime/net/imap/IMAPStore.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+#include "vmime/net/imap/IMAPUtils.hpp"
+
+#include <sstream>
+#include <iterator>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+//
+// IMAPpart
+//
+
+class IMAPstructure;
+
+class IMAPpart : public part
+{
+private:
+
+ friend class vmime::creator;
+
+ IMAPpart(weak_ref <IMAPpart> parent, const int number, const IMAPParser::body_type_mpart* mpart);
+ IMAPpart(weak_ref <IMAPpart> parent, const int number, const IMAPParser::body_type_1part* part);
+
+public:
+
+ const structure& getStructure() const;
+ structure& getStructure();
+
+ weak_ref <const IMAPpart> getParent() const { return (m_parent); }
+
+ const mediaType& getType() const { return (m_mediaType); }
+ const int getSize() const { return (m_size); }
+ const int getNumber() const { return (m_number); }
+
+ const header& getHeader() const
+ {
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+ else
+ return (*m_header);
+ }
+
+
+ static ref <IMAPpart> create
+ (weak_ref <IMAPpart> parent, const int number, const IMAPParser::body* body)
+ {
+ if (body->body_type_mpart())
+ return vmime::create <IMAPpart>(parent, number, body->body_type_mpart());
+ else
+ return vmime::create <IMAPpart>(parent, number, body->body_type_1part());
+ }
+
+
+ header& getOrCreateHeader()
+ {
+ if (m_header != NULL)
+ return (*m_header);
+ else
+ return (*(m_header = vmime::create <header>()));
+ }
+
+private:
+
+ ref <IMAPstructure> m_structure;
+ weak_ref <IMAPpart> m_parent;
+ ref <header> m_header;
+
+ int m_number;
+ int m_size;
+ mediaType m_mediaType;
+};
+
+
+
+//
+// IMAPstructure
+//
+
+class IMAPstructure : public structure
+{
+private:
+
+ IMAPstructure()
+ {
+ }
+
+public:
+
+ IMAPstructure(const IMAPParser::body* body)
+ {
+ m_parts.push_back(IMAPpart::create(NULL, 1, body));
+ }
+
+ IMAPstructure(weak_ref <IMAPpart> parent, const std::vector <IMAPParser::body*>& list)
+ {
+ int number = 1;
+
+ for (std::vector <IMAPParser::body*>::const_iterator
+ it = list.begin() ; it != list.end() ; ++it, ++number)
+ {
+ m_parts.push_back(IMAPpart::create(parent, number, *it));
+ }
+ }
+
+
+ const part& operator[](const int x) const
+ {
+ return (*m_parts[x - 1]);
+ }
+
+ part& operator[](const int x)
+ {
+ return (*m_parts[x - 1]);
+ }
+
+ const int getCount() const
+ {
+ return (m_parts.size());
+ }
+
+
+ static IMAPstructure* emptyStructure()
+ {
+ return (&m_emptyStructure);
+ }
+
+private:
+
+ static IMAPstructure m_emptyStructure;
+
+ std::vector <ref <IMAPpart> > m_parts;
+};
+
+
+IMAPstructure IMAPstructure::m_emptyStructure;
+
+
+
+IMAPpart::IMAPpart(weak_ref <IMAPpart> parent, const int number, const IMAPParser::body_type_mpart* mpart)
+ : m_parent(parent), m_header(NULL), m_number(number), m_size(0)
+{
+ m_mediaType = vmime::mediaType
+ ("multipart", mpart->media_subtype()->value());
+
+ m_structure = vmime::create <IMAPstructure>
+ (thisWeakRef().dynamicCast <IMAPpart>(), mpart->list());
+}
+
+
+IMAPpart::IMAPpart(weak_ref <IMAPpart> parent, const int number, const IMAPParser::body_type_1part* part)
+ : m_parent(parent), m_header(NULL), m_number(number), m_size(0)
+{
+ if (part->body_type_text())
+ {
+ m_mediaType = vmime::mediaType
+ ("text", part->body_type_text()->
+ media_text()->media_subtype()->value());
+
+ m_size = part->body_type_text()->body_fields()->body_fld_octets()->value();
+ }
+ else if (part->body_type_msg())
+ {
+ m_mediaType = vmime::mediaType
+ ("message", part->body_type_msg()->
+ media_message()->media_subtype()->value());
+ }
+ else
+ {
+ m_mediaType = vmime::mediaType
+ (part->body_type_basic()->media_basic()->media_type()->value(),
+ part->body_type_basic()->media_basic()->media_subtype()->value());
+
+ m_size = part->body_type_basic()->body_fields()->body_fld_octets()->value();
+ }
+
+ m_structure = NULL;
+}
+
+
+const class structure& IMAPpart::getStructure() const
+{
+ if (m_structure != NULL)
+ return (*m_structure);
+ else
+ return (*IMAPstructure::emptyStructure());
+}
+
+
+class structure& IMAPpart::getStructure()
+{
+ if (m_structure != NULL)
+ return (*m_structure);
+ else
+ return (*IMAPstructure::emptyStructure());
+}
+
+
+
+#ifndef VMIME_BUILDING_DOC
+
+//
+// IMAPMessage_literalHandler
+//
+
+class IMAPMessage_literalHandler : public IMAPParser::literalHandler
+{
+public:
+
+ IMAPMessage_literalHandler(utility::outputStream& os, utility::progressionListener* progress)
+ : m_os(os), m_progress(progress)
+ {
+ }
+
+ target* targetFor(const IMAPParser::component& comp, const int /* data */)
+ {
+ if (typeid(comp) == typeid(IMAPParser::msg_att_item))
+ {
+ const int type = static_cast
+ <const IMAPParser::msg_att_item&>(comp).type();
+
+ if (type == IMAPParser::msg_att_item::BODY_SECTION ||
+ type == IMAPParser::msg_att_item::RFC822_TEXT)
+ {
+ return new targetStream(m_progress, m_os);
+ }
+ }
+
+ return (NULL);
+ }
+
+private:
+
+ utility::outputStream& m_os;
+ utility::progressionListener* m_progress;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+//
+// IMAPMessage
+//
+
+
+IMAPMessage::IMAPMessage(IMAPFolder* folder, const int num)
+ : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED),
+ m_expunged(false), m_structure(NULL)
+{
+ m_folder->registerMessage(this);
+}
+
+
+IMAPMessage::~IMAPMessage()
+{
+ if (m_folder)
+ m_folder->unregisterMessage(this);
+}
+
+
+void IMAPMessage::onFolderClosed()
+{
+ m_folder = NULL;
+}
+
+
+const int IMAPMessage::getNumber() const
+{
+ return (m_num);
+}
+
+
+const message::uid IMAPMessage::getUniqueId() const
+{
+ return (m_uid);
+}
+
+
+const int IMAPMessage::getSize() const
+{
+ if (m_size == -1)
+ throw exceptions::unfetched_object();
+
+ return (m_size);
+}
+
+
+const bool IMAPMessage::isExpunged() const
+{
+ return (m_expunged);
+}
+
+
+const int IMAPMessage::getFlags() const
+{
+ if (m_flags == FLAG_UNDEFINED)
+ throw exceptions::unfetched_object();
+
+ return (m_flags);
+}
+
+
+const structure& IMAPMessage::getStructure() const
+{
+ if (m_structure == NULL)
+ throw exceptions::unfetched_object();
+
+ return (*m_structure);
+}
+
+
+structure& IMAPMessage::getStructure()
+{
+ if (m_structure == NULL)
+ throw exceptions::unfetched_object();
+
+ return (*m_structure);
+}
+
+
+ref <const header> IMAPMessage::getHeader() const
+{
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+
+ return (m_header);
+}
+
+
+void IMAPMessage::extract(utility::outputStream& os, utility::progressionListener* progress,
+ const int start, const int length, const bool peek) const
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+
+ extract(NULL, os, progress, start, length, false, peek);
+}
+
+
+void IMAPMessage::extractPart
+ (const part& p, utility::outputStream& os, utility::progressionListener* progress,
+ const int start, const int length, const bool peek) const
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+
+ extract(&p, os, progress, start, length, false, peek);
+}
+
+
+void IMAPMessage::fetchPartHeader(part& p)
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ extract(&p, ossAdapter, NULL, 0, -1, true, true);
+
+ static_cast <IMAPpart&>(p).getOrCreateHeader().parse(oss.str());
+}
+
+
+void IMAPMessage::extract(const part* p, utility::outputStream& os,
+ utility::progressionListener* progress, const int start,
+ const int length, const bool headerOnly, const bool peek) const
+{
+ IMAPMessage_literalHandler literalHandler(os, progress);
+
+ // Construct section identifier
+ std::ostringstream section;
+
+ if (p != NULL)
+ {
+ weak_ref <const IMAPpart> currentPart = static_cast <const IMAPpart*>(p);
+ std::vector <int> numbers;
+
+ numbers.push_back(currentPart->getNumber());
+ currentPart = currentPart->getParent();
+
+ while (currentPart != NULL)
+ {
+ numbers.push_back(currentPart->getNumber());
+ currentPart = currentPart->getParent();
+ }
+
+ numbers.erase(numbers.end() - 1);
+
+ for (std::vector <int>::reverse_iterator it = numbers.rbegin() ; it != numbers.rend() ; ++it)
+ {
+ if (it != numbers.rbegin()) section << ".";
+ section << *it;
+ }
+ }
+
+ // Build the request text
+ std::ostringstream command;
+
+ command << "FETCH " << m_num << " BODY";
+ if (peek) command << ".PEEK";
+ command << "[";
+ command << section.str();
+ if (headerOnly) command << ".MIME"; // "MIME" not "HEADER" for parts
+ command << "]";
+
+ if (start != 0 || length != -1)
+ command << "<" << start << "." << length << ">";
+
+ // Send the request
+ m_folder->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp
+ (m_folder->m_connection->readResponse(&literalHandler));
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("FETCH",
+ m_folder->m_connection->getParser()->lastLine(), "bad response");
+ }
+
+
+ if (!headerOnly)
+ {
+ // TODO: update the flags (eg. flag "\Seen" may have been set)
+ }
+}
+
+
+void IMAPMessage::fetch(IMAPFolder* folder, const int options)
+{
+ if (m_folder != folder)
+ throw exceptions::folder_not_found();
+
+ // TODO: optimization: send the request for multiple
+ // messages at the same time (FETCH x:y)
+
+ // Example:
+ // C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
+ // S: * 2 FETCH ....
+ // S: * 3 FETCH ....
+ // S: * 4 FETCH ....
+ // S: A654 OK FETCH completed
+
+ std::vector <string> items;
+
+ if (options & folder::FETCH_SIZE)
+ items.push_back("RFC822.SIZE");
+
+ if (options & folder::FETCH_FLAGS)
+ items.push_back("FLAGS");
+
+ if (options & folder::FETCH_STRUCTURE)
+ items.push_back("BODYSTRUCTURE");
+
+ if (options & folder::FETCH_UID)
+ items.push_back("UID");
+
+ if (options & folder::FETCH_FULL_HEADER)
+ items.push_back("RFC822.HEADER");
+ else
+ {
+ if (options & folder::FETCH_ENVELOPE)
+ items.push_back("ENVELOPE");
+
+ std::vector <string> headerFields;
+
+ if (options & folder::FETCH_CONTENT_INFO)
+ headerFields.push_back("CONTENT_TYPE");
+
+ if (options & folder::FETCH_IMPORTANCE)
+ {
+ headerFields.push_back("IMPORTANCE");
+ headerFields.push_back("X-PRIORITY");
+ }
+
+ if (!headerFields.empty())
+ {
+ string list;
+
+ for (std::vector <string>::iterator it = headerFields.begin() ;
+ it != headerFields.end() ; ++it)
+ {
+ if (it != headerFields.begin())
+ list += " ";
+
+ list += *it;
+ }
+
+ items.push_back("BODY[HEADER.FIELDS (" + list + ")]");
+ }
+ }
+
+ // Build the request text
+ std::ostringstream command;
+ command << "FETCH " << m_num << " (";
+
+ for (std::vector <string>::const_iterator it = items.begin() ;
+ it != items.end() ; ++it)
+ {
+ if (it != items.begin()) command << " ";
+ command << *it;
+ }
+
+ command << ")";
+
+ // Send the request
+ m_folder->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_folder->m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("FETCH",
+ m_folder->m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("FETCH",
+ m_folder->m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::message_data* messageData =
+ (*it)->response_data()->message_data();
+
+ // We are only interested in responses of type "FETCH"
+ if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH)
+ continue;
+
+ if (static_cast <int>(messageData->number()) != m_num)
+ continue;
+
+ // Process fetch response for this message
+ processFetchResponse(options, messageData->msg_att());
+ }
+}
+
+
+void IMAPMessage::processFetchResponse
+ (const int options, const IMAPParser::msg_att* msgAtt)
+{
+ // Get message attributes
+ const std::vector <IMAPParser::msg_att_item*> atts =
+ msgAtt->items();
+
+ int flags = 0;
+
+ for (std::vector <IMAPParser::msg_att_item*>::const_iterator
+ it = atts.begin() ; it != atts.end() ; ++it)
+ {
+ switch ((*it)->type())
+ {
+ case IMAPParser::msg_att_item::FLAGS:
+ {
+ flags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list());
+ break;
+ }
+ case IMAPParser::msg_att_item::UID:
+ {
+ std::ostringstream oss;
+ oss << m_folder->m_uidValidity << ":" << (*it)->unique_id()->value();
+
+ m_uid = oss.str();
+ break;
+ }
+ case IMAPParser::msg_att_item::ENVELOPE:
+ {
+ if (!(options & folder::FETCH_FULL_HEADER))
+ {
+ const IMAPParser::envelope* env = (*it)->envelope();
+ ref <vmime::header> hdr = getOrCreateHeader();
+
+ // Date
+ hdr->Date()->setValue(env->env_date()->value());
+
+ // Subject
+ text subject;
+ text::decodeAndUnfold(env->env_subject()->value(), &subject);
+
+ hdr->Subject()->setValue(subject);
+
+ // From
+ mailboxList from;
+ convertAddressList(*(env->env_from()), from);
+
+ if (!from.isEmpty())
+ hdr->From()->setValue(*(from.getMailboxAt(0)));
+
+ // To
+ mailboxList to;
+ convertAddressList(*(env->env_to()), to);
+
+ hdr->To()->setValue(to);
+
+ // Sender
+ mailboxList sender;
+ convertAddressList(*(env->env_sender()), sender);
+
+ if (!sender.isEmpty())
+ hdr->Sender()->setValue(*(sender.getMailboxAt(0)));
+
+ // Reply-to
+ mailboxList replyTo;
+ convertAddressList(*(env->env_reply_to()), replyTo);
+
+ if (!replyTo.isEmpty())
+ hdr->ReplyTo()->setValue(*(replyTo.getMailboxAt(0)));
+
+ // Cc
+ mailboxList cc;
+ convertAddressList(*(env->env_cc()), cc);
+
+ if (!cc.isEmpty())
+ hdr->Cc()->setValue(cc);
+
+ // Bcc
+ mailboxList bcc;
+ convertAddressList(*(env->env_bcc()), bcc);
+
+ if (!bcc.isEmpty())
+ hdr->Bcc()->setValue(bcc);
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_STRUCTURE:
+ {
+ m_structure = vmime::create <IMAPstructure>((*it)->body());
+ break;
+ }
+ case IMAPParser::msg_att_item::RFC822_HEADER:
+ {
+ getOrCreateHeader()->parse((*it)->nstring()->value());
+ break;
+ }
+ case IMAPParser::msg_att_item::RFC822_SIZE:
+ {
+ m_size = (*it)->number()->value();
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_SECTION:
+ {
+ if (!(options & folder::FETCH_FULL_HEADER))
+ {
+ if ((*it)->section()->section_text1() &&
+ (*it)->section()->section_text1()->type()
+ == IMAPParser::section_text::HEADER_FIELDS)
+ {
+ header tempHeader;
+ tempHeader.parse((*it)->nstring()->value());
+
+ vmime::header& hdr = *getOrCreateHeader();
+ std::vector <ref <headerField> > fields = tempHeader.getFieldList();
+
+ for (std::vector <ref <headerField> >::const_iterator jt = fields.begin() ;
+ jt != fields.end() ; ++jt)
+ {
+ hdr.appendField((*jt)->clone().dynamicCast <headerField>());
+ }
+ }
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::INTERNALDATE:
+ case IMAPParser::msg_att_item::RFC822:
+ case IMAPParser::msg_att_item::RFC822_TEXT:
+ case IMAPParser::msg_att_item::BODY:
+ {
+ break;
+ }
+
+ }
+ }
+
+ if (options & folder::FETCH_FLAGS)
+ m_flags = flags;
+}
+
+
+ref <header> IMAPMessage::getOrCreateHeader()
+{
+ if (m_header != NULL)
+ return (m_header);
+ else
+ return (m_header = vmime::create <header>());
+}
+
+
+void IMAPMessage::convertAddressList
+ (const IMAPParser::address_list& src, mailboxList& dest)
+{
+ for (std::vector <IMAPParser::address*>::const_iterator
+ it = src.addresses().begin() ; it != src.addresses().end() ; ++it)
+ {
+ const IMAPParser::address& addr = **it;
+
+ text name;
+ text::decodeAndUnfold(addr.addr_name()->value(), &name);
+
+ string email = addr.addr_mailbox()->value()
+ + "@" + addr.addr_host()->value();
+
+ dest.appendMailbox(vmime::create <mailbox>(name, email));
+ }
+}
+
+
+void IMAPMessage::setFlags(const int flags, const int mode)
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+ else if (m_folder->m_mode == folder::MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << m_num;
+
+ switch (mode)
+ {
+ case FLAG_MODE_ADD: command << " +FLAGS"; break;
+ case FLAG_MODE_REMOVE: command << " -FLAGS"; break;
+ default:
+ case FLAG_MODE_SET: command << " FLAGS"; break;
+ }
+
+ if (m_flags == FLAG_UNDEFINED) // Update local flags only if they
+ command << ".SILENT "; // have been fetched previously
+ else
+ command << " ";
+
+ std::vector <string> flagList;
+
+ if (flags & FLAG_REPLIED) flagList.push_back("\\Answered");
+ if (flags & FLAG_MARKED) flagList.push_back("\\Flagged");
+ if (flags & FLAG_DELETED) flagList.push_back("\\Deleted");
+ if (flags & FLAG_SEEN) flagList.push_back("\\Seen");
+
+ if (!flagList.empty())
+ {
+ command << "(";
+
+ if (flagList.size() >= 2)
+ {
+ std::copy(flagList.begin(), flagList.end() - 1,
+ std::ostream_iterator <string>(command, " "));
+ }
+
+ command << *(flagList.end() - 1) << ")";
+
+ // Send the request
+ m_folder->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_folder->m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("STORE",
+ m_folder->m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update the local flags for this message
+ if (m_flags != FLAG_UNDEFINED)
+ {
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ int newFlags = 0;
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ continue;
+
+ const IMAPParser::message_data* messageData =
+ (*it)->response_data()->message_data();
+
+ // We are only interested in responses of type "FETCH"
+ if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH)
+ continue;
+
+ // Get message attributes
+ const std::vector <IMAPParser::msg_att_item*> atts =
+ messageData->msg_att()->items();
+
+ for (std::vector <IMAPParser::msg_att_item*>::const_iterator
+ it = atts.begin() ; it != atts.end() ; ++it)
+ {
+ if ((*it)->type() == IMAPParser::msg_att_item::FLAGS)
+ newFlags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list());
+ }
+ }
+
+ m_flags = newFlags;
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.push_back(m_num);
+
+ events::messageChangedEvent event
+ (m_folder->thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ for (std::list <IMAPFolder*>::iterator it = m_folder->m_store->m_folders.begin() ;
+ it != m_folder->m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == m_folder->m_path)
+ (*it)->notifyMessageChanged(event);
+ }
+ }
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPStore.cpp b/src/net/imap/IMAPStore.cpp
new file mode 100644
index 00000000..c8dab178
--- /dev/null
+++ b/src/net/imap/IMAPStore.cpp
@@ -0,0 +1,308 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 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/net/imap/IMAPStore.hpp"
+#include "vmime/net/imap/IMAPFolder.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+
+#include <map>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+#ifndef VMIME_BUILDING_DOC
+
+//
+// IMAPauthenticator: private class used internally
+//
+// Used to request user credentials only in the first authentication
+// and reuse this information the next times
+//
+
+class IMAPauthenticator : public authenticator
+{
+public:
+
+ IMAPauthenticator(ref <authenticator> auth)
+ : m_auth(auth), m_infos(NULL)
+ {
+ }
+
+ ~IMAPauthenticator()
+ {
+ }
+
+ const authenticationInfos requestAuthInfos() const
+ {
+ if (m_infos == NULL)
+ m_infos = vmime::create <authenticationInfos>(m_auth->requestAuthInfos());
+
+ return (*m_infos);
+ }
+
+private:
+
+ ref <authenticator> m_auth;
+ mutable ref <authenticationInfos> m_infos;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+//
+// IMAPStore
+//
+
+IMAPStore::IMAPStore(ref <session> sess, ref <authenticator> auth)
+ : store(sess, getInfosInstance(), auth),
+ m_connection(NULL), m_oneTimeAuth(NULL)
+{
+}
+
+
+IMAPStore::~IMAPStore()
+{
+ if (isConnected())
+ disconnect();
+}
+
+
+ref <authenticator> IMAPStore::oneTimeAuthenticator()
+{
+ return (m_oneTimeAuth);
+}
+
+
+const string IMAPStore::getProtocolName() const
+{
+ return "imap";
+}
+
+
+ref <folder> IMAPStore::getRootFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <IMAPFolder>(folder::path(), this);
+}
+
+
+ref <folder> IMAPStore::getDefaultFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <IMAPFolder>(folder::path::component("INBOX"), this);
+}
+
+
+ref <folder> IMAPStore::getFolder(const folder::path& path)
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <IMAPFolder>(path, this);
+}
+
+
+const bool IMAPStore::isValidFolderName(const folder::path::component& /* name */) const
+{
+ return true;
+}
+
+
+void IMAPStore::connect()
+{
+ if (isConnected())
+ throw exceptions::already_connected();
+
+ m_oneTimeAuth = vmime::create <IMAPauthenticator>(getAuthenticator());
+
+ m_connection = vmime::create <IMAPConnection>
+ (thisWeakRef().dynamicCast <IMAPStore>(), m_oneTimeAuth);
+
+ try
+ {
+ m_connection->connect();
+ }
+ catch (std::exception&)
+ {
+ m_connection = NULL;
+ throw;
+ }
+}
+
+
+const bool IMAPStore::isConnected() const
+{
+ return (m_connection && m_connection->isConnected());
+}
+
+
+void IMAPStore::disconnect()
+{
+ if (!isConnected())
+ throw exceptions::not_connected();
+
+ for (std::list <IMAPFolder*>::iterator it = m_folders.begin() ;
+ it != m_folders.end() ; ++it)
+ {
+ (*it)->onStoreDisconnected();
+ }
+
+ m_folders.clear();
+
+
+ m_connection->disconnect();
+
+ m_oneTimeAuth = NULL;
+
+ m_connection = NULL;
+}
+
+
+void IMAPStore::noop()
+{
+ if (!isConnected())
+ throw exceptions::not_connected();
+
+ m_connection->send(true, "NOOP", true);
+
+ utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error("NOOP", m_connection->getParser()->lastLine());
+ }
+}
+
+
+ref <IMAPConnection> IMAPStore::connection()
+{
+ return (m_connection);
+}
+
+
+void IMAPStore::registerFolder(IMAPFolder* folder)
+{
+ m_folders.push_back(folder);
+}
+
+
+void IMAPStore::unregisterFolder(IMAPFolder* folder)
+{
+ std::list <IMAPFolder*>::iterator it = std::find(m_folders.begin(), m_folders.end(), folder);
+ if (it != m_folders.end()) m_folders.erase(it);
+}
+
+
+const int IMAPStore::getCapabilities() const
+{
+ return (CAPABILITY_CREATE_FOLDER |
+ CAPABILITY_RENAME_FOLDER |
+ CAPABILITY_ADD_MESSAGE |
+ CAPABILITY_COPY_MESSAGE |
+ CAPABILITY_DELETE_MESSAGE |
+ CAPABILITY_PARTIAL_FETCH |
+ CAPABILITY_MESSAGE_FLAGS |
+ CAPABILITY_EXTRACT_PART);
+}
+
+
+
+// Service infos
+
+IMAPStore::_infos IMAPStore::sm_infos;
+
+
+const serviceInfos& IMAPStore::getInfosInstance()
+{
+ return (sm_infos);
+}
+
+
+const serviceInfos& IMAPStore::getInfos() const
+{
+ return (sm_infos);
+}
+
+
+const string IMAPStore::_infos::getPropertyPrefix() const
+{
+ return "store.imap.";
+}
+
+
+const IMAPStore::_infos::props& IMAPStore::_infos::getProperties() const
+{
+ static props p =
+ {
+ // IMAP-specific options
+ // (none)
+
+ // Common properties
+ property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "143"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ return p;
+}
+
+
+const std::vector <serviceInfos::property> IMAPStore::_infos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ // IMAP-specific options
+ // (none)
+
+ // Common properties
+ list.push_back(p.PROPERTY_AUTH_USERNAME);
+ list.push_back(p.PROPERTY_AUTH_PASSWORD);
+
+ list.push_back(p.PROPERTY_SERVER_ADDRESS);
+ list.push_back(p.PROPERTY_SERVER_PORT);
+ list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
+
+ list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
+
+ return (list);
+}
+
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPTag.cpp b/src/net/imap/IMAPTag.cpp
new file mode 100644
index 00000000..5331d8bf
--- /dev/null
+++ b/src/net/imap/IMAPTag.cpp
@@ -0,0 +1,99 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 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/net/imap/IMAPTag.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+const int IMAPTag::sm_maxNumber = 52 * 10 * 10 * 10;
+
+
+IMAPTag::IMAPTag(const int number)
+ : m_number(number)
+{
+ m_tag.resize(4);
+}
+
+
+IMAPTag::IMAPTag(const IMAPTag& tag)
+ : object(), m_number(tag.m_number)
+{
+ m_tag.resize(4);
+}
+
+
+IMAPTag::IMAPTag()
+ : m_number(0)
+{
+ m_tag.resize(4);
+}
+
+
+IMAPTag& IMAPTag::operator++()
+{
+ ++m_number;
+
+ if (m_number >= sm_maxNumber)
+ m_number = 1;
+
+ generate();
+
+ return (*this);
+}
+
+
+const IMAPTag IMAPTag::operator++(int)
+{
+ IMAPTag old(*this);
+ operator++();
+ return (old);
+}
+
+
+const int IMAPTag::number() const
+{
+ return (m_number);
+}
+
+
+IMAPTag::operator string() const
+{
+ return (m_tag);
+}
+
+
+void IMAPTag::generate()
+{
+ static const char prefixChars[53] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ m_tag[0] = prefixChars[m_number / 1000];
+ m_tag[1] = '0' + (m_number % 1000) / 100;
+ m_tag[2] = '0' + (m_number % 100) / 10;
+ m_tag[3] = '0' + (m_number % 10);
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPUtils.cpp b/src/net/imap/IMAPUtils.cpp
new file mode 100644
index 00000000..11111b13
--- /dev/null
+++ b/src/net/imap/IMAPUtils.cpp
@@ -0,0 +1,555 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 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/net/imap/IMAPUtils.hpp"
+#include "vmime/net/message.hpp"
+
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+const string IMAPUtils::quoteString(const string& text)
+{
+ //
+ // ATOM_CHAR ::= <any CHAR except atom_specials>
+ //
+ // atom_specials ::= "(" / ")" / "{" / SPACE / CTL /
+ // list_wildcards / quoted_specials
+ //
+ // list_wildcards ::= "%" / "*"
+ //
+ // quoted_specials ::= <"> / "\"
+ //
+ // CHAR ::= <any 7-bit US-ASCII character except NUL,
+ // 0x01 - 0x7f>
+ //
+ // CTL ::= <any ASCII control character and DEL,
+ // 0x00 - 0x1f, 0x7f>
+ //
+
+ bool needQuoting = text.empty();
+
+ for (string::const_iterator it = text.begin() ;
+ !needQuoting && it != text.end() ; ++it)
+ {
+ const unsigned char c = *it;
+
+ switch (c)
+ {
+ case '(':
+ case ')':
+ case '{':
+ case 0x20: // SPACE
+ case '%':
+ case '*':
+ case '"':
+ case '\\':
+
+ needQuoting = true;
+ break;
+
+ default:
+
+ if (c <= 0x1f || c >= 0x7f)
+ needQuoting = true;
+ }
+ }
+
+ if (needQuoting)
+ {
+ string quoted;
+ quoted.reserve((text.length() * 3) / 2 + 2);
+
+ quoted += '"';
+
+ for (string::const_iterator it = text.begin() ;
+ !needQuoting && it != text.end() ; ++it)
+ {
+ const unsigned char c = *it;
+
+ if (c == '\\' || c == '"')
+ quoted += '\\';
+
+ quoted += c;
+ }
+
+ quoted += '"';
+
+ return (quoted);
+ }
+ else
+ {
+ return (text);
+ }
+}
+
+
+const string IMAPUtils::pathToString
+ (const char hierarchySeparator, const folder::path& path)
+{
+ string result;
+
+ for (int i = 0 ; i < path.getSize() ; ++i)
+ {
+ if (i > 0) result += hierarchySeparator;
+ result += toModifiedUTF7(hierarchySeparator, path[i]);
+ }
+
+ return (result);
+}
+
+
+const folder::path IMAPUtils::stringToPath
+ (const char hierarchySeparator, const string& str)
+{
+ folder::path result;
+ string::const_iterator begin = str.begin();
+
+ for (string::const_iterator it = str.begin() ; it != str.end() ; ++it)
+ {
+ if (*it == hierarchySeparator)
+ {
+ result /= fromModifiedUTF7(string(begin, it));
+ begin = it + 1;
+ }
+ }
+
+ if (begin != str.end())
+ {
+ result /= fromModifiedUTF7(string(begin, str.end()));
+ }
+
+ return (result);
+}
+
+
+const string IMAPUtils::toModifiedUTF7
+ (const char hierarchySeparator, const folder::path::component& text)
+{
+ // We will replace the hierarchy separator with an equivalent
+ // UTF-7 sequence, so we compute it here...
+ const char base64alphabet[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,=";
+
+ const unsigned int hs = static_cast <unsigned int>(static_cast <unsigned char>(hierarchySeparator));
+
+ string hsUTF7;
+ hsUTF7.resize(3);
+
+ hsUTF7[0] = base64alphabet[0];
+ hsUTF7[1] = base64alphabet[(hs & 0xF0) >> 4];
+ hsUTF7[2] = base64alphabet[(hs & 0x0F) << 2];
+
+ // Transcode path component to UTF-7 charset.
+ // WARNING: This may throw "exceptions::charset_conv_error"
+ const string cvt = text.getConvertedText(charset(charsets::UTF_7));
+
+ // Transcode to modified UTF-7 (RFC-2060).
+ string out;
+ out.reserve((cvt.length() * 3) / 2);
+
+ bool inB64sequence = false;
+
+ for (string::const_iterator it = cvt.begin() ; it != cvt.end() ; ++it)
+ {
+ const unsigned char c = *it;
+
+ // Replace hierarchy separator with an equivalent UTF-7 Base64 sequence
+ if (!inB64sequence && c == hierarchySeparator)
+ {
+ out += "&" + hsUTF7 + "-";
+ continue;
+ }
+
+ switch (c)
+ {
+ // Beginning of Base64 sequence: replace '+' with '&'
+ case '+':
+ {
+ if (!inB64sequence)
+ {
+ inB64sequence = true;
+ out += '&';
+ }
+ else
+ {
+ out += '+';
+ }
+
+ break;
+ }
+ // End of Base64 sequence
+ case '-':
+ {
+ inB64sequence = false;
+ out += '-';
+ break;
+ }
+ // ',' is used instead of '/' in modified Base64
+ case '/':
+ {
+ out += inB64sequence ? ',' : '/';
+ break;
+ }
+ // '&' (0x26) is represented by the two-octet sequence "&-"
+ case '&':
+ {
+ if (!inB64sequence)
+ out += "&-";
+ else
+ out += '&';
+
+ break;
+ }
+ default:
+ {
+ out += c;
+ break;
+ }
+
+ }
+ }
+
+ return (out);
+}
+
+
+const folder::path::component IMAPUtils::fromModifiedUTF7(const string& text)
+{
+ // Transcode from modified UTF-7 (RFC-2060).
+ string out;
+ out.reserve(text.length());
+
+ bool inB64sequence = false;
+ unsigned char prev = 0;
+
+ for (string::const_iterator it = text.begin() ; it != text.end() ; ++it)
+ {
+ const unsigned char c = *it;
+
+ switch (c)
+ {
+ // Start of Base64 sequence
+ case '&':
+ {
+ if (!inB64sequence)
+ {
+ inB64sequence = true;
+ out += '+';
+ }
+ else
+ {
+ out += '&';
+ }
+
+ break;
+ }
+ // End of Base64 sequence (or "&-" --> "&")
+ case '-':
+ {
+ if (inB64sequence && prev == '&')
+ out += '&';
+ else
+ out += '-';
+
+ inB64sequence = false;
+ break;
+ }
+ // ',' is used instead of '/' in modified Base64
+ case ',':
+ {
+ out += (inB64sequence ? '/' : ',');
+ break;
+ }
+ default:
+ {
+ out += c;
+ break;
+ }
+
+ }
+
+ prev = c;
+ }
+
+ // Store it as UTF-8 by default
+ string cvt;
+ charset::convert(out, cvt,
+ charset(charsets::UTF_7), charset(charsets::UTF_8));
+
+ return (folder::path::component(cvt, charset(charsets::UTF_8)));
+}
+
+
+const int IMAPUtils::folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list)
+{
+ // Get folder type
+ int type = folder::TYPE_CONTAINS_MESSAGES | folder::TYPE_CONTAINS_FOLDERS;
+ const std::vector <IMAPParser::mailbox_flag*>& flags = list->flags();
+
+ for (std::vector <IMAPParser::mailbox_flag*>::const_iterator it = flags.begin() ;
+ it != flags.end() ; ++it)
+ {
+ if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT)
+ type &= ~folder::TYPE_CONTAINS_MESSAGES;
+ }
+
+ if (type & folder::TYPE_CONTAINS_MESSAGES)
+ type &= ~folder::TYPE_CONTAINS_FOLDERS;
+
+ return (type);
+}
+
+
+const int IMAPUtils::folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list)
+{
+ // Get folder flags
+ int folderFlags = folder::FLAG_CHILDREN;
+ const std::vector <IMAPParser::mailbox_flag*>& flags = list->flags();
+
+ for (std::vector <IMAPParser::mailbox_flag*>::const_iterator it = flags.begin() ;
+ it != flags.end() ; ++it)
+ {
+ if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT)
+ folderFlags |= folder::FLAG_NO_OPEN;
+ else if ((*it)->type() == IMAPParser::mailbox_flag::NOINFERIORS)
+ folderFlags &= ~folder::FLAG_CHILDREN;
+ }
+
+ return (folderFlags);
+}
+
+
+const int IMAPUtils::messageFlagsFromFlags(const IMAPParser::flag_list* list)
+{
+ const std::vector <IMAPParser::flag*>& flagList = list->flags();
+ int flags = 0;
+
+ for (std::vector <IMAPParser::flag*>::const_iterator
+ it = flagList.begin() ; it != flagList.end() ; ++it)
+ {
+ switch ((*it)->type())
+ {
+ case IMAPParser::flag::ANSWERED:
+ flags |= message::FLAG_REPLIED;
+ break;
+ case IMAPParser::flag::FLAGGED:
+ flags |= message::FLAG_MARKED;
+ break;
+ case IMAPParser::flag::DELETED:
+ flags |= message::FLAG_DELETED;
+ break;
+ case IMAPParser::flag::SEEN:
+ flags |= message::FLAG_SEEN;
+ break;
+
+ default:
+ //case IMAPParser::flag::UNKNOWN:
+ //case IMAPParser::flag::DRAFT:
+ break;
+ }
+ }
+
+ return (flags);
+}
+
+
+const string IMAPUtils::messageFlagList(const int flags)
+{
+ std::vector <string> flagList;
+
+ if (flags & message::FLAG_REPLIED) flagList.push_back("\\Answered");
+ if (flags & message::FLAG_MARKED) flagList.push_back("\\Flagged");
+ if (flags & message::FLAG_DELETED) flagList.push_back("\\Deleted");
+ if (flags & message::FLAG_SEEN) flagList.push_back("\\Seen");
+
+ if (!flagList.empty())
+ {
+ std::ostringstream res;
+ res << "(";
+
+ if (flagList.size() >= 2)
+ {
+ std::copy(flagList.begin(), flagList.end() - 1,
+ std::ostream_iterator <string>(res, " "));
+ }
+
+ res << *(flagList.end() - 1) << ")";
+
+ return (res.str());
+ }
+
+ return "";
+}
+
+
+// This function builds a "IMAP set" given a list. Try to group consecutive
+// message numbers to reduce the list.
+//
+// Example:
+// IN = "1,2,3,4,5,7,8,13,15,16,17"
+// OUT = "1:5,7:8,13,15:*" for a mailbox with a total of 17 messages (max = 17)
+
+const string IMAPUtils::listToSet(const std::vector <int>& list, const int max,
+ const bool alreadySorted)
+{
+ // Sort a copy of the list (if not already sorted)
+ std::vector <int> temp;
+
+ if (!alreadySorted)
+ {
+ temp.resize(list.size());
+ std::copy(list.begin(), list.end(), temp.begin());
+
+ std::sort(temp.begin(), temp.end());
+ }
+
+ const std::vector <int>& theList = (alreadySorted ? list : temp);
+
+ // Build the set
+ std::ostringstream res;
+ int previous = -1, setBegin = -1;
+
+ for (std::vector <int>::const_iterator it = theList.begin() ;
+ it != theList.end() ; ++it)
+ {
+ const int current = *it;
+
+ if (previous == -1)
+ {
+ res << current;
+
+ previous = current;
+ setBegin = current;
+ }
+ else
+ {
+ if (current == previous + 1)
+ {
+ previous = current;
+ }
+ else
+ {
+ if (setBegin != previous)
+ {
+ res << ":" << previous << "," << current;
+
+ previous = current;
+ setBegin = current;
+ }
+ else
+ {
+ if (setBegin != current) // skip duplicates
+ res << "," << current;
+
+ previous = current;
+ setBegin = current;
+ }
+ }
+ }
+ }
+
+ if (previous != setBegin)
+ {
+ if (previous == max)
+ res << ":*";
+ else
+ res << ":" << previous;
+ }
+
+ return (res.str());
+}
+
+
+const string IMAPUtils::dateTime(const vmime::datetime& date)
+{
+ std::ostringstream res;
+
+ // date_time ::= <"> date_day_fixed "-" date_month "-" date_year
+ // SPACE time SPACE zone <">
+ //
+ // time ::= 2digit ":" 2digit ":" 2digit
+ // ;; Hours minutes seconds
+ // zone ::= ("+" / "-") 4digit
+ // ;; Signed four-digit value of hhmm representing
+ // ;; hours and minutes west of Greenwich
+ res << '"';
+
+ // Date
+ if (date.getDay() < 10) res << ' ';
+ res << date.getDay();
+
+ res << '-';
+
+ static const char* monthNames[12] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+ res << monthNames[std::min(std::max(date.getMonth() - 1, 0), 11)];
+
+ res << '-';
+
+ if (date.getYear() < 10) res << '0';
+ if (date.getYear() < 100) res << '0';
+ if (date.getYear() < 1000) res << '0';
+ res << date.getYear();
+
+ res << ' ';
+
+ // Time
+ if (date.getHour() < 10) res << '0';
+ res << date.getHour() << ':';
+
+ if (date.getMinute() < 10) res << '0';
+ res << date.getMinute() << ':';
+
+ if (date.getSecond() < 10) res << '0';
+ res << date.getSecond();
+
+ res << ' ';
+
+ // Zone
+ const int zs = (date.getZone() < 0 ? -1 : 1);
+ const int zh = (date.getZone() * zs) / 60;
+ const int zm = (date.getZone() * zs) % 60;
+
+ res << (zs < 0 ? '-' : '+');
+
+ if (zh < 10) res << '0';
+ res << zh;
+
+ if (zm < 10) res << '0';
+ res << zm;
+
+ res << '"';
+
+
+ return (res.str());
+}
+
+
+} // imap
+} // net
+} // vmime