diff --git a/SConstruct b/SConstruct index 6f8b83ac..e4f149f3 100644 --- a/SConstruct +++ b/SConstruct @@ -204,6 +204,7 @@ libvmime_messaging_sources = [ 'net/defaultConnectionInfos.cpp', 'net/defaultConnectionInfos.hpp', 'net/events.cpp', 'net/events.hpp', 'net/folder.cpp', 'net/folder.hpp', + 'net/folderStatus.hpp', 'net/message.cpp', 'net/message.hpp', 'net/securedConnectionInfos.hpp', 'net/service.cpp', 'net/service.hpp', @@ -245,6 +246,7 @@ libvmime_messaging_proto_sources = [ 'net/pop3/POP3Store.cpp', 'net/pop3/POP3Store.hpp', 'net/pop3/POP3SStore.cpp', 'net/pop3/POP3SStore.hpp', 'net/pop3/POP3Folder.cpp', 'net/pop3/POP3Folder.hpp', + 'net/pop3/POP3FolderStatus.cpp', 'net/pop3/POP3FolderStatus.hpp', 'net/pop3/POP3Message.cpp', 'net/pop3/POP3Message.hpp', 'net/pop3/POP3Response.cpp', 'net/pop3/POP3Response.hpp', 'net/pop3/POP3Utils.cpp', 'net/pop3/POP3Utils.hpp' @@ -271,6 +273,7 @@ libvmime_messaging_proto_sources = [ 'net/imap/IMAPStore.cpp', 'net/imap/IMAPStore.hpp', 'net/imap/IMAPSStore.cpp', 'net/imap/IMAPSStore.hpp', 'net/imap/IMAPFolder.cpp', 'net/imap/IMAPFolder.hpp', + 'net/imap/IMAPFolderStatus.cpp', 'net/imap/IMAPFolderStatus.hpp', 'net/imap/IMAPMessage.cpp', 'net/imap/IMAPMessage.hpp', 'net/imap/IMAPTag.cpp', 'net/imap/IMAPTag.hpp', 'net/imap/IMAPUtils.cpp', 'net/imap/IMAPUtils.hpp', @@ -286,6 +289,7 @@ libvmime_messaging_proto_sources = [ 'net/maildir/maildirServiceInfos.cpp', 'net/maildir/maildirServiceInfos.hpp', 'net/maildir/maildirStore.cpp', 'net/maildir/maildirStore.hpp', 'net/maildir/maildirFolder.cpp', 'net/maildir/maildirFolder.hpp', + 'net/maildir/maildirFolderStatus.cpp', 'net/maildir/maildirFolderStatus.hpp', 'net/maildir/maildirMessage.cpp', 'net/maildir/maildirMessage.hpp', 'net/maildir/maildirUtils.cpp', 'net/maildir/maildirUtils.hpp', 'net/maildir/maildirFormat.cpp', 'net/maildir/maildirFormat.hpp', diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp index 935de8cf..c5ff58a7 100644 --- a/src/net/imap/IMAPConnection.cpp +++ b/src/net/imap/IMAPConnection.cpp @@ -35,6 +35,8 @@ #include "vmime/exception.hpp" #include "vmime/platform.hpp" +#include "vmime/utility/stringUtils.hpp" + #include "vmime/net/defaultConnectionInfos.hpp" #if VMIME_HAVE_SASL_SUPPORT @@ -66,7 +68,7 @@ namespace imap { IMAPConnection::IMAPConnection(ref store, ref 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), - m_secured(false), m_firstTag(true), m_capabilitiesFetched(false) + m_secured(false), m_firstTag(true), m_capabilitiesFetched(false), m_noModSeq(false) { } @@ -155,6 +157,12 @@ void IMAPConnection::connect() needAuth = true; } + if (greet->resp_cond_auth()->resp_text()->resp_text_code() && + greet->resp_cond_auth()->resp_text()->resp_text_code()->capability_data()) + { + processCapabilityResponseData(greet->resp_cond_auth()->resp_text()->resp_text_code()->capability_data()); + } + #if VMIME_HAVE_TLS_SUPPORT // Setup secured connection, if requested const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) @@ -266,6 +274,10 @@ void IMAPConnection::authenticate() internalDisconnect(); throw exceptions::authentication_error(m_parser->lastLine()); } + + // Server capabilities may change when logged in + if (!processCapabilityResponseData(resp)) + invalidateCapabilities(); } @@ -397,6 +409,9 @@ void IMAPConnection::authenticateSASL() // Send response send(false, saslContext->encodeB64(resp, respLen), true); + + // Server capabilities may change when logged in + invalidateCapabilities(); } catch (exceptions::sasl_exception& e) { @@ -506,6 +521,23 @@ const std::vector IMAPConnection::getCapabilities() } +bool IMAPConnection::hasCapability(const string& capa) +{ + if (!m_capabilitiesFetched) + fetchCapabilities(); + + const string normCapa = utility::stringUtils::toUpper(capa); + + for (unsigned int i = 0, n = m_capabilities.size() ; i < n ; ++i) + { + if (m_capabilities[i] == normCapa) + return true; + } + + return false; +} + + void IMAPConnection::invalidateCapabilities() { m_capabilities.clear(); @@ -519,35 +551,50 @@ void IMAPConnection::fetchCapabilities() utility::auto_ptr resp(m_parser->readResponse()); - std::vector res; - if (resp->response_done()->response_tagged()-> resp_cond_state()->status() == IMAPParser::resp_cond_state::OK) { - const std::vector & respDataList = - resp->continue_req_or_response_data(); + processCapabilityResponseData(resp); + } +} - for (unsigned int i = 0 ; i < respDataList.size() ; ++i) - { - if (respDataList[i]->response_data() == NULL) - continue; - const IMAPParser::capability_data* capaData = - respDataList[i]->response_data()->capability_data(); +bool IMAPConnection::processCapabilityResponseData(const IMAPParser::response* resp) +{ + const std::vector & respDataList = + resp->continue_req_or_response_data(); - if (capaData == NULL) - continue; + for (unsigned int i = 0 ; i < respDataList.size() ; ++i) + { + if (respDataList[i]->response_data() == NULL) + continue; - std::vector caps = capaData->capabilities(); + const IMAPParser::capability_data* capaData = + respDataList[i]->response_data()->capability_data(); - for (unsigned int j = 0 ; j < caps.size() ; ++j) - { - if (caps[j]->auth_type()) - res.push_back("AUTH=" + caps[j]->auth_type()->name()); - else - res.push_back(caps[j]->atom()->value()); - } - } + if (capaData == NULL) + continue; + + processCapabilityResponseData(capaData); + return true; + } + + return false; +} + + +void IMAPConnection::processCapabilityResponseData(const IMAPParser::capability_data* capaData) +{ + std::vector res; + + std::vector caps = capaData->capabilities(); + + for (unsigned int j = 0 ; j < caps.size() ; ++j) + { + if (caps[j]->auth_type()) + res.push_back("AUTH=" + caps[j]->auth_type()->name()); + else + res.push_back(utility::stringUtils::toUpper(caps[j]->atom()->value())); } m_capabilities = res; @@ -756,6 +803,18 @@ ref IMAPConnection::getSocket() const } +bool IMAPConnection::isMODSEQDisabled() const +{ + return m_noModSeq; +} + + +void IMAPConnection::disableMODSEQ() +{ + m_noModSeq = true; +} + + } // imap } // net } // vmime diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index 1ee8ac3f..3c54bce8 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -34,6 +34,7 @@ #include "vmime/net/imap/IMAPMessage.hpp" #include "vmime/net/imap/IMAPUtils.hpp" #include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPFolderStatus.hpp" #include "vmime/message.hpp" @@ -57,6 +58,8 @@ IMAPFolder::IMAPFolder(const folder::path& path, ref store, const in m_open(false), m_type(type), m_flags(flags), m_messageCount(0), m_uidValidity(0) { store->registerFolder(this); + + m_status = vmime::create (); } @@ -176,6 +179,9 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) oss << IMAPUtils::quoteString(IMAPUtils::pathToString (connection->hierarchySeparator(), getFullPath())); + if (m_connection->hasCapability("CONDSTORE")) + oss << " (CONDSTORE)"; + connection->send(true, oss.str(), true); // Read the response @@ -217,6 +223,11 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) m_uidValidity = static_cast (code->nz_number()->value()); break; + case IMAPParser::resp_text_code::NOMODSEQ: + + connection->disableMODSEQ(); + break; + default: break; @@ -255,6 +266,8 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) } } + processStatusUpdate(resp); + // 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(); @@ -792,7 +805,7 @@ void IMAPFolder::fetchMessages(std::vector >& msg, const int opti } // Send the request - const string command = IMAPUtils::buildFetchRequest(list, options); + const string command = IMAPUtils::buildFetchRequest(m_connection, list, options); m_connection->send(true, command, true); // Get the response @@ -856,19 +869,17 @@ void IMAPFolder::fetchMessages(std::vector >& msg, const int opti if (progress) progress->stop(total); + + processStatusUpdate(resp); } void IMAPFolder::fetchMessage(ref msg, const int options) { - ref store = m_store.acquire(); + std::vector > msgs; + msgs.push_back(msg); - if (!store) - throw exceptions::illegal_state("Store disconnected"); - else if (!isOpen()) - throw exceptions::illegal_state("Folder not open"); - - msg.dynamicCast ()->fetch(thisRef().dynamicCast (), options); + fetchMessages(msgs, options, /* progress */ NULL); } @@ -973,6 +984,8 @@ void IMAPFolder::deleteMessage(const int num) events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); + + processStatusUpdate(resp); } @@ -1040,6 +1053,8 @@ void IMAPFolder::deleteMessages(const int from, const int to) events::messageChangedEvent::TYPE_FLAGS, nums); notifyMessageChanged(event); + + processStatusUpdate(resp); } @@ -1103,6 +1118,8 @@ void IMAPFolder::deleteMessages(const std::vector & nums) events::messageChangedEvent::TYPE_FLAGS, list); notifyMessageChanged(event); + + processStatusUpdate(resp); } @@ -1311,6 +1328,8 @@ void IMAPFolder::setMessageFlags(const string& set, const int flags, const int m throw exceptions::command_error("STORE", m_connection->getParser()->lastLine(), "bad response"); } + + processStatusUpdate(resp); } } @@ -1455,6 +1474,8 @@ void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int (*it)->notifyMessageCount(event); } } + + processStatusUpdate(resp); } @@ -1545,6 +1566,8 @@ void IMAPFolder::expunge() (*it)->notifyMessageCount(event); } } + + processStatusUpdate(resp); } @@ -1624,6 +1647,8 @@ void IMAPFolder::rename(const folder::path& newPath) (*it)->notifyFolder(event); } } + + processStatusUpdate(resp); } @@ -1767,16 +1792,30 @@ void IMAPFolder::copyMessages(const string& set, const folder::path& dest) throw exceptions::command_error("COPY", m_connection->getParser()->lastLine(), "bad response"); } + + processStatusUpdate(resp); } void IMAPFolder::status(int& count, int& unseen) { - ref store = m_store.acquire(); - count = 0; unseen = 0; + ref status = getStatus(); + + count = status->getMessageCount(); + unseen = status->getUnseenCount(); +} + + +ref IMAPFolder::getStatus() +{ + ref store = m_store.acquire(); + + if (!store) + throw exceptions::illegal_state("Store disconnected"); + // Build the request text std::ostringstream command; command.imbue(std::locale::classic()); @@ -1784,7 +1823,14 @@ void IMAPFolder::status(int& count, int& unseen) command << "STATUS "; command << IMAPUtils::quoteString(IMAPUtils::pathToString (m_connection->hierarchySeparator(), getFullPath())); - command << " (MESSAGES UNSEEN)"; + command << " ("; + + command << "MESSAGES" << ' ' << "UNSEEN" << ' ' << "UIDNEXT" << ' ' << "UIDVALIDITY"; + + if (m_connection->hasCapability("CONDSTORE")) + command << ' ' << "HIGHESTMODSEQ"; + + command << ")"; // Send the request m_connection->send(true, command.str(), true); @@ -1805,81 +1851,47 @@ void IMAPFolder::status(int& count, int& unseen) for (std::vector ::const_iterator it = respDataList.begin() ; it != respDataList.end() ; ++it) { - if ((*it)->response_data() == NULL) + if ((*it)->response_data() != NULL) { - throw exceptions::command_error("STATUS", - m_connection->getParser()->lastLine(), "invalid response"); - } + const IMAPParser::response_data* responseData = (*it)->response_data(); - const IMAPParser::response_data* responseData = (*it)->response_data(); - - if (responseData->mailbox_data() && - responseData->mailbox_data()->type() == IMAPParser::mailbox_data::STATUS) - { - const IMAPParser::status_att_list* statusAttList = - responseData->mailbox_data()->status_att_list(); - - for (std::vector ::const_iterator - jt = statusAttList->values().begin() ; jt != statusAttList->values().end() ; ++jt) + if (responseData->mailbox_data() && + responseData->mailbox_data()->type() == IMAPParser::mailbox_data::STATUS) { - switch ((*jt)->type()) - { - case IMAPParser::status_att_val::MESSAGES: + ref status = vmime::create (); + status->updateFromResponse(responseData->mailbox_data()); - count = (*jt)->value_as_number()->value(); - break; + m_messageCount = status->getMessageCount(); + m_uidValidity = status->getUIDValidity(); - case IMAPParser::status_att_val::UNSEEN: - - unseen = (*jt)->value_as_number()->value(); - break; - - default: - - break; - } + return status; } } } - // Notify message count changed (new messages) - if (m_messageCount != count) + throw exceptions::command_error("STATUS", + m_connection->getParser()->lastLine(), "invalid response"); +} + + +void IMAPFolder::noop() +{ + ref store = m_store.acquire(); + + if (!store) + throw exceptions::illegal_state("Store disconnected"); + + m_connection->send(true, "NOOP", true); + + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) { - const int oldCount = m_messageCount; - - m_messageCount = count; - - if (count > oldCount) - { - std::vector nums; - nums.reserve(count - oldCount); - - for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) - nums[j] = i; - - events::messageCountEvent event - (thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); - - notifyMessageCount(event); - - // Notify folders with the same path - for (std::list ::iterator it = store->m_folders.begin() ; - it != store->m_folders.end() ; ++it) - { - if ((*it) != this && (*it)->getFullPath() == m_path) - { - (*it)->m_messageCount = count; - - events::messageCountEvent event - ((*it)->thisRef().dynamicCast (), - events::messageCountEvent::TYPE_ADDED, nums); - - (*it)->notifyMessageCount(event); - } - } - } + throw exceptions::command_error("NOOP", m_connection->getParser()->lastLine()); } + + processStatusUpdate(resp); } @@ -1939,6 +1951,39 @@ std::vector IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& } +void IMAPFolder::processStatusUpdate(const IMAPParser::response* resp) +{ + // Process tagged response + if (resp->response_done() && resp->response_done()->response_tagged() && + resp->response_done()->response_tagged() + ->resp_cond_state()->resp_text()->resp_text_code()) + { + const IMAPParser::resp_text_code* code = + resp->response_done()->response_tagged() + ->resp_cond_state()->resp_text()->resp_text_code(); + + m_status->updateFromResponse(code); + } + + // Process untagged responses + for (std::vector ::const_iterator + it = resp->continue_req_or_response_data().begin() ; + it != resp->continue_req_or_response_data().end() ; ++it) + { + if ((*it)->response_data() && (*it)->response_data()->resp_cond_state() && + (*it)->response_data()->resp_cond_state()->resp_text()->resp_text_code()) + { + const IMAPParser::resp_text_code* code = + (*it)->response_data()->resp_cond_state()->resp_text()->resp_text_code(); + + m_status->updateFromResponse(code); + } + } + + m_messageCount = m_status->getMessageCount(); + m_uidValidity = m_status->getUIDValidity(); +} + } // imap } // net } // vmime diff --git a/src/net/imap/IMAPFolderStatus.cpp b/src/net/imap/IMAPFolderStatus.cpp new file mode 100644 index 00000000..009f6ba4 --- /dev/null +++ b/src/net/imap/IMAPFolderStatus.cpp @@ -0,0 +1,158 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/imap/IMAPFolderStatus.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +unsigned int IMAPFolderStatus::getMessageCount() const +{ + return m_count; +} + + +unsigned int IMAPFolderStatus::getUnseenCount() const +{ + return m_unseen; +} + + +unsigned int IMAPFolderStatus::getRecentCount() const +{ + return m_recent; +} + + +vmime_uint32 IMAPFolderStatus::getUIDValidity() const +{ + return m_uidValidity; +} + + +vmime_uint32 IMAPFolderStatus::getUIDNext() const +{ + return m_uidNext; +} + + +vmime_uint64 IMAPFolderStatus::getHighestModSeq() const +{ + return m_highestModSeq; +} + + +void IMAPFolderStatus::updateFromResponse(const IMAPParser::mailbox_data* resp) +{ + const IMAPParser::status_att_list* statusAttList = resp->status_att_list(); + + for (std::vector ::const_iterator + jt = statusAttList->values().begin() ; jt != statusAttList->values().end() ; ++jt) + { + switch ((*jt)->type()) + { + case IMAPParser::status_att_val::MESSAGES: + + m_count = (*jt)->value_as_number()->value(); + break; + + case IMAPParser::status_att_val::UNSEEN: + + m_unseen = (*jt)->value_as_number()->value(); + break; + + case IMAPParser::status_att_val::RECENT: + + m_recent = (*jt)->value_as_number()->value(); + break; + + case IMAPParser::status_att_val::UIDNEXT: + + m_uidNext = (*jt)->value_as_number()->value(); + break; + + case IMAPParser::status_att_val::UIDVALIDITY: + + m_uidValidity = (*jt)->value_as_number()->value(); + break; + + case IMAPParser::status_att_val::HIGHESTMODSEQ: + + m_highestModSeq = (*jt)->value_as_mod_sequence_value()->value(); + break; + } + } +} + + +void IMAPFolderStatus::updateFromResponse(const IMAPParser::resp_text_code* resp) +{ + switch (resp->type()) + { + case IMAPParser::resp_text_code::UIDVALIDITY: + + m_uidValidity = resp->nz_number()->value(); + break; + + case IMAPParser::resp_text_code::UIDNEXT: + + m_uidNext = resp->nz_number()->value(); + break; + + case IMAPParser::resp_text_code::UNSEEN: + + m_unseen = resp->nz_number()->value(); + break; + + case IMAPParser::resp_text_code::HIGHESTMODSEQ: + + m_highestModSeq = resp->mod_sequence_value()->value(); + break; + + case IMAPParser::resp_text_code::NOMODSEQ: + + m_highestModSeq = 0; + break; + + default: + + break; + } +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index 6db00768..7e88ef85 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -98,15 +98,15 @@ private: IMAPMessage::IMAPMessage(ref folder, const int num) : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED), - m_expunged(false), m_structure(NULL) + m_expunged(false), m_modseq(0), m_structure(NULL) { folder->registerMessage(this); } -IMAPMessage::IMAPMessage(ref folder, const int num, const uid& uniqueId) +IMAPMessage::IMAPMessage(ref folder, const int num, const uid& uid) : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED), - m_expunged(false), m_uid(uniqueId), m_structure(NULL) + m_expunged(false), m_uid(uid), m_modseq(0), m_structure(NULL) { folder->registerMessage(this); } @@ -133,9 +133,15 @@ int IMAPMessage::getNumber() const } -const message::uid IMAPMessage::getUniqueId() const +const message::uid IMAPMessage::getUID() const { - return (m_uid); + return m_uid; +} + + +vmime_uint64 IMAPMessage::getModSequence() const +{ + return m_modseq; } @@ -358,59 +364,6 @@ void IMAPMessage::extractImpl(ref p, utility::outputStream& os, } -void IMAPMessage::fetch(ref msgFolder, const int options) -{ - ref folder = m_folder.acquire(); - - if (folder != msgFolder) - throw exceptions::folder_not_found(); - - // Send the request - std::vector list; - list.push_back(m_num); - - const string command = IMAPUtils::buildFetchRequest(list, options); - - folder->m_connection->send(true, command, true); - - // Get the response - utility::auto_ptr resp(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", - folder->m_connection->getParser()->lastLine(), "bad response"); - } - - const std::vector & respDataList = - resp->continue_req_or_response_data(); - - for (std::vector ::const_iterator - it = respDataList.begin() ; it != respDataList.end() ; ++it) - { - if ((*it)->response_data() == NULL) - { - throw exceptions::command_error("FETCH", - 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 (messageData->number()) != m_num) - continue; - - // Process fetch response for this message - processFetchResponse(options, messageData); - } -} - - void IMAPMessage::processFetchResponse (const int options, const IMAPParser::message_data* msgData) { @@ -436,6 +389,11 @@ void IMAPMessage::processFetchResponse m_uid = IMAPUtils::makeGlobalUID(folder->m_uidValidity, (*it)->unique_id()->value()); break; } + case IMAPParser::msg_att_item::MODSEQ: + { + m_modseq = (*it)->mod_sequence_value()->value(); + break; + } case IMAPParser::msg_att_item::ENVELOPE: { if (!(options & folder::FETCH_FULL_HEADER)) @@ -731,7 +689,11 @@ ref IMAPMessage::getParsedMessage() } catch (exceptions::unfetched_object&) { - fetch(m_folder.acquire(), IMAPFolder::FETCH_STRUCTURE); + std::vector > msgs; + msgs.push_back(thisRef().dynamicCast ()); + + m_folder.acquire()->fetchMessages(msgs, IMAPFolder::FETCH_STRUCTURE, /* progress */ NULL); + structure = getStructure(); } diff --git a/src/net/imap/IMAPStore.cpp b/src/net/imap/IMAPStore.cpp index 1821b221..e8778f90 100644 --- a/src/net/imap/IMAPStore.cpp +++ b/src/net/imap/IMAPStore.cpp @@ -153,6 +153,12 @@ ref IMAPStore::getConnectionInfos() const } +ref IMAPStore::getConnection() +{ + return m_connection; +} + + void IMAPStore::disconnect() { if (!isConnected()) @@ -187,6 +193,13 @@ void IMAPStore::noop() { throw exceptions::command_error("NOOP", m_connection->getParser()->lastLine()); } + + + for (std::list ::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) + { + (*it)->noop(); + } } diff --git a/src/net/imap/IMAPUtils.cpp b/src/net/imap/IMAPUtils.cpp index 4fea517f..2aad06a1 100644 --- a/src/net/imap/IMAPUtils.cpp +++ b/src/net/imap/IMAPUtils.cpp @@ -634,7 +634,7 @@ const string IMAPUtils::dateTime(const vmime::datetime& date) // static const string IMAPUtils::buildFetchRequestImpl - (const string& mode, const string& set, const int options) + (ref cnt, const string& mode, const string& set, const int options) { // Example: // C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) @@ -655,8 +655,14 @@ const string IMAPUtils::buildFetchRequestImpl items.push_back("BODYSTRUCTURE"); if (options & folder::FETCH_UID) + { items.push_back("UID"); + // Also fetch MODSEQ if CONDSTORE is supported + if (cnt->hasCapability("CONDSTORE") && !cnt->isMODSEQDisabled()) + items.push_back("MODSEQ"); + } + if (options & folder::FETCH_FULL_HEADER) items.push_back("RFC822.HEADER"); else @@ -715,16 +721,18 @@ const string IMAPUtils::buildFetchRequestImpl // static -const string IMAPUtils::buildFetchRequest(const std::vector & list, const int options) +const string IMAPUtils::buildFetchRequest + (ref cnt, const std::vector & list, const int options) { - return buildFetchRequestImpl("number", listToSet(list, -1, false), options); + return buildFetchRequestImpl(cnt, "number", listToSet(list, -1, false), options); } // static -const string IMAPUtils::buildFetchRequest(const std::vector & list, const int options) +const string IMAPUtils::buildFetchRequest + (ref cnt, const std::vector & list, const int options) { - return buildFetchRequestImpl("uid", listToSet(list), options); + return buildFetchRequestImpl(cnt, "uid", listToSet(list), options); } @@ -749,7 +757,7 @@ void IMAPUtils::convertAddressList // static -unsigned int IMAPUtils::extractUIDFromGlobalUID(const message::uid& uid) +vmime_uint32 IMAPUtils::extractUIDFromGlobalUID(const message::uid& uid) { message::uid::size_type colonPos = uid.find(':'); @@ -758,7 +766,7 @@ unsigned int IMAPUtils::extractUIDFromGlobalUID(const message::uid& uid) std::istringstream iss(uid); iss.imbue(std::locale::classic()); - unsigned int n = 0; + vmime_uint32 n = 0; iss >> n; return n; @@ -768,7 +776,7 @@ unsigned int IMAPUtils::extractUIDFromGlobalUID(const message::uid& uid) std::istringstream iss(uid.substr(colonPos + 1)); iss.imbue(std::locale::classic()); - unsigned int n = 0; + vmime_uint32 n = 0; iss >> n; return n; @@ -777,7 +785,7 @@ unsigned int IMAPUtils::extractUIDFromGlobalUID(const message::uid& uid) // static -const message::uid IMAPUtils::makeGlobalUID(const unsigned int UIDValidity, const unsigned int messageUID) +const message::uid IMAPUtils::makeGlobalUID(const vmime_uint32 UIDValidity, const vmime_uint32 messageUID) { std::ostringstream oss; oss.imbue(std::locale::classic()); diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp index aa886ff3..3ea1093e 100644 --- a/src/net/maildir/maildirFolder.cpp +++ b/src/net/maildir/maildirFolder.cpp @@ -33,6 +33,7 @@ #include "vmime/net/maildir/maildirMessage.hpp" #include "vmime/net/maildir/maildirUtils.hpp" #include "vmime/net/maildir/maildirFormat.hpp" +#include "vmime/net/maildir/maildirFolderStatus.hpp" #include "vmime/utility/smartPtr.hpp" @@ -1162,6 +1163,18 @@ void maildirFolder::notifyMessagesCopied(const folder::path& dest) void maildirFolder::status(int& count, int& unseen) +{ + count = 0; + unseen = 0; + + ref status = getStatus(); + + count = status->getMessageCount(); + unseen = status->getUnseenCount(); +} + + +ref maildirFolder::getStatus() { ref store = m_store.acquire(); @@ -1169,16 +1182,18 @@ void maildirFolder::status(int& count, int& unseen) scanFolder(); - count = m_messageCount; - unseen = m_unreadMessageCount; + ref status = vmime::create (); + + status->setMessageCount(m_messageCount); + status->setUnseenCount(m_unreadMessageCount); // Notify message count changed (new messages) - if (count > oldCount) + if (m_messageCount > oldCount) { std::vector nums; - nums.reserve(count - oldCount); + nums.reserve(m_messageCount - oldCount); - for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) + for (int i = oldCount + 1, j = 0 ; i <= m_messageCount ; ++i, ++j) nums[j] = i; events::messageCountEvent event @@ -1207,6 +1222,8 @@ void maildirFolder::status(int& count, int& unseen) } } } + + return status; } diff --git a/src/net/maildir/maildirFolderStatus.cpp b/src/net/maildir/maildirFolderStatus.cpp new file mode 100644 index 00000000..49425e59 --- /dev/null +++ b/src/net/maildir/maildirFolderStatus.cpp @@ -0,0 +1,67 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/maildir/maildirFolderStatus.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +unsigned int maildirFolderStatus::getMessageCount() const +{ + return m_count; +} + + +unsigned int maildirFolderStatus::getUnseenCount() const +{ + return m_unseen; +} + + +void maildirFolderStatus::setMessageCount(const unsigned int count) +{ + m_count = count; +} + + +void maildirFolderStatus::setUnseenCount(const unsigned int unseen) +{ + m_unseen = unseen; +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR diff --git a/src/net/maildir/maildirMessage.cpp b/src/net/maildir/maildirMessage.cpp index 9b14ad78..b6c12053 100644 --- a/src/net/maildir/maildirMessage.cpp +++ b/src/net/maildir/maildirMessage.cpp @@ -261,7 +261,7 @@ int maildirMessage::getNumber() const } -const message::uid maildirMessage::getUniqueId() const +const message::uid maildirMessage::getUID() const { return (m_uid); } diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp index 7aa850b1..1b7b3df6 100644 --- a/src/net/pop3/POP3Folder.cpp +++ b/src/net/pop3/POP3Folder.cpp @@ -33,6 +33,7 @@ #include "vmime/net/pop3/POP3Message.hpp" #include "vmime/net/pop3/POP3Command.hpp" #include "vmime/net/pop3/POP3Response.hpp" +#include "vmime/net/pop3/POP3FolderStatus.hpp" #include "vmime/net/pop3/POP3Utils.hpp" @@ -751,13 +752,23 @@ void POP3Folder::copyMessages(const folder::path& /* dest */, const std::vector void POP3Folder::status(int& count, int& unseen) +{ + count = 0; + unseen = 0; + + ref status = getStatus(); + + count = status->getMessageCount(); + unseen = status->getUnseenCount(); +} + + +ref POP3Folder::getStatus() { ref store = m_store.acquire(); if (!store) throw exceptions::illegal_state("Store disconnected"); - else if (!isOpen()) - throw exceptions::illegal_state("Folder not open"); POP3Command::STAT()->send(store->getConnection()); @@ -767,10 +778,16 @@ void POP3Folder::status(int& count, int& unseen) if (!response->isSuccess()) throw exceptions::command_error("STAT", response->getFirstLine()); + + unsigned int count = 0; + std::istringstream iss(response->getText()); iss >> count; - unseen = count; + ref status = vmime::create (); + + status->setMessageCount(count); + status->setUnseenCount(count); // Update local message count if (m_messageCount != count) @@ -811,6 +828,8 @@ void POP3Folder::status(int& count, int& unseen) } } } + + return status; } diff --git a/src/net/pop3/POP3FolderStatus.cpp b/src/net/pop3/POP3FolderStatus.cpp new file mode 100644 index 00000000..e8c5face --- /dev/null +++ b/src/net/pop3/POP3FolderStatus.cpp @@ -0,0 +1,67 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/net/pop3/POP3FolderStatus.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +unsigned int POP3FolderStatus::getMessageCount() const +{ + return m_count; +} + + +unsigned int POP3FolderStatus::getUnseenCount() const +{ + return m_unseen; +} + + +void POP3FolderStatus::setMessageCount(const unsigned int count) +{ + m_count = count; +} + + +void POP3FolderStatus::setUnseenCount(const unsigned int unseen) +{ + m_unseen = unseen; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 diff --git a/src/net/pop3/POP3Message.cpp b/src/net/pop3/POP3Message.cpp index d2bb9881..659e3d79 100644 --- a/src/net/pop3/POP3Message.cpp +++ b/src/net/pop3/POP3Message.cpp @@ -72,7 +72,7 @@ int POP3Message::getNumber() const } -const message::uid POP3Message::getUniqueId() const +const message::uid POP3Message::getUID() const { return (m_uid); } diff --git a/vmime/net/folder.hpp b/vmime/net/folder.hpp index b34ff812..f65f3711 100644 --- a/vmime/net/folder.hpp +++ b/vmime/net/folder.hpp @@ -39,6 +39,7 @@ #include "vmime/message.hpp" #include "vmime/net/message.hpp" #include "vmime/net/events.hpp" +#include "vmime/net/folderStatus.hpp" #include "vmime/utility/path.hpp" #include "vmime/utility/stream.hpp" @@ -334,6 +335,8 @@ public: virtual void copyMessages(const folder::path& dest, const std::vector & nums) = 0; /** Request folder status without opening it. + * + * \deprecated Use the new getStatus() method * * @param count will receive the number of messages in the folder * @param unseen will receive the number of unseen messages in the folder @@ -341,6 +344,13 @@ public: */ virtual void status(int& count, int& unseen) = 0; + /** Request folder status without opening it. + * + * @return current folder status + * @throw net_exception if an error occurs + */ + virtual ref getStatus() = 0; + /** Expunge deleted messages. * * @throw net_exception if an error occurs diff --git a/vmime/net/folderStatus.hpp b/vmime/net/folderStatus.hpp new file mode 100644 index 00000000..90beea66 --- /dev/null +++ b/vmime/net/folderStatus.hpp @@ -0,0 +1,68 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_FOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_FOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/base.hpp" + + +namespace vmime { +namespace net { + + +/** Holds the status of a mail store folder. + */ + +class VMIME_EXPORT folderStatus : public object +{ +public: + + /** Returns the total number of messages in the folder. + * + * @return number of messages + */ + virtual unsigned int getMessageCount() const = 0; + + /** Returns the number of unseen messages in the folder. + * + * @return number of unseen messages + */ + virtual unsigned int getUnseenCount() const = 0; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_FOLDERSTATUS_HPP_INCLUDED diff --git a/vmime/net/imap/IMAPConnection.hpp b/vmime/net/imap/IMAPConnection.hpp index 257e60d3..72defc29 100644 --- a/vmime/net/imap/IMAPConnection.hpp +++ b/vmime/net/imap/IMAPConnection.hpp @@ -96,6 +96,7 @@ public: void fetchCapabilities(); void invalidateCapabilities(); const std::vector getCapabilities(); + bool hasCapability(const string& capa); ref getAuthenticator(); @@ -104,6 +105,9 @@ public: ref getSocket() const; + bool isMODSEQDisabled() const; + void disableMODSEQ(); + private: void authenticate(); @@ -115,6 +119,9 @@ private: void startTLS(); #endif // VMIME_HAVE_TLS_SUPPORT + bool processCapabilityResponseData(const IMAPParser::response* resp); + void processCapabilityResponseData(const IMAPParser::capability_data* capaData); + weak_ref m_store; @@ -140,6 +147,8 @@ private: std::vector m_capabilities; bool m_capabilitiesFetched; + bool m_noModSeq; + void internalDisconnect(); diff --git a/vmime/net/imap/IMAPFolder.hpp b/vmime/net/imap/IMAPFolder.hpp index 55c16e9c..48e07603 100644 --- a/vmime/net/imap/IMAPFolder.hpp +++ b/vmime/net/imap/IMAPFolder.hpp @@ -38,6 +38,8 @@ #include "vmime/net/folder.hpp" +#include "vmime/net/imap/IMAPParser.hpp" + namespace vmime { namespace net { @@ -47,6 +49,7 @@ namespace imap { class IMAPStore; class IMAPMessage; class IMAPConnection; +class IMAPFolderStatus; /** IMAP folder implementation. @@ -62,7 +65,7 @@ private: IMAPFolder(const folder::path& path, ref store, const int type = TYPE_UNDEFINED, const int flags = FLAG_UNDEFINED); - IMAPFolder(const IMAPFolder&) : folder() { } + IMAPFolder(const IMAPFolder&); ~IMAPFolder(); @@ -118,6 +121,9 @@ public: void copyMessages(const folder::path& dest, const std::vector & nums); void status(int& count, int& unseen); + ref getStatus(); + + void noop(); void expunge(); @@ -148,6 +154,18 @@ private: void copyMessages(const string& set, const folder::path& dest); + /** Process status updates ("unsolicited responses") contained in the + * specified response. Example: + * + * C: a006 NOOP + * S: * 930 EXISTS <-- this is a status update + * S: a006 OK Success + * + * @param resp parsed IMAP response + */ + void processStatusUpdate(const IMAPParser::response* resp); + + weak_ref m_store; ref m_connection; @@ -161,8 +179,8 @@ private: int m_flags; int m_messageCount; - - unsigned int m_uidValidity; + vmime_uint32 m_uidValidity; + ref m_status; std::vector m_messages; }; diff --git a/vmime/net/imap/IMAPFolderStatus.hpp b/vmime/net/imap/IMAPFolderStatus.hpp new file mode 100644 index 00000000..1cad217c --- /dev/null +++ b/vmime/net/imap/IMAPFolderStatus.hpp @@ -0,0 +1,117 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_IMAP_IMAPFOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPFOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/folderStatus.hpp" + +#include "vmime/net/imap/IMAPParser.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +/** Holds the status of an IMAP folder. + */ + +class VMIME_EXPORT IMAPFolderStatus : public folderStatus +{ +public: + + // Inherited from folderStatus + unsigned int getMessageCount() const; + unsigned int getUnseenCount() const; + + /** Returns the the number of messages with the Recent flag set. + * + * @return number of messages flagged Recent + */ + unsigned int getRecentCount() const; + + /** Returns the UID validity of the folder for the current session. + * If the server is capable of persisting UIDs accross sessions, + * this value should never change for a folder. + * + * @return UID validity of the folder + */ + vmime_uint32 getUIDValidity() const; + + /** Returns the UID value that will be assigned to a new message + * in the folder. If the server does not support the UIDPLUS + * extension, it will return 0. + * + * @return UID of the next message + */ + vmime_uint32 getUIDNext() const; + + /** Returns the highest modification sequence of all messages + * in the folder, or 0 if not available for this folder, or not + * supported by the server. The server must support the CONDSTORE + * extension for this to be available. + * + * @return highest modification sequence + */ + vmime_uint64 getHighestModSeq() const; + + + /** Reads the folder status from the specified IMAP response. + * + * @param resp parsed IMAP response + */ + void updateFromResponse(const IMAPParser::mailbox_data* resp); + + /** Reads the folder status from the specified IMAP response. + * + * @param resp parsed IMAP response + */ + void updateFromResponse(const IMAPParser::resp_text_code* resp); + +private: + + unsigned int m_count; + unsigned int m_unseen; + unsigned int m_recent; + vmime_uint32 m_uidValidity; + vmime_uint32 m_uidNext; + vmime_uint64 m_highestModSeq; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPFOLDERSTATUS_HPP_INCLUDED diff --git a/vmime/net/imap/IMAPMessage.hpp b/vmime/net/imap/IMAPMessage.hpp index 5c36ce13..fb86696f 100644 --- a/vmime/net/imap/IMAPMessage.hpp +++ b/vmime/net/imap/IMAPMessage.hpp @@ -57,7 +57,7 @@ private: friend class vmime::creator; // vmime::create IMAPMessage(ref folder, const int num); - IMAPMessage(ref folder, const int num, const uid& uniqueId); + IMAPMessage(ref folder, const int num, const uid& uid); IMAPMessage(const IMAPMessage&) : message() { } ~IMAPMessage(); @@ -66,7 +66,19 @@ public: int getNumber() const; - const uid getUniqueId() const; + const uid getUID() const; + + /** Returns the modification sequence for this message. + * + * Every time metadata for this message changes, the modification + * sequence is updated, and is greater than the previous one. The + * server must support the CONDSTORE extension for this to be + * available. + * + * @return modification sequence, or zero if not supported by + * the underlying protocol + */ + vmime_uint64 getModSequence() const; int getSize() const; @@ -89,8 +101,6 @@ public: private: - void fetch(ref folder, const int options); - void processFetchResponse(const int options, const IMAPParser::message_data* msgData); /** Recursively fetch part header for all parts in the structure. @@ -132,6 +142,7 @@ private: int m_flags; bool m_expunged; uid m_uid; + vmime_uint64 m_modseq; ref
m_header; ref m_structure; diff --git a/vmime/net/imap/IMAPParser.hpp b/vmime/net/imap/IMAPParser.hpp index 41252a92..af871c27 100644 --- a/vmime/net/imap/IMAPParser.hpp +++ b/vmime/net/imap/IMAPParser.hpp @@ -1288,6 +1288,211 @@ public: }; + // seq-number = nz-number / "*" + // ; message sequence number (COPY, FETCH, STORE + // ; commands) or unique identifier (UID COPY, + // ; UID FETCH, UID STORE commands). + + class seq_number : public component + { + public: + + seq_number() + : m_number(NULL), m_star(false) + { + } + + ~seq_number() + { + delete m_number; + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("seq_number"); + + string::size_type pos = *currentPos; + + if (parser.check >(line, &pos, true)) + { + m_star = true; + m_number = NULL; + } + else + { + m_star = false; + m_number = parser.get (line, &pos); + } + + *currentPos = pos; + } + + private: + + IMAPParser::number* m_number; + bool m_star; + + public: + + const IMAPParser::number* number() const { return m_number; } + bool star() const { return m_star; } + }; + + + // seq-range = seq-number ":" seq-number + // ; two seq-number values and all values between + // ; these two regardless of order. + // ; Example: 2:4 and 4:2 are equivalent and indicate + // ; values 2, 3, and 4. + + class seq_range : public component + { + public: + + seq_range() + : m_first(NULL), m_last(NULL) + { + } + + ~seq_range() + { + delete m_first; + delete m_last; + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("seq_range"); + + string::size_type pos = *currentPos; + + m_first = parser.get (line, &pos); + + parser.check >(line, &pos); + + m_last = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::seq_number* m_first; + IMAPParser::seq_number* m_last; + + public: + + const IMAPParser::seq_number* first() const { return m_first; } + const IMAPParser::seq_number* last() const { return m_last; } + }; + + + // sequence-set = (seq-number / seq-range) *("," sequence-set) + // ; set of seq-number values, regardless of order. + // ; Servers MAY coalesce overlaps and/or execute the + // ; sequence in any order. + // ; Example: a message sequence number set of + // ; 2,4:7,9,12:* for a mailbox with 15 messages is + // ; equivalent to 2,4,5,6,7,9,12,13,14,15 + + class sequence_set : public component + { + public: + + sequence_set() + : m_number(NULL), m_range(NULL), m_nextSet(NULL) + { + } + + ~sequence_set() + { + delete m_number; + delete m_range; + delete m_nextSet; + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("sequence_set"); + + string::size_type pos = *currentPos; + + if ((m_range = parser.get (line, &pos, true)) == NULL) + m_number = parser.get (line, &pos); + + if (parser.check >(line, &pos, true)) + m_nextSet = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::seq_number* m_number; + IMAPParser::seq_range* m_range; + IMAPParser::sequence_set* m_nextSet; + + public: + + const IMAPParser::seq_number* seq_number() const { return m_number; } + const IMAPParser::seq_range* seq_range() const { return m_range; } + const IMAPParser::sequence_set* next_sequence_set() const { return m_nextSet; } + }; + + + // mod-sequence-value = 1*DIGIT + // ;; Positive unsigned 64-bit integer + // ;; (mod-sequence) + // ;; (1 <= n < 18,446,744,073,709,551,615) + + class mod_sequence_value : public component + { + public: + + mod_sequence_value() + : m_value(0) + { + } + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mod_sequence_value"); + + string::size_type pos = *currentPos; + + bool valid = true; + vmime_uint64 val = 0; + + while (valid && pos < line.length()) + { + const char c = line[pos]; + + if (c >= '0' && c <= '9') + { + val = (val * 10) + (c - '0'); + ++pos; + } + else + { + valid = false; + } + } + + m_value = val; + + *currentPos = pos; + } + + private: + + vmime_uint64 m_value; + + public: + + const vmime_uint64 value() const { return m_value; } + }; + + // // flag ::= "\Answered" / "\Flagged" / "\Deleted" / // "\Seen" / "\Draft" / flag_keyword / flag_extension @@ -1665,249 +1870,6 @@ public: }; - // - // resp_text_code ::= "ALERT" / "PARSE" / - // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / - // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / - // "UIDVALIDITY" SPACE nz_number / - // "UNSEEN" SPACE nz_number / - // atom [SPACE 1*] - - class resp_text_code : public component - { - public: - - resp_text_code() - : m_nz_number(NULL), m_atom(NULL), m_flag_list(NULL), m_text(NULL) - { - } - - ~resp_text_code() - { - delete (m_nz_number); - delete (m_atom); - delete (m_flag_list); - delete (m_text); - } - - void go(IMAPParser& parser, string& line, string::size_type* currentPos) - { - DEBUG_ENTER_COMPONENT("resp_text_code"); - - string::size_type pos = *currentPos; - - // "ALERT" - if (parser.checkWithArg (line, &pos, "alert", true)) - { - m_type = ALERT; - } - // "PARSE" - else if (parser.checkWithArg (line, &pos, "parse", true)) - { - m_type = PARSE; - } - // "PERMANENTFLAGS" SPACE flag_list - else if (parser.checkWithArg (line, &pos, "permanentflags", true)) - { - m_type = PERMANENTFLAGS; - - parser.check (line, &pos); - - m_flag_list = parser.get (line, &pos); - } - // "READ-ONLY" - else if (parser.checkWithArg (line, &pos, "read-only", true)) - { - m_type = READ_ONLY; - } - // "READ-WRITE" - else if (parser.checkWithArg (line, &pos, "read-write", true)) - { - m_type = READ_WRITE; - } - // "TRYCREATE" - else if (parser.checkWithArg (line, &pos, "trycreate", true)) - { - m_type = TRYCREATE; - } - // "UIDVALIDITY" SPACE nz_number - else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) - { - m_type = UIDVALIDITY; - - parser.check (line, &pos); - m_nz_number = parser.get (line, &pos); - } - // "UNSEEN" SPACE nz_number - else if (parser.checkWithArg (line, &pos, "unseen", true)) - { - m_type = UNSEEN; - - parser.check (line, &pos); - m_nz_number = parser.get (line, &pos); - } - // atom [SPACE 1*] - else - { - m_type = OTHER; - - m_atom = parser.get (line, &pos); - - if (parser.check (line, &pos, true)) - m_text = parser.get >(line, &pos); - } - - *currentPos = pos; - } - - - enum Type - { - ALERT, - PARSE, - PERMANENTFLAGS, - READ_ONLY, - READ_WRITE, - TRYCREATE, - UIDVALIDITY, - UNSEEN, - OTHER - }; - - private: - - Type m_type; - - IMAPParser::nz_number* m_nz_number; - IMAPParser::atom* m_atom; - IMAPParser::flag_list* m_flag_list; - IMAPParser::text* m_text; - - public: - - Type type() const { return (m_type); } - - const IMAPParser::nz_number* nz_number() const { return (m_nz_number); } - const IMAPParser::atom* atom() const { return (m_atom); } - const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } - const IMAPParser::text* text() const { return (m_text); } - }; - - - // - // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) - // ;; text SHOULD NOT begin with "[" or "=" - - class resp_text : public component - { - public: - - resp_text() - : m_resp_text_code(NULL) - { - } - - ~resp_text() - { - delete (m_resp_text_code); - } - - void go(IMAPParser& parser, string& line, string::size_type* currentPos) - { - DEBUG_ENTER_COMPONENT("resp_text"); - - string::size_type pos = *currentPos; - - if (parser.check >(line, &pos, true)) - { - m_resp_text_code = parser.get (line, &pos); - - parser.check >(line, &pos); - parser.check (line, &pos, true); - } - - text_mime2* text1 = parser.get (line, &pos, true); - - if (text1 != NULL) - { - m_text = text1->value(); - delete (text1); - } - else - { - IMAPParser::text* text2 = - parser.get (line, &pos, true); - - if (text2 != NULL) - { - m_text = text2->value(); - delete (text2); - } - else - { - // Empty response text - } - } - - *currentPos = pos; - } - - private: - - IMAPParser::resp_text_code* m_resp_text_code; - string m_text; - - public: - - const IMAPParser::resp_text_code* resp_text_code() const { return (m_resp_text_code); } - const string& text() const { return (m_text); } - }; - - - // - // continue_req ::= "+" SPACE (resp_text / base64) - // - - class continue_req : public component - { - public: - - continue_req() - : m_resp_text(NULL) - { - } - - ~continue_req() - { - delete (m_resp_text); - } - - void go(IMAPParser& parser, string& line, string::size_type* currentPos) - { - DEBUG_ENTER_COMPONENT("continue_req"); - - string::size_type pos = *currentPos; - - parser.check >(line, &pos); - parser.check (line, &pos); - - m_resp_text = parser.get (line, &pos); - - parser.check (line, &pos); - - *currentPos = pos; - } - - private: - - IMAPParser::resp_text* m_resp_text; - - public: - - const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } - }; - - // // auth_type ::= atom // ;; Defined by [IMAP-AUTH] @@ -1965,6 +1927,13 @@ public: // ("UIDVALIDITY" SP nz-number) / // ("UNSEEN" SP number) // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // status-att-val =/ "HIGHESTMODSEQ" SP mod-sequence-valzer + // ;; extends non-terminal defined in [IMAPABNF]. + // ;; Value 0 denotes that the mailbox doesn't + // ;; support persistent mod-sequences + // class status_att_val : public component { @@ -1976,30 +1945,41 @@ public: string::size_type pos = *currentPos; - if (parser.checkWithArg (line, &pos, "messages", true)) + // "HIGHESTMODSEQ" SP mod-sequence-valzer + if (parser.checkWithArg (line, &pos, "highestmodseq", true)) { - m_type = MESSAGES; - } - else if (parser.checkWithArg (line, &pos, "recent", true)) - { - m_type = RECENT; - } - else if (parser.checkWithArg (line, &pos, "uidnext", true)) - { - m_type = UIDNEXT; - } - else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) - { - m_type = UIDVALIDITY; + m_type = HIGHESTMODSEQ; + + parser.check (line, &pos); + m_value = parser.get (line, &pos); } else { - parser.checkWithArg (line, &pos, "unseen"); - m_type = UNSEEN; - } + if (parser.checkWithArg (line, &pos, "messages", true)) + { + m_type = MESSAGES; + } + else if (parser.checkWithArg (line, &pos, "recent", true)) + { + m_type = RECENT; + } + else if (parser.checkWithArg (line, &pos, "uidnext", true)) + { + m_type = UIDNEXT; + } + else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + } + else + { + parser.checkWithArg (line, &pos, "unseen"); + m_type = UNSEEN; + } - parser.check (line, &pos); - m_value = parser.get (line, &pos); + parser.check (line, &pos); + m_value = parser.get (line, &pos); + } *currentPos = pos; } @@ -2007,6 +1987,10 @@ public: enum Type { + // Extensions + HIGHESTMODSEQ, + + // Standard IMAP MESSAGES, RECENT, UIDNEXT, @@ -2027,6 +2011,11 @@ public: { return dynamic_cast (m_value); } + + const IMAPParser::mod_sequence_value* value_as_mod_sequence_value() const + { + return dynamic_cast (m_value); + } }; @@ -3882,6 +3871,9 @@ public: // "BODY" section ["<" number ">"] SPACE nstring / // "UID" SPACE uniqueid // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // msg_att_item /= "MODSEQ" SP "(" mod_sequence_value ")" class msg_att_item : public component { @@ -3890,7 +3882,7 @@ public: msg_att_item() : m_date_time(NULL), m_number(NULL), m_envelope(NULL), m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL), - m_section(NULL) + m_section(NULL), m_mod_sequence_value(NULL) { } @@ -3905,6 +3897,7 @@ public: delete (m_body); delete (m_flag_list); delete (m_section); + delete m_mod_sequence_value; } void go(IMAPParser& parser, string& line, string::size_type* currentPos) @@ -4015,6 +4008,18 @@ public: m_body = parser.get (line, &pos); } } + // "MODSEQ" SP "(" mod_sequence_value ")" + else if (parser.checkWithArg (line, &pos, "modseq", true)) + { + m_type = MODSEQ; + + parser.check (line, &pos); + parser.check >(line, &pos); + + m_mod_sequence_value = parser.get (line, &pos); + + parser.check >(line, &pos); + } // "UID" SPACE uniqueid else { @@ -4042,7 +4047,8 @@ public: BODY, BODY_SECTION, BODY_STRUCTURE, - UID + UID, + MODSEQ }; private: @@ -4057,6 +4063,7 @@ public: IMAPParser::xbody* m_body; IMAPParser::flag_list* m_flag_list; IMAPParser::section* m_section; + IMAPParser::mod_sequence_value* m_mod_sequence_value; public: @@ -4070,6 +4077,7 @@ public: const IMAPParser::xbody* body() const { return (m_body); } const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } const IMAPParser::section* section() const { return (m_section); } + const IMAPParser::mod_sequence_value* mod_sequence_value() { return m_mod_sequence_value; } }; @@ -4188,6 +4196,307 @@ public: }; + // + // resp_text_code ::= "ALERT" / "PARSE" / + // capability-data / + // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + // "UIDVALIDITY" SPACE nz_number / + // "UNSEEN" SPACE nz_number / + // atom [SPACE 1*] + // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value / + // "NOMODSEQ" / + // "MODIFIED" SP set + + class resp_text_code : public component + { + public: + + resp_text_code() + : m_nz_number(NULL), m_atom(NULL), m_flag_list(NULL), + m_text(NULL), m_capability_data(NULL) + { + } + + ~resp_text_code() + { + delete (m_nz_number); + delete (m_atom); + delete (m_flag_list); + delete (m_text); + delete m_capability_data; + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_text_code"); + + string::size_type pos = *currentPos; + + // "ALERT" + if (parser.checkWithArg (line, &pos, "alert", true)) + { + m_type = ALERT; + } + // "PARSE" + else if (parser.checkWithArg (line, &pos, "parse", true)) + { + m_type = PARSE; + } + // capability_data + else if ((m_capability_data = parser.get (line, &pos, true))) + { + m_type = CAPABILITY; + } + // "PERMANENTFLAGS" SPACE flag_list + else if (parser.checkWithArg (line, &pos, "permanentflags", true)) + { + m_type = PERMANENTFLAGS; + + parser.check (line, &pos); + + m_flag_list = parser.get (line, &pos); + } + // "READ-ONLY" + else if (parser.checkWithArg (line, &pos, "read-only", true)) + { + m_type = READ_ONLY; + } + // "READ-WRITE" + else if (parser.checkWithArg (line, &pos, "read-write", true)) + { + m_type = READ_WRITE; + } + // "TRYCREATE" + else if (parser.checkWithArg (line, &pos, "trycreate", true)) + { + m_type = TRYCREATE; + } + // "UIDVALIDITY" SPACE nz_number + else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + + parser.check (line, &pos); + m_nz_number = parser.get (line, &pos); + } + // "UIDNEXT" SPACE nz_number + else if (parser.checkWithArg (line, &pos, "uidnext", true)) + { + m_type = UIDNEXT; + + parser.check (line, &pos); + m_nz_number = parser.get (line, &pos); + } + // "UNSEEN" SPACE nz_number + else if (parser.checkWithArg (line, &pos, "unseen", true)) + { + m_type = UNSEEN; + + parser.check (line, &pos); + m_nz_number = parser.get (line, &pos); + } + // "HIGHESTMODSEQ" SP mod-sequence-value + else if (parser.checkWithArg (line, &pos, "highestmodseq", true)) + { + m_type = HIGHESTMODSEQ; + + parser.check (line, &pos); + m_mod_sequence_value = parser.get (line, &pos); + } + // "NOMODSEQ" + else if (parser.checkWithArg (line, &pos, "nomodseq", true)) + { + m_type = NOMODSEQ; + } + // "MODIFIED" SP sequence-set + else if (parser.checkWithArg (line, &pos, "modified", true)) + { + m_type = MODIFIED; + + parser.check (line, &pos); + + m_sequence_set = parser.get (line, &pos); + } + // atom [SPACE 1*] + else + { + m_type = OTHER; + + m_atom = parser.get (line, &pos); + + if (parser.check (line, &pos, true)) + m_text = parser.get >(line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + // Extensions + HIGHESTMODSEQ, + NOMODSEQ, + MODIFIED, + + // Standard IMAP + ALERT, + PARSE, + CAPABILITY, + PERMANENTFLAGS, + READ_ONLY, + READ_WRITE, + TRYCREATE, + UIDVALIDITY, + UIDNEXT, + UNSEEN, + OTHER + }; + + private: + + Type m_type; + + IMAPParser::nz_number* m_nz_number; + IMAPParser::atom* m_atom; + IMAPParser::flag_list* m_flag_list; + IMAPParser::text* m_text; + IMAPParser::mod_sequence_value* m_mod_sequence_value; + IMAPParser::sequence_set* m_sequence_set; + IMAPParser::capability_data* m_capability_data; + + public: + + Type type() const { return (m_type); } + + const IMAPParser::nz_number* nz_number() const { return (m_nz_number); } + const IMAPParser::atom* atom() const { return (m_atom); } + const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } + const IMAPParser::text* text() const { return (m_text); } + const IMAPParser::mod_sequence_value* mod_sequence_value() const { return m_mod_sequence_value; } + const IMAPParser::sequence_set* sequence_set() const { return m_sequence_set; } + const IMAPParser::capability_data* capability_data() const { return m_capability_data; } + }; + + + // + // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + // ;; text SHOULD NOT begin with "[" or "=" + + class resp_text : public component + { + public: + + resp_text() + : m_resp_text_code(NULL) + { + } + + ~resp_text() + { + delete (m_resp_text_code); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_text"); + + string::size_type pos = *currentPos; + + if (parser.check >(line, &pos, true)) + { + m_resp_text_code = parser.get (line, &pos); + + parser.check >(line, &pos); + parser.check (line, &pos, true); + } + + text_mime2* text1 = parser.get (line, &pos, true); + + if (text1 != NULL) + { + m_text = text1->value(); + delete (text1); + } + else + { + IMAPParser::text* text2 = + parser.get (line, &pos, true); + + if (text2 != NULL) + { + m_text = text2->value(); + delete (text2); + } + else + { + // Empty response text + } + } + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text_code* m_resp_text_code; + string m_text; + + public: + + const IMAPParser::resp_text_code* resp_text_code() const { return (m_resp_text_code); } + const string& text() const { return (m_text); } + }; + + + // + // continue_req ::= "+" SPACE (resp_text / base64) + // + + class continue_req : public component + { + public: + + continue_req() + : m_resp_text(NULL) + { + } + + ~continue_req() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("continue_req"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + parser.check (line, &pos); + + m_resp_text = parser.get (line, &pos); + + parser.check (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text* m_resp_text; + + public: + + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + }; + + // // resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text // ;; Status condition diff --git a/vmime/net/imap/IMAPStore.hpp b/vmime/net/imap/IMAPStore.hpp index 6a06fdc9..e6b27d8d 100644 --- a/vmime/net/imap/IMAPStore.hpp +++ b/vmime/net/imap/IMAPStore.hpp @@ -86,6 +86,7 @@ public: bool isSecuredConnection() const; ref getConnectionInfos() const; + ref getConnection(); protected: diff --git a/vmime/net/imap/IMAPUtils.hpp b/vmime/net/imap/IMAPUtils.hpp index aea02ef9..591db3f3 100644 --- a/vmime/net/imap/IMAPUtils.hpp +++ b/vmime/net/imap/IMAPUtils.hpp @@ -37,6 +37,7 @@ #include "vmime/net/folder.hpp" #include "vmime/net/message.hpp" #include "vmime/net/imap/IMAPParser.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" #include "vmime/mailboxList.hpp" @@ -104,19 +105,23 @@ public: /** Construct a fetch request for the specified messages, designated by their sequence numbers. * + * @param cnt connection * @param list list of message numbers * @param options fetch options * @return fetch request */ - static const string buildFetchRequest(const std::vector & list, const int options); + static const string buildFetchRequest + (ref cnt, const std::vector & list, const int options); /** Construct a fetch request for the specified messages, designated by their UIDs. * + * @param cnt connection * @param list list of message UIDs * @param options fetch options * @return fetch request */ - static const string buildFetchRequest(const std::vector & list, const int options); + static const string buildFetchRequest + (ref cnt, const std::vector & list, const int options); /** Convert a parser-style address list to a mailbox list. * @@ -130,7 +135,7 @@ public: * @param uid globally unique UID (as returned by makeGlobalUID(), for example) * @return message UID */ - static unsigned int extractUIDFromGlobalUID(const message::uid& uid); + static vmime_uint32 extractUIDFromGlobalUID(const message::uid& uid); /** Construct a globally unique UID from UID Validity and a message UID. * @@ -138,12 +143,12 @@ public: * @param messageUID UID of the message * @return global UID */ - static const message::uid makeGlobalUID(const unsigned int UIDValidity, const unsigned int messageUID); + static const message::uid makeGlobalUID(const vmime_uint32 UIDValidity, const vmime_uint32 messageUID); private: static const string buildFetchRequestImpl - (const string& mode, const string& set, const int options); + (ref cnt, const string& mode, const string& set, const int options); }; diff --git a/vmime/net/maildir/maildirFolder.hpp b/vmime/net/maildir/maildirFolder.hpp index 8a418268..940fcaae 100644 --- a/vmime/net/maildir/maildirFolder.hpp +++ b/vmime/net/maildir/maildirFolder.hpp @@ -117,6 +117,7 @@ public: void copyMessages(const folder::path& dest, const std::vector & nums); void status(int& count, int& unseen); + ref getStatus(); void expunge(); diff --git a/vmime/net/maildir/maildirFolderStatus.hpp b/vmime/net/maildir/maildirFolderStatus.hpp new file mode 100644 index 00000000..80018b14 --- /dev/null +++ b/vmime/net/maildir/maildirFolderStatus.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRFOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRFOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/folderStatus.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +/** Holds the status of a Maildir folder. + */ + +class VMIME_EXPORT maildirFolderStatus : public folderStatus +{ +public: + + // Inherited from folderStatus + unsigned int getMessageCount() const; + unsigned int getUnseenCount() const; + + void setMessageCount(const unsigned int count); + void setUnseenCount(const unsigned int unseen); + +private: + + unsigned int m_count; + unsigned int m_unseen; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRFOLDERSTATUS_HPP_INCLUDED diff --git a/vmime/net/maildir/maildirMessage.hpp b/vmime/net/maildir/maildirMessage.hpp index 4efc2eed..de8c1751 100644 --- a/vmime/net/maildir/maildirMessage.hpp +++ b/vmime/net/maildir/maildirMessage.hpp @@ -62,7 +62,7 @@ public: int getNumber() const; - const uid getUniqueId() const; + const uid getUID() const; int getSize() const; diff --git a/vmime/net/message.hpp b/vmime/net/message.hpp index 288e08a2..3b94fbf3 100644 --- a/vmime/net/message.hpp +++ b/vmime/net/message.hpp @@ -204,11 +204,12 @@ public: */ virtual int getNumber() const = 0; - /** Return the unique identified of this message (must fetch before). + /** Return the unique identifier (UID) of this message in its + * folder (must fetch before). * * @return UID of the message */ - virtual const uid getUniqueId() const = 0; + virtual const uid getUID() const = 0; /** Return the size of the message (must fetch before). * diff --git a/vmime/net/pop3/POP3Folder.hpp b/vmime/net/pop3/POP3Folder.hpp index 952b8580..8a97213c 100644 --- a/vmime/net/pop3/POP3Folder.hpp +++ b/vmime/net/pop3/POP3Folder.hpp @@ -114,6 +114,7 @@ public: void copyMessages(const folder::path& dest, const std::vector & nums); void status(int& count, int& unseen); + ref getStatus(); void expunge(); diff --git a/vmime/net/pop3/POP3FolderStatus.hpp b/vmime/net/pop3/POP3FolderStatus.hpp new file mode 100644 index 00000000..3e5d15a1 --- /dev/null +++ b/vmime/net/pop3/POP3FolderStatus.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_POP3_POP3FOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_POP3_POP3FOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/net/folderStatus.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +/** Holds the status of a POP3 folder. + */ + +class VMIME_EXPORT POP3FolderStatus : public folderStatus +{ +public: + + // Inherited from folderStatus + unsigned int getMessageCount() const; + unsigned int getUnseenCount() const; + + void setMessageCount(const unsigned int count); + void setUnseenCount(const unsigned int unseen); + +private: + + unsigned int m_count; + unsigned int m_unseen; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3FOLDERSTATUS_HPP_INCLUDED diff --git a/vmime/net/pop3/POP3Message.hpp b/vmime/net/pop3/POP3Message.hpp index c415e8a7..31b26154 100644 --- a/vmime/net/pop3/POP3Message.hpp +++ b/vmime/net/pop3/POP3Message.hpp @@ -62,7 +62,7 @@ public: int getNumber() const; - const uid getUniqueId() const; + const uid getUID() const; int getSize() const;