1560 lines
38 KiB
C++
1560 lines
38 KiB
C++
//
|
|
// VMime library (http://www.vmime.org)
|
|
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
|
|
//
|
|
// 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/IMAPFolder.hpp"
|
|
|
|
#include "vmime/messaging/IMAPStore.hpp"
|
|
#include "vmime/messaging/IMAPParser.hpp"
|
|
#include "vmime/messaging/IMAPMessage.hpp"
|
|
#include "vmime/messaging/IMAPUtils.hpp"
|
|
#include "vmime/messaging/IMAPConnection.hpp"
|
|
|
|
#include "vmime/message.hpp"
|
|
|
|
#include "vmime/exception.hpp"
|
|
#include "vmime/utility/smartPtr.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
|
|
|
|
namespace vmime {
|
|
namespace messaging {
|
|
|
|
|
|
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)
|
|
{
|
|
delete (m_connection);
|
|
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
|
|
IMAPConnection* connection =
|
|
new 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&)
|
|
{
|
|
delete (connection);
|
|
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");
|
|
|
|
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();
|
|
|
|
delete (oldConnection);
|
|
}
|
|
|
|
|
|
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(this, 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);
|
|
}
|
|
|
|
|
|
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 new IMAPMessage(this, num);
|
|
}
|
|
|
|
|
|
std::vector <message*> IMAPFolder::getMessages(const int from, const int to)
|
|
{
|
|
if (!isOpen())
|
|
throw exceptions::illegal_state("Folder not open");
|
|
|
|
std::vector <message*> v;
|
|
|
|
for (int i = from ; i <= to ; ++i)
|
|
v.push_back(new IMAPMessage(this, i));
|
|
|
|
return (v);
|
|
}
|
|
|
|
|
|
std::vector <message*> IMAPFolder::getMessages(const std::vector <int>& nums)
|
|
{
|
|
if (!isOpen())
|
|
throw exceptions::illegal_state("Folder not open");
|
|
|
|
std::vector <message*> v;
|
|
|
|
for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
|
|
v.push_back(new IMAPMessage(this, *it));
|
|
|
|
return (v);
|
|
}
|
|
|
|
|
|
const int IMAPFolder::getMessageCount()
|
|
{
|
|
if (!isOpen())
|
|
throw exceptions::illegal_state("Folder not open");
|
|
|
|
return (m_messageCount);
|
|
}
|
|
|
|
|
|
folder* IMAPFolder::getFolder(const folder::path::component& name)
|
|
{
|
|
if (!m_store)
|
|
throw exceptions::illegal_state("Store disconnected");
|
|
|
|
return new IMAPFolder(m_path / name, m_store);
|
|
}
|
|
|
|
|
|
std::vector <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 ";
|
|
oss << IMAPUtils::quoteString
|
|
(IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()));
|
|
|
|
if (recursive)
|
|
oss << " *";
|
|
else
|
|
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 <folder*> v;
|
|
|
|
try
|
|
{
|
|
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(new IMAPFolder(path, m_store,
|
|
IMAPUtils::folderTypeFromFlags(mailbox_flag_list),
|
|
IMAPUtils::folderFlagsFromFlags(mailbox_flag_list)));
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception&)
|
|
{
|
|
for (std::vector <folder*>::iterator it = v.begin() ; it != v.end() ; ++it)
|
|
delete (*it);
|
|
|
|
throw;
|
|
}
|
|
|
|
return (v);
|
|
}
|
|
|
|
|
|
void IMAPFolder::fetchMessages(std::vector <message*>& msg, const int options,
|
|
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 <message*>::iterator it = msg.begin() ;
|
|
it != msg.end() ; ++it)
|
|
{
|
|
dynamic_cast <IMAPMessage*>(*it)->fetch(this, options);
|
|
|
|
if (progress)
|
|
progress->progress(++current, total);
|
|
}
|
|
|
|
if (progress)
|
|
progress->stop(total);
|
|
}
|
|
|
|
|
|
void IMAPFolder::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 <IMAPMessage*>(msg)->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);
|
|
}
|
|
|
|
|
|
folder* IMAPFolder::getParent()
|
|
{
|
|
return (m_path.isEmpty() ? NULL : new IMAPFolder(m_path.getParent(), m_store));
|
|
}
|
|
|
|
|
|
const store* IMAPFolder::getStore() const
|
|
{
|
|
return (m_store);
|
|
}
|
|
|
|
|
|
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(this, 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(this, 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(this, 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(this, 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(this, 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(vmime::message* msg, const int flags,
|
|
vmime::datetime* date, 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, 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(this, 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, 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(this, 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, 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(this, 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, 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, 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);
|
|
|
|
events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
|
|
|
|
for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
|
|
it != m_store->m_folders.end() ; ++it)
|
|
{
|
|
if ((*it)->getFullPath() == dest)
|
|
{
|
|
(*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;
|
|
|
|
events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
|
|
|
|
for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
|
|
it != m_store->m_folders.end() ; ++it)
|
|
{
|
|
if ((*it)->getFullPath() == dest)
|
|
{
|
|
(*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();
|
|
|
|
events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums);
|
|
|
|
for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
|
|
it != m_store->m_folders.end() ; ++it)
|
|
{
|
|
if ((*it)->getFullPath() == dest)
|
|
{
|
|
(*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(this, 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, events::messageCountEvent::TYPE_ADDED, nums);
|
|
|
|
(*it)->notifyMessageCount(event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // messaging
|
|
} // vmime
|