diff options
author | Vincent Richard <[email protected]> | 2021-11-25 19:22:50 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-11-25 19:22:50 +0000 |
commit | c6e3b759bcd092865f69ea78431e89e546a68811 (patch) | |
tree | b93a5e1dd12b37cd14fc3ae46b9fb1a878110db5 /src | |
parent | Merge pull request #262 from ibanic/master (diff) | |
parent | Implemented IMAP SEARCH (diff) | |
download | vmime-c6e3b759bcd092865f69ea78431e89e546a68811.tar.gz vmime-c6e3b759bcd092865f69ea78431e89e546a68811.zip |
Merge pull request #268 from jacadcaps/search
Implemented IMAP SEARCH.
Diffstat (limited to 'src')
-rw-r--r-- | src/vmime/dateTime.cpp | 24 | ||||
-rw-r--r-- | src/vmime/dateTime.hpp | 2 | ||||
-rw-r--r-- | src/vmime/net/folder.hpp | 31 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPCommand.cpp | 19 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPCommand.hpp | 1 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPFolder.cpp | 93 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPFolder.hpp | 14 | ||||
-rw-r--r-- | src/vmime/net/maildir/maildirFolder.cpp | 15 | ||||
-rw-r--r-- | src/vmime/net/maildir/maildirFolder.hpp | 10 | ||||
-rw-r--r-- | src/vmime/net/pop3/POP3Folder.cpp | 15 | ||||
-rw-r--r-- | src/vmime/net/pop3/POP3Folder.hpp | 10 | ||||
-rw-r--r-- | src/vmime/net/searchAttributes.cpp | 440 | ||||
-rw-r--r-- | src/vmime/net/searchAttributes.hpp | 139 |
13 files changed, 803 insertions, 10 deletions
diff --git a/src/vmime/dateTime.cpp b/src/vmime/dateTime.cpp index fd443030..1caa2641 100644 --- a/src/vmime/dateTime.cpp +++ b/src/vmime/dateTime.cpp @@ -66,6 +66,14 @@ zone = "UT" / "GMT" ; Universal Time ; hours+min. (HHMM) */ +static const char* dayNames[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static const char* monthNames[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; void datetime::parseImpl( const parsingContext& /* ctx */, @@ -590,14 +598,6 @@ void datetime::generateImpl( size_t* newLinePos ) const { - static const char* dayNames[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const char* monthNames[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - const int z = ((m_zone < 0) ? -m_zone : m_zone); const int zh = z / 60; const int zm = z % 60; @@ -775,6 +775,14 @@ void datetime::getDate(int& year, int& month, int& day) const { } +string datetime::getDate() const { + + std::ostringstream date; + date << m_day << "-" << monthNames[m_month - 1] << "-" << m_year; + return date.str(); +} + + void datetime::setTime( const int hour, const int minute, diff --git a/src/vmime/dateTime.hpp b/src/vmime/dateTime.hpp index 64e8dad4..311e60c1 100644 --- a/src/vmime/dateTime.hpp +++ b/src/vmime/dateTime.hpp @@ -216,6 +216,8 @@ public: void getTime(int& hour, int& minute, int& second) const; void getDate(int& year, int& month, int& day) const; + string getDate() const; + // Set void setYear(const int year); void setMonth(const int month); diff --git a/src/vmime/net/folder.hpp b/src/vmime/net/folder.hpp index 4d5cf6ef..8eaf49c1 100644 --- a/src/vmime/net/folder.hpp +++ b/src/vmime/net/folder.hpp @@ -43,6 +43,7 @@ #include "vmime/net/folderStatus.hpp" #include "vmime/net/fetchAttributes.hpp" #include "vmime/net/folderAttributes.hpp" +#include "vmime/net/searchAttributes.hpp" #include "vmime/utility/path.hpp" #include "vmime/utility/stream.hpp" @@ -406,6 +407,36 @@ public: */ virtual std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid) = 0; + /** Return the sequence numbers of messages matching the searchAttributes. + * + * @param sa the searchAttributes containing search tokens to match messages to + * @param charset optional charset name, the string tokens are assumed to be encoded + * in the provided encoding OR need to be in US-ASCII if no charset is provided + * @throw exceptions::net_exception if an error occurs + * + * Quirks: some servers will not accept any encoding other than US-ASCII, + * other servers will accept UTF-8 but will fail utf-8 + */ + virtual std::vector <size_t> getMessageNumbersMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) = 0; + + /** Return the UIDs of messages matching the searchAttributes. + * + * @param sa the searchAttributes containing search tokens to match messages to + * @param charset optional charset name, the string tokens are assumed to be encoded + * in the provided encoding OR need to be in US-ASCII if no charset is provided + * @throw exceptions::net_exception if an error occurs + * + * Quirks: some servers will not accept any encoding other than US-ASCII, + * other servers will accept UTF-8 but will fail utf-8 + */ + virtual std::vector <message::uid> getMessageUIDsMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) = 0; + // Event listeners void addMessageChangedListener(events::messageChangedListener* l); void removeMessageChangedListener(events::messageChangedListener* l); diff --git a/src/vmime/net/imap/IMAPCommand.cpp b/src/vmime/net/imap/IMAPCommand.cpp index 8911ed02..256924e7 100644 --- a/src/vmime/net/imap/IMAPCommand.cpp +++ b/src/vmime/net/imap/IMAPCommand.cpp @@ -354,6 +354,25 @@ shared_ptr <IMAPCommand> IMAPCommand::SEARCH( return createCommand(cmd.str()); } +shared_ptr <IMAPCommand> IMAPCommand::UIDSEARCH( + const std::vector <string>& keys, + const vmime::charset* charset +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "UID SEARCH"; + + if (charset) { + cmd << " CHARSET " << charset->getName(); + } + + for (size_t i = 0, n = keys.size() ; i < n ; ++i) { + cmd << " " << keys[i]; + } + + return createCommand(cmd.str()); +} // static shared_ptr <IMAPCommand> IMAPCommand::STARTTLS() { diff --git a/src/vmime/net/imap/IMAPCommand.hpp b/src/vmime/net/imap/IMAPCommand.hpp index 4915a577..a04e9ff5 100644 --- a/src/vmime/net/imap/IMAPCommand.hpp +++ b/src/vmime/net/imap/IMAPCommand.hpp @@ -66,6 +66,7 @@ public: static shared_ptr <IMAPCommand> APPEND(const string& mailboxName, const std::vector <string>& flags, vmime::datetime* date, const size_t size); static shared_ptr <IMAPCommand> COPY(const messageSet& msgs, const string& mailboxName); static shared_ptr <IMAPCommand> SEARCH(const std::vector <string>& keys, const vmime::charset* charset); + static shared_ptr <IMAPCommand> UIDSEARCH(const std::vector <string>& keys, const vmime::charset* charset); static shared_ptr <IMAPCommand> STARTTLS(); static shared_ptr <IMAPCommand> CAPABILITY(); static shared_ptr <IMAPCommand> NOOP(); diff --git a/src/vmime/net/imap/IMAPFolder.cpp b/src/vmime/net/imap/IMAPFolder.cpp index 2e07ac3a..bc3e907f 100644 --- a/src/vmime/net/imap/IMAPFolder.cpp +++ b/src/vmime/net/imap/IMAPFolder.cpp @@ -1501,6 +1501,99 @@ std::vector <size_t> IMAPFolder::getMessageNumbersStartingOnUID(const message::u return seqNumbers; } +std::vector <size_t> IMAPFolder::getMessageNumbersMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset +) { + + auto searchKeys = sa.generate(); + + IMAPCommand::SEARCH(searchKeys, charset)->send(m_connection); + + // Get the response + scoped_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("SEARCH", resp->getErrorLog(), "bad response"); + } + + auto& respDataList = resp->continue_req_or_response_data; + + std::vector <size_t> seqNumbers; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("SEARCH", resp->getErrorLog(), "invalid response"); + } + + auto *mailboxData = (*it)->response_data->mailbox_data.get(); + + // We are only interested in responses of type "SEARCH" + if (!mailboxData || + mailboxData->type != IMAPParser::mailbox_data::SEARCH) { + + continue; + } + + for (auto &nzn : mailboxData->search_nz_number_list) { + seqNumbers.push_back(nzn->value); + } + } + + processStatusUpdate(resp.get()); + + return seqNumbers; +} + +std::vector <message::uid> IMAPFolder::getMessageUIDsMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset +) { + + auto searchKeys = sa.generate(); + + IMAPCommand::UIDSEARCH(searchKeys, charset)->send(m_connection); + + // Get the response + scoped_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("UIDSEARCH", resp->getErrorLog(), "bad response"); + } + + auto& respDataList = resp->continue_req_or_response_data; + + std::vector <message::uid> uidNumbers; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("UIDSEARCH", resp->getErrorLog(), "invalid response"); + } + + auto *mailboxData = (*it)->response_data->mailbox_data.get(); + + // We are only interested in responses of type "SEARCH" + if (!mailboxData || + mailboxData->type != IMAPParser::mailbox_data::SEARCH) { + + continue; + } + + for (auto &nzn : mailboxData->search_nz_number_list) { + uidNumbers.push_back(nzn->value); + } + } + + processStatusUpdate(resp.get()); + + return uidNumbers; + } void IMAPFolder::processStatusUpdate(const IMAPParser::response* resp) { diff --git a/src/vmime/net/imap/IMAPFolder.hpp b/src/vmime/net/imap/IMAPFolder.hpp index 3e6b04b5..a3319eac 100644 --- a/src/vmime/net/imap/IMAPFolder.hpp +++ b/src/vmime/net/imap/IMAPFolder.hpp @@ -94,8 +94,18 @@ public: std::vector <shared_ptr <message> > getMessages(const messageSet& msgs); std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid); - - size_t getMessageCount(); + + std::vector <size_t> getMessageNumbersMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) override; + + std::vector <message::uid> getMessageUIDsMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) override; + + size_t getMessageCount(); shared_ptr <folder> getFolder(const folder::path::component& name); std::vector <shared_ptr <folder> > getFolders(const bool recursive = false); diff --git a/src/vmime/net/maildir/maildirFolder.cpp b/src/vmime/net/maildir/maildirFolder.cpp index 8c02025b..ff256c48 100644 --- a/src/vmime/net/maildir/maildirFolder.cpp +++ b/src/vmime/net/maildir/maildirFolder.cpp @@ -1355,6 +1355,21 @@ std::vector <size_t> maildirFolder::getMessageNumbersStartingOnUID(const message throw exceptions::operation_not_supported(); } +std::vector <size_t> maildirFolder::getMessageNumbersMatchingSearchAttributes( + const searchAttributes&, + const vmime::charset* +) { + + throw exceptions::operation_not_supported(); +} + +std::vector <message::uid> maildirFolder::getMessageUIDsMatchingSearchAttributes( + const searchAttributes&, + const vmime::charset* +) { + + throw exceptions::operation_not_supported(); +} } // maildir } // net diff --git a/src/vmime/net/maildir/maildirFolder.hpp b/src/vmime/net/maildir/maildirFolder.hpp index 24f2bf86..3e611908 100644 --- a/src/vmime/net/maildir/maildirFolder.hpp +++ b/src/vmime/net/maildir/maildirFolder.hpp @@ -147,6 +147,16 @@ public: std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid); + std::vector <size_t> getMessageNumbersMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) override; + + std::vector <message::uid> getMessageUIDsMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) override; + private: void scanFolder(); diff --git a/src/vmime/net/pop3/POP3Folder.cpp b/src/vmime/net/pop3/POP3Folder.cpp index b69a483d..ded2e09c 100644 --- a/src/vmime/net/pop3/POP3Folder.cpp +++ b/src/vmime/net/pop3/POP3Folder.cpp @@ -812,6 +812,21 @@ std::vector <size_t> POP3Folder::getMessageNumbersStartingOnUID(const message::u throw exceptions::operation_not_supported(); } +std::vector <size_t> POP3Folder::getMessageNumbersMatchingSearchAttributes( + const searchAttributes&, + const vmime::charset* +) { + + throw exceptions::operation_not_supported(); +} + +std::vector <message::uid> POP3Folder::getMessageUIDsMatchingSearchAttributes( + const searchAttributes&, + const vmime::charset* +) { + + throw exceptions::operation_not_supported(); +} } // pop3 } // net diff --git a/src/vmime/net/pop3/POP3Folder.hpp b/src/vmime/net/pop3/POP3Folder.hpp index 73e29f99..1aa4da87 100644 --- a/src/vmime/net/pop3/POP3Folder.hpp +++ b/src/vmime/net/pop3/POP3Folder.hpp @@ -144,6 +144,16 @@ public: std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid); + std::vector <size_t> getMessageNumbersMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) override; + + std::vector <message::uid> getMessageUIDsMatchingSearchAttributes( + const searchAttributes& sa, + const vmime::charset* charset = nullptr + ) override; + private: void registerMessage(POP3Message* msg); diff --git a/src/vmime/net/searchAttributes.cpp b/src/vmime/net/searchAttributes.cpp new file mode 100644 index 00000000..20639ae4 --- /dev/null +++ b/src/vmime/net/searchAttributes.cpp @@ -0,0 +1,440 @@ +#include "vmime/net/searchAttributes.hpp" + +#if VMIME_HAVE_MESSAGING_FEATURES + +namespace vmime { +namespace net { + +namespace helpers { + +class keylessToken : public searchToken { + +public: + keylessToken(const char* token) + : searchToken(token) { + } + + void generate(std::ostringstream& out) const { + out << m_token; + } +}; + +template < class TYPE > +class typedSearchToken : public searchToken { + +public: + typedSearchToken(const char* token, const TYPE& keyword) + : searchToken(token) + , m_keyword(keyword) { + } + + typedSearchToken(const char* token, const TYPE&& keyword) + : searchToken(token) + , m_keyword(std::move(keyword)) { + } + + +protected: + TYPE m_keyword; +}; + +// Represents a string search token with the search string quoted +class stringToken : public typedSearchToken< string > { + +public: + stringToken(const char* token, const string& keyword) + : typedSearchToken(token, keyword) { + } + + void generate(std::ostringstream& out) const override { + out << m_token << " \"" << m_keyword << "\""; + }; +}; + +class headerToken : public typedSearchToken< const char* > { + +public: + headerToken(const char* token, const char* header) + : typedSearchToken(token, header) { + } + + headerToken(const char* token, const char* header, const string& headerKeyword) + : typedSearchToken(token, header) + , m_headerKeyword(headerKeyword) { + } + + void generate(std::ostringstream& out) const override { + out << m_token << " " << m_keyword << " \"" << m_headerKeyword << "\""; + }; + +protected: + string m_headerKeyword; +}; + +class dateToken : public typedSearchToken< vmime::datetime > { + +public: + dateToken(const char* token, const vmime::datetime& date) + : typedSearchToken(token, date) { + } + + // RFC claims that we need to disregard time information + void generate(std::ostringstream& out) const override { + out << m_token << " " << m_keyword.getDate(); + }; +}; + +class numberToken : public typedSearchToken< int > { + +public: + numberToken(const char* token, uint32_t keyword) + : typedSearchToken(token, keyword) { + } + + void generate(std::ostringstream& out) const override { + out << m_token << " " << m_keyword; + }; +}; + +class flagToken : public typedSearchToken< vmime::net::message::Flags > { + +public: + flagToken(const char* token, vmime::net::message::Flags keyword) + : typedSearchToken(token, keyword) { + } + + void generate(std::ostringstream& out) const override { + out << m_token << " "; + switch (m_keyword) { + case vmime::net::message::Flags::FLAG_SEEN: out << "Seen"; break; + case vmime::net::message::Flags::FLAG_REPLIED: out << "Answered"; break; + case vmime::net::message::Flags::FLAG_MARKED: out << "Flagged"; break; + case vmime::net::message::Flags::FLAG_DRAFT: out << "Draft"; break; + case vmime::net::message::Flags::FLAG_DELETED: out << "Deleted"; break; + default: throw exceptions::operation_not_supported(); + } + }; +}; + +class tokenMessageSetEnumerator : public messageSetEnumerator { + +public: + + tokenMessageSetEnumerator() + : m_first(true) { + + m_oss.imbue(std::locale::classic()); + } + + void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range) { + + if (!m_first) { + m_oss << ","; + } + + if (range.getFirst() == range.getLast()) { + m_oss << range.getFirst(); + } else if (range.getLast() == size_t(-1)) { + m_oss << range.getFirst() << ":*"; + } else { + m_oss << range.getFirst() << ":" << range.getLast(); + } + + m_first = false; + } + + void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& range) { + + if (!m_first) { + m_oss << ","; + } + + if (range.getFirst() == range.getLast()) { + m_oss << range.getFirst(); + } else if (range.getLast() == size_t(-1)) { + m_oss << range.getFirst() << ":*"; + } else { + m_oss << range.getFirst() << ":" << range.getLast(); + } + + m_first = false; + } + + const std::string str() const { + + return m_oss.str(); + } + +private: + + std::ostringstream m_oss; + bool m_first; +}; + + +class messageSetToken : public typedSearchToken< vmime::net::messageSet > { + +public: + messageSetToken(const char *token, const vmime::net::messageSet& keyword) + : typedSearchToken(token, keyword) { + } + + messageSetToken(const char *token, const vmime::net::messageSet&& keyword) + : typedSearchToken(token, std::move(keyword)) { + } + + void generate(std::ostringstream& out) const override { + if (*m_token) { + out << m_token << " ("; + } + else { + out << "("; + } + tokenMessageSetEnumerator enu; + m_keyword.enumerate(enu); + out << enu.str(); + out << ")"; + } +}; + +// Contains a list of tokens which the server will interpret as AND +class tokenVectorToken : public typedSearchToken< std::vector< vmime::shared_ptr< const searchToken > > > { + +public: + tokenVectorToken(const char* token, const std::vector< vmime::shared_ptr< const searchToken > >& tokensAndKeywords) + : typedSearchToken(token, tokensAndKeywords) { + + if (0 == m_keyword.size()) { + throw exceptions::invalid_argument(); + } + } + + tokenVectorToken(const char* token, const std::vector< vmime::shared_ptr< const searchToken > >&& tokensAndKeywords) + : typedSearchToken(token, tokensAndKeywords) { + + if (0 == m_keyword.size()) { + throw exceptions::invalid_argument(); + } + } + + void generate(std::ostringstream& out) const override { + out << m_token; + if (*m_token) + out << " ("; + else + out << "("; + + m_keyword[0]->generate(out); + for (size_t i = 1; i < m_keyword.size(); i++) { + out << " "; + m_keyword[i]->generate(out); + } + + out << ")"; + }; +}; + +// A pair of tokens, used with OR +class tokenPairToken : public typedSearchToken< std::pair< vmime::shared_ptr< const searchToken >, vmime::shared_ptr< const searchToken > > > { + +public: + tokenPairToken(const char* token, const std::pair< vmime::shared_ptr< const searchToken >, vmime::shared_ptr< const searchToken > >& pair) + : typedSearchToken(token, pair) { + } + + void generate(std::ostringstream& out) const override { + out << m_token << " "; + m_keyword.first->generate(out); + out << " "; + m_keyword.second->generate(out); + }; +}; + +} // namespace helpers + +searchTokenPtr searchTokenFactory::AND(const std::vector< searchTokenPtr >&&keywords) { + return vmime::make_shared<helpers::tokenVectorToken>("", std::move(keywords)); +} + +searchTokenPtr searchTokenFactory::ANSWERED() { + return vmime::make_shared<helpers::keylessToken>("ANSWERED"); +} + +searchTokenPtr searchTokenFactory::BCC(const string& keyword) { + return vmime::make_shared<helpers::stringToken>("BCC", keyword); +} + +searchTokenPtr searchTokenFactory::BEFORE(const datetime& keyword) { + return vmime::make_shared<helpers::dateToken>("BEFORE", keyword); +} + +searchTokenPtr searchTokenFactory::BODY(const string& keyword) { + return vmime::make_shared<helpers::stringToken>("BODY", keyword); +} + +searchTokenPtr searchTokenFactory::CC(const string& keyword) { + return vmime::make_shared<helpers::stringToken>("CC", keyword); +} + +searchTokenPtr searchTokenFactory::DELETED() { + return vmime::make_shared<helpers::keylessToken>("DELETED"); +} + +searchTokenPtr searchTokenFactory::DRAFT() { + return vmime::make_shared<helpers::keylessToken>("DRAFT"); +} + +searchTokenPtr searchTokenFactory::FLAGGED() { + return vmime::make_shared<helpers::keylessToken>("FLAGGED"); +} + +searchTokenPtr searchTokenFactory::FROM(const string& keyword) { + return vmime::make_shared<helpers::stringToken>("FROM", keyword); +} + +searchTokenPtr searchTokenFactory::HEADER(const char* fieldName) { + return vmime::make_shared<helpers::headerToken>("HEADER", fieldName); +} + +searchTokenPtr searchTokenFactory::HEADER(const char* fieldName, const string& fieldContents) { + return vmime::make_shared<helpers::headerToken>("HEADER", fieldName, fieldContents); +} + +searchTokenPtr searchTokenFactory::KEYWORD(vmime::net::message::Flags flag) { + return vmime::make_shared<helpers::flagToken>("KEYWORD", flag); +} + +searchTokenPtr searchTokenFactory::LARGER(uint32_t size) { + return vmime::make_shared<helpers::numberToken>("LARGER", size); +} + +searchTokenPtr searchTokenFactory::MESSAGESET(const vmime::net::messageSet& set) { + return vmime::make_shared<helpers::messageSetToken>("", set); +} + +searchTokenPtr searchTokenFactory::MESSAGESET(const vmime::net::messageSet&& set) { + return vmime::make_shared<helpers::messageSetToken>("", std::move(set)); +} + +searchTokenPtr searchTokenFactory::NEW() { + return vmime::make_shared<helpers::keylessToken>("NEW"); +} + +searchTokenPtr searchTokenFactory::NOT(const searchTokenPtr& token) { + return vmime::make_shared<helpers::tokenVectorToken>("NOT", std::vector< vmime::shared_ptr< const searchToken > >({token})); +} + +searchTokenPtr searchTokenFactory::OLD() { + return vmime::make_shared<helpers::keylessToken>("OLD"); +} + +searchTokenPtr searchTokenFactory::ON(const datetime& date) { + return vmime::make_shared<helpers::dateToken>("ON", date); +} + +searchTokenPtr searchTokenFactory::OR(const searchTokenPtr& tokenA, const searchTokenPtr& tokenB) { + return vmime::make_shared<helpers::tokenPairToken>("OR", std::make_pair(tokenA, tokenB)); +} + +searchTokenPtr searchTokenFactory::RECENT() { + return vmime::make_shared<helpers::keylessToken>("RECENT"); +} + +searchTokenPtr searchTokenFactory::SEEN() { + return vmime::make_shared<helpers::keylessToken>("SEEN"); +} + +searchTokenPtr searchTokenFactory::SENTBEFORE(const datetime& date) { + return vmime::make_shared<helpers::dateToken>("SENTBEFORE", date); +} + +searchTokenPtr searchTokenFactory::SENTON(const datetime& date) { + return vmime::make_shared<helpers::dateToken>("SENTON", date); +} + +searchTokenPtr searchTokenFactory::SENTSINCE(const datetime& date) { + return vmime::make_shared<helpers::dateToken>("SENTSINCE", date); +} + +searchTokenPtr searchTokenFactory::SINCE(const datetime& date) { + return vmime::make_shared<helpers::dateToken>("SINCE", date); +} + +searchTokenPtr searchTokenFactory::SMALLER(uint32_t size) { + return vmime::make_shared<helpers::numberToken>("SMALLER", size); +} + +searchTokenPtr searchTokenFactory::SUBJECT(const string& keyword) { + return vmime::make_shared<helpers::stringToken>("SUBJECT", keyword); +} + +searchTokenPtr searchTokenFactory::TEXT(const string& keyword) { + return vmime::make_shared<helpers::stringToken>("TEXT", keyword); +} + +searchTokenPtr searchTokenFactory::TO(const string& keyword) { + return vmime::make_shared<helpers::stringToken>("TO", keyword); +} + +searchTokenPtr searchTokenFactory::UID(const vmime::net::messageSet& set) { + return vmime::make_shared<helpers::messageSetToken>("UID", set); +} + +searchTokenPtr searchTokenFactory::UID(const vmime::net::messageSet&& set) { + return vmime::make_shared<helpers::messageSetToken>("UID", std::move(set)); +} + +searchTokenPtr searchTokenFactory::UNANSWERED() { + return vmime::make_shared<helpers::keylessToken>("UNANSWERED"); +} + +searchTokenPtr searchTokenFactory::UNDELETED() { + return vmime::make_shared<helpers::keylessToken>("UNDELETED"); +} + +searchTokenPtr searchTokenFactory::UNDRAFT() { + return vmime::make_shared<helpers::keylessToken>("UNDRAFT"); +} + +searchTokenPtr searchTokenFactory::UNFLAGGED() { + return vmime::make_shared<helpers::keylessToken>("UNFLAGGED"); +} + +searchTokenPtr searchTokenFactory::UNKEYWORD(vmime::net::message::Flags flag) { + return vmime::make_shared<helpers::flagToken>("UNKEYWORD", flag); +} + +searchTokenPtr searchTokenFactory::UNSEEN() { + return vmime::make_shared<helpers::keylessToken>("UNSEEN"); +} + +searchAttributes::searchAttributes(std::vector< vmime::shared_ptr< const searchToken > >&& tokens) + : m_andTokens(std::move(tokens)) { +} + +void searchAttributes::add(const vmime::shared_ptr< const searchToken >& token) { + m_andTokens.push_back(token); +} + +std::vector< string > searchAttributes::generate() const { + + std::vector< string > keys; + + for (auto& token : m_andTokens) { + std::ostringstream key; + key.imbue(std::locale::classic()); + + token->generate(key); + + keys.push_back(key.str()); + } + + return keys; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/src/vmime/net/searchAttributes.hpp b/src/vmime/net/searchAttributes.hpp new file mode 100644 index 00000000..4bd0b1c7 --- /dev/null +++ b/src/vmime/net/searchAttributes.hpp @@ -0,0 +1,139 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <[email protected]> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 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_SEARCHATTRIBUTES_HPP_INCLUDED +#define VMIME_NET_SEARCHATTRIBUTES_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <vector> + +#include "vmime/types.hpp" +#include "vmime/utility/stringUtils.hpp" +#include "vmime/dateTime.hpp" +#include "vmime/net/message.hpp" +#include "vmime/net/messageSet.hpp" + +namespace vmime { +namespace net { + +class searchToken : public object { + +public: + searchToken(const char* token) + : m_token(token) { + + if (nullptr == m_token) { + throw exceptions::invalid_argument(); + } + } + + virtual void generate(std::ostringstream& out) const = 0; + +protected: + const char* m_token; +}; + +typedef vmime::shared_ptr< const searchToken> searchTokenPtr; + +class searchTokenFactory : public object { + +public: + static searchTokenPtr AND(const std::vector< searchTokenPtr >&); + static searchTokenPtr AND(const std::vector< searchTokenPtr >&&); + static searchTokenPtr ANSWERED(); + static searchTokenPtr BCC(const string&); + static searchTokenPtr BEFORE(const datetime&); + static searchTokenPtr BODY(const string&); + static searchTokenPtr CC(const string&); + static searchTokenPtr DELETED(); + static searchTokenPtr DRAFT(); + static searchTokenPtr FLAGGED(); + static searchTokenPtr FROM(const string&); + static searchTokenPtr HEADER(const char* fieldName); + static searchTokenPtr HEADER(const char* filedName, const string& fieldContents); + static searchTokenPtr KEYWORD(vmime::net::message::Flags); + static searchTokenPtr LARGER(uint32_t); + static searchTokenPtr MESSAGESET(const vmime::net::messageSet&); + static searchTokenPtr MESSAGESET(const vmime::net::messageSet&&); + static searchTokenPtr NEW(); + static searchTokenPtr NOT(const searchTokenPtr&); + static searchTokenPtr OLD(); + static searchTokenPtr ON(const datetime&); + static searchTokenPtr OR(const searchTokenPtr&, const searchTokenPtr&); + static searchTokenPtr RECENT(); + static searchTokenPtr SEEN(); + static searchTokenPtr SENTBEFORE(const datetime&); + static searchTokenPtr SENTON(const datetime&); + static searchTokenPtr SENTSINCE(const datetime&); + static searchTokenPtr SINCE(const datetime&); + static searchTokenPtr SMALLER(uint32_t); + static searchTokenPtr SUBJECT(const string&); + static searchTokenPtr TEXT(const string&); + static searchTokenPtr TO(const string&); + static searchTokenPtr UID(const vmime::net::messageSet&); + static searchTokenPtr UID(const vmime::net::messageSet&&); + static searchTokenPtr UNANSWERED(); + static searchTokenPtr UNDELETED(); + static searchTokenPtr UNDRAFT(); + static searchTokenPtr UNFLAGGED(); + static searchTokenPtr UNKEYWORD(vmime::net::message::Flags); + static searchTokenPtr UNSEEN(); +}; + +/** Holds a set of attributes to match messages against when searching folder contents. + */ +class VMIME_EXPORT searchAttributes : public object { + +public: + searchAttributes() = default; + searchAttributes(std::vector< vmime::shared_ptr< const searchToken > >&&); + + /** Adds a new search token that will be used to match messages against. Multiple tokens are + * treated as an AND operation. + * + * @param token the search token to add + */ + void add(const vmime::shared_ptr< const searchToken >& token); + + std::vector< string > generate() const; + +protected: + + std::vector< vmime::shared_ptr< const searchToken > > m_andTokens; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_NET_SEARCHATTRIBUTES_HPP_INCLUDED |