diff options
author | x-yl <kylepereira@mail.com> | 2021-06-02 18:40:41 +0400 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2021-06-11 23:58:28 +0430 |
commit | 076c708d0a970c121d193770fac05f8798d59225 (patch) | |
tree | 48e460178db8d138c19bf94a7c85d69ca1b8e4ed /Userland/Libraries/LibIMAP | |
parent | a6339297eccf3f9d0d81f7f12746add081b6cd31 (diff) | |
download | serenity-076c708d0a970c121d193770fac05f8798d59225.zip |
LibIMAP: Support for STORE and STATUS
Diffstat (limited to 'Userland/Libraries/LibIMAP')
-rw-r--r-- | Userland/Libraries/LibIMAP/Client.cpp | 62 | ||||
-rw-r--r-- | Userland/Libraries/LibIMAP/Client.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibIMAP/Objects.h | 100 | ||||
-rw-r--r-- | Userland/Libraries/LibIMAP/Parser.cpp | 35 |
4 files changed, 199 insertions, 0 deletions
diff --git a/Userland/Libraries/LibIMAP/Client.cpp b/Userland/Libraries/LibIMAP/Client.cpp index 21d623ec35..a0d79412b4 100644 --- a/Userland/Libraries/LibIMAP/Client.cpp +++ b/Userland/Libraries/LibIMAP/Client.cpp @@ -126,12 +126,18 @@ static ReadonlyBytes command_byte_buffer(CommandType command) return "SELECT"sv.bytes(); case CommandType::Fetch: return "FETCH"sv.bytes(); + case CommandType::Store: + return "STORE"sv.bytes(); case CommandType::Search: return "SEARCH"sv.bytes(); case CommandType::UIDFetch: return "UID FETCH"sv.bytes(); + case CommandType::UIDStore: + return "UID STORE"sv.bytes(); case CommandType::UIDSearch: return "UID SEARCH"sv.bytes(); + case CommandType::Status: + return "STATUS"sv.bytes(); } VERIFY_NOT_REACHED(); } @@ -248,6 +254,32 @@ void Client::send_next_command() send_raw(buffer); m_expecting_response = true; } +RefPtr<Promise<Optional<SolidResponse>>> Client::store(StoreMethod method, Sequence sequence_set, bool silent, Vector<String> const& flags, bool uid) +{ + StringBuilder data_item_name; + switch (method) { + case StoreMethod::Replace: + data_item_name.append("FLAGS"); + break; + case StoreMethod::Add: + data_item_name.append("+FLAGS"); + break; + case StoreMethod::Remove: + data_item_name.append("-FLAGS"); + break; + } + if (silent) { + data_item_name.append(".SILENT"); + } + + StringBuilder flags_builder; + flags_builder.append('('); + flags_builder.join(" ", flags); + flags_builder.append(')'); + + auto command = Command { uid ? CommandType::UIDStore : CommandType::Store, m_current_command, { sequence_set.serialize(), data_item_name.build(), flags_builder.build() } }; + return cast_promise<SolidResponse>(send_command(move(command))); +} RefPtr<Promise<Optional<SolidResponse>>> Client::search(Optional<String> charset, Vector<SearchKey>&& keys, bool uid) { Vector<String> args; @@ -276,6 +308,36 @@ RefPtr<Promise<Optional<SolidResponse>>> Client::finish_idle() return cast_promise<SolidResponse>(promise); } +RefPtr<Promise<Optional<SolidResponse>>> Client::status(StringView mailbox, Vector<StatusItemType> const& types) +{ + Vector<String> args; + for (auto type : types) { + switch (type) { + case StatusItemType::Recent: + args.append("RECENT"); + break; + case StatusItemType::UIDNext: + args.append("UIDNEXT"); + break; + case StatusItemType::UIDValidity: + args.append("UIDVALIDITY"); + break; + case StatusItemType::Unseen: + args.append("UNSEEN"); + break; + case StatusItemType::Messages: + args.append("MESSAGES"); + break; + } + } + StringBuilder types_list; + types_list.append('('); + types_list.join(" ", args); + types_list.append(')'); + auto command = Command { CommandType::Status, m_current_command, { mailbox, types_list.build() } }; + return cast_promise<SolidResponse>(send_command(move(command))); +} + void Client::close() { if (m_tls) { diff --git a/Userland/Libraries/LibIMAP/Client.h b/Userland/Libraries/LibIMAP/Client.h index e1e99b6a3a..f6329547d8 100644 --- a/Userland/Libraries/LibIMAP/Client.h +++ b/Userland/Libraries/LibIMAP/Client.h @@ -26,8 +26,10 @@ public: RefPtr<Promise<Optional<SolidResponse>>> select(StringView string); RefPtr<Promise<Optional<SolidResponse>>> search(Optional<String> charset, Vector<SearchKey>&& search_keys, bool uid); RefPtr<Promise<Optional<SolidResponse>>> fetch(FetchCommand request, bool uid); + RefPtr<Promise<Optional<SolidResponse>>> store(StoreMethod, Sequence, bool silent, Vector<String> const& flags, bool uid); RefPtr<Promise<Optional<ContinueRequest>>> idle(); RefPtr<Promise<Optional<SolidResponse>>> finish_idle(); + RefPtr<Promise<Optional<SolidResponse>>> status(StringView mailbox, Vector<StatusItemType> const& types); void close(); diff --git a/Userland/Libraries/LibIMAP/Objects.h b/Userland/Libraries/LibIMAP/Objects.h index 0dd87cb044..76f2da186a 100644 --- a/Userland/Libraries/LibIMAP/Objects.h +++ b/Userland/Libraries/LibIMAP/Objects.h @@ -26,8 +26,11 @@ enum class CommandType { Noop, Search, Select, + Status, + Store, UIDFetch, UIDSearch, + UIDStore, }; enum class MailboxFlag : unsigned { @@ -60,6 +63,7 @@ enum class ResponseType : unsigned { Fetch = 1u << 9, Search = 1u << 10, Bye = 1u << 13, + Status = 1u << 14 }; enum class FetchResponseType : unsigned { @@ -71,8 +75,86 @@ enum class FetchResponseType : unsigned { BodyStructure = 1u << 6, }; +enum class StatusItemType : unsigned { + Recent = 1u << 1, + UIDNext = 1u << 2, + UIDValidity = 1u << 3, + Unseen = 1u << 4, + Messages = 1u << 5, +}; + class Parser; +class StatusItem { +public: + [[nodiscard]] unsigned status_items() const + { + return m_status_items; + } + + [[nodiscard]] bool contains_status_item_type(StatusItemType type) const + { + return (static_cast<unsigned>(type) & m_status_items) != 0; + } + + void add_status_item_type(StatusItemType type) + { + m_status_items |= static_cast<unsigned>(type); + } + + void set_mailbox(String&& mailbox) { m_mailbox = move(mailbox); } + String& mailbox() { return m_mailbox; } + + unsigned get(StatusItemType type) const + { + VERIFY(contains_status_item_type(type)); + switch (type) { + case StatusItemType::Recent: + return m_recent; + case StatusItemType::UIDNext: + return m_uid_next; + case StatusItemType::UIDValidity: + return m_uid_validity; + case StatusItemType::Unseen: + return m_unseen; + case StatusItemType::Messages: + return m_messages; + } + VERIFY_NOT_REACHED(); + } + + void set(StatusItemType type, unsigned value) + { + add_status_item_type(type); + switch (type) { + case StatusItemType::Recent: + m_recent = value; + break; + case StatusItemType::UIDNext: + m_uid_next = value; + break; + case StatusItemType::UIDValidity: + m_uid_validity = value; + break; + case StatusItemType::Unseen: + m_unseen = value; + break; + case StatusItemType::Messages: + m_uid_next = value; + break; + } + } + +private: + unsigned m_status_items { 0 }; + unsigned m_messages { 0 }; + unsigned m_recent { 0 }; + unsigned m_uid_next { 0 }; + unsigned m_uid_validity { 0 }; + unsigned m_unseen { 0 }; + String m_mailbox; +}; + struct Address { Optional<String> name; Optional<String> source_route; @@ -564,6 +646,17 @@ public: return m_bye_message; } + void set_status(StatusItem&& status_item) + { + add_response_type(ResponseType::Status); + m_status_item = move(status_item); + } + + StatusItem& status_item() + { + return m_status_item; + } + private: unsigned m_response_type; @@ -581,6 +674,13 @@ private: Vector<Tuple<unsigned, FetchResponseData>> m_fetch_responses; Vector<unsigned> m_search_results; Optional<String> m_bye_message; + StatusItem m_status_item; +}; + +enum class StoreMethod { + Replace, + Add, + Remove }; class SolidResponse { diff --git a/Userland/Libraries/LibIMAP/Parser.cpp b/Userland/Libraries/LibIMAP/Parser.cpp index 8ed562017a..c00383f711 100644 --- a/Userland/Libraries/LibIMAP/Parser.cpp +++ b/Userland/Libraries/LibIMAP/Parser.cpp @@ -194,6 +194,41 @@ void Parser::parse_untagged() auto message = parse_while([](u8 x) { return x != '\r'; }); consume("\r\n"); m_response.data().set_bye(message.is_empty() ? Optional<String>() : Optional<String>(message)); + } else if (try_consume("STATUS")) { + consume(" "); + auto mailbox = parse_astring(); + consume(" ("); + auto status_item = StatusItem(); + status_item.set_mailbox(mailbox); + while (!try_consume(")")) { + auto status_att = parse_atom(); + consume(" "); + auto value = parse_number(); + + auto type = StatusItemType::Recent; + if (status_att.matches("MESSAGES")) { + type = StatusItemType::Messages; + } else if (status_att.matches("UNSEEN")) { + type = StatusItemType::Unseen; + } else if (status_att.matches("UIDNEXT")) { + type = StatusItemType::UIDNext; + } else if (status_att.matches("UIDVALIDITY")) { + type = StatusItemType::UIDValidity; + } else if (status_att.matches("RECENT")) { + type = StatusItemType::Recent; + } else { + dbgln("Unmatched status attribute: {}", status_att); + m_parsing_failed = true; + } + + status_item.set(type, value); + + if (!at_end() && m_buffer[position] != ')') + consume(" "); + } + m_response.data().set_status(move(status_item)); + try_consume(" "); // Not in the spec but the Outlook server sends a space for some reason. + consume("\r\n"); } else { auto x = parse_while([](u8 x) { return x != '\r'; }); consume("\r\n"); |