diff options
author | x-yl <kylepereira@mail.com> | 2021-06-01 18:39:50 +0400 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2021-06-11 23:58:28 +0430 |
commit | 2f04d24b66f8bd234341f34c3f4047d77a639532 (patch) | |
tree | 299cf4804d2635497ddc1991e1e2a76b8c6eae61 /Userland/Libraries/LibIMAP | |
parent | 0f42ea6770af1e086b07fb8309ca0af6848097c9 (diff) | |
download | serenity-2f04d24b66f8bd234341f34c3f4047d77a639532.zip |
LibIMAP: Support for the LIST and SELECT commands
Diffstat (limited to 'Userland/Libraries/LibIMAP')
-rw-r--r-- | Userland/Libraries/LibIMAP/Client.cpp | 28 | ||||
-rw-r--r-- | Userland/Libraries/LibIMAP/Client.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibIMAP/Objects.h | 141 | ||||
-rw-r--r-- | Userland/Libraries/LibIMAP/Parser.cpp | 159 | ||||
-rw-r--r-- | Userland/Libraries/LibIMAP/Parser.h | 13 |
5 files changed, 343 insertions, 1 deletions
diff --git a/Userland/Libraries/LibIMAP/Client.cpp b/Userland/Libraries/LibIMAP/Client.cpp index 77570c6ac6..07ad200a13 100644 --- a/Userland/Libraries/LibIMAP/Client.cpp +++ b/Userland/Libraries/LibIMAP/Client.cpp @@ -114,6 +114,10 @@ static ReadonlyBytes command_byte_buffer(CommandType command) return "NOOP"sv.bytes(); case CommandType::Capability: return "CAPABILITY"sv.bytes(); + case CommandType::List: + return "LIST"sv.bytes(); + case CommandType::Select: + return "SELECT"sv.bytes(); } VERIFY_NOT_REACHED(); } @@ -143,12 +147,36 @@ RefPtr<Promise<Optional<Response>>> Client::send_command(Command&& command) return promise; } +template<typename T> +RefPtr<Promise<Optional<T>>> cast_promise(RefPtr<Promise<Optional<Response>>> promise_variant) +{ + auto new_promise = promise_variant->map<Optional<T>>( + [](Optional<Response>& variant) { + return variant.has_value() ? move(variant->get<T>()) : Optional<T>(); + }); + return new_promise; +} + +RefPtr<Promise<Optional<SolidResponse>>> Client::list(StringView reference_name, StringView mailbox) +{ + auto command = Command { CommandType::List, m_current_command, + { String::formatted("\"{}\"", reference_name), + String::formatted("\"{}\"", mailbox) } }; + return cast_promise<SolidResponse>(send_command(move(command))); +} + RefPtr<Promise<Optional<Response>>> Client::send_simple_command(CommandType type) { auto command = Command { type, m_current_command, {} }; return send_command(move(command)); } +RefPtr<Promise<Optional<SolidResponse>>> Client::select(StringView string) +{ + auto command = Command { CommandType::Select, m_current_command, { string } }; + return cast_promise<SolidResponse>(send_command(move(command))); +} + void Client::handle_parsed_response(ParseStatus&& parse_status) { if (!m_expecting_response) { diff --git a/Userland/Libraries/LibIMAP/Client.h b/Userland/Libraries/LibIMAP/Client.h index 1481d92eb7..026173bae9 100644 --- a/Userland/Libraries/LibIMAP/Client.h +++ b/Userland/Libraries/LibIMAP/Client.h @@ -21,6 +21,9 @@ public: RefPtr<Promise<Optional<Response>>> send_command(Command&&); RefPtr<Promise<Optional<Response>>> send_simple_command(CommandType); void send_raw(StringView data); + RefPtr<Promise<Optional<SolidResponse>>> list(StringView reference_name, StringView mailbox_name); + RefPtr<Promise<Optional<SolidResponse>>> select(StringView string); + void close(); Function<void(ResponseData&&)> unrequested_response_callback; diff --git a/Userland/Libraries/LibIMAP/Objects.h b/Userland/Libraries/LibIMAP/Objects.h index daaf5fbe55..819a125a1f 100644 --- a/Userland/Libraries/LibIMAP/Objects.h +++ b/Userland/Libraries/LibIMAP/Objects.h @@ -18,11 +18,38 @@ namespace IMAP { enum class CommandType { Capability, + List, Noop, + Select, +}; + +enum class MailboxFlag : unsigned { + All = 1u << 0, + Drafts = 1u << 1, + Flagged = 1u << 2, + HasChildren = 1u << 3, + HasNoChildren = 1u << 4, + Important = 1u << 5, + Junk = 1u << 6, + Marked = 1u << 7, + NoInferiors = 1u << 8, + NoSelect = 1u << 9, + Sent = 1u << 10, + Trash = 1u << 11, + Unmarked = 1u << 12, + Unknown = 1u << 13, }; enum class ResponseType : unsigned { Capability = 1u << 0, + List = 1u << 1, + Exists = 1u << 2, + Recent = 1u << 3, + Flags = 1u << 4, + UIDNext = 1u << 5, + UIDValidity = 1u << 6, + Unseen = 1u << 7, + PermanentFlags = 1u << 8, }; class Parser; @@ -40,6 +67,12 @@ enum class ResponseStatus { OK, }; +struct ListItem { + unsigned flags; + String reference; + String name; +}; + class ResponseData { public: [[nodiscard]] unsigned response_type() const @@ -79,10 +112,116 @@ public: return m_capabilities; } + void add_list_item(ListItem&& item) + { + add_response_type(ResponseType::List); + m_list_items.append(move(item)); + } + + Vector<ListItem>& list_items() + { + VERIFY(contains_response_type(ResponseType::List)); + return m_list_items; + } + + void set_exists(unsigned exists) + { + add_response_type(ResponseType::Exists); + m_exists = exists; + } + + [[nodiscard]] unsigned exists() const + { + VERIFY(contains_response_type(ResponseType::Exists)); + return m_exists; + } + + void set_recent(unsigned recent) + { + add_response_type(ResponseType::Recent); + m_recent = recent; + } + + [[nodiscard]] unsigned recent() const + { + VERIFY(contains_response_type(ResponseType::Recent)); + return m_recent; + } + + void set_uid_next(unsigned uid_next) + { + add_response_type(ResponseType::UIDNext); + m_uid_next = uid_next; + } + + [[nodiscard]] unsigned uid_next() const + { + VERIFY(contains_response_type(ResponseType::UIDNext)); + return m_uid_next; + } + + void set_uid_validity(unsigned uid_validity) + { + add_response_type(ResponseType::UIDValidity); + m_uid_validity = uid_validity; + } + + [[nodiscard]] unsigned uid_validity() const + { + VERIFY(contains_response_type(ResponseType::UIDValidity)); + return m_uid_validity; + } + + void set_unseen(unsigned unseen) + { + add_response_type(ResponseType::Unseen); + m_unseen = unseen; + } + + [[nodiscard]] unsigned unseen() const + { + VERIFY(contains_response_type(ResponseType::Unseen)); + return m_unseen; + } + + void set_flags(Vector<String>&& flags) + { + m_response_type |= static_cast<unsigned>(ResponseType::Flags); + m_flags = move(flags); + } + + Vector<String>& flags() + { + VERIFY(contains_response_type(ResponseType::Flags)); + return m_flags; + } + + void set_permanent_flags(Vector<String>&& flags) + { + add_response_type(ResponseType::PermanentFlags); + m_permanent_flags = move(flags); + } + + Vector<String>& permanent_flags() + { + VERIFY(contains_response_type(ResponseType::PermanentFlags)); + return m_permanent_flags; + } + private: unsigned m_response_type; Vector<String> m_capabilities; + Vector<ListItem> m_list_items; + + unsigned m_recent {}; + unsigned m_exists {}; + + unsigned m_uid_next {}; + unsigned m_uid_validity {}; + unsigned m_unseen {}; + Vector<String> m_permanent_flags; + Vector<String> m_flags; }; class SolidResponse { @@ -154,7 +293,7 @@ public: // Converts a Promise<A> to a Promise<B> using a function func: A -> B template<typename T> - RefPtr<Promise<T>> map(Function<T(Result&)> func) + RefPtr<Promise<T>> map(T func(Result&)) { RefPtr<Promise<T>> new_promise = Promise<T>::construct(); on_resolved = [new_promise, func](Result& result) mutable { diff --git a/Userland/Libraries/LibIMAP/Parser.cpp b/Userland/Libraries/LibIMAP/Parser.cpp index 48e9db7ab5..72514d87e8 100644 --- a/Userland/Libraries/LibIMAP/Parser.cpp +++ b/Userland/Libraries/LibIMAP/Parser.cpp @@ -125,8 +125,60 @@ void Parser::parse_untagged() { consume(" "); + // Certain messages begin with a number like: + // * 15 EXISTS + auto number = try_parse_number(); + if (number.has_value()) { + consume(" "); + auto data_type = parse_atom().to_string(); + if (data_type.matches("EXISTS")) { + m_response.data().set_exists(number.value()); + consume("\r\n"); + } else if (data_type.matches("RECENT")) { + m_response.data().set_recent(number.value()); + consume("\r\n"); + } + return; + } + if (try_consume("CAPABILITY")) { parse_capability_response(); + } else if (try_consume("LIST")) { + auto item = parse_list_item(); + m_response.data().add_list_item(move(item)); + } else if (try_consume("FLAGS")) { + consume(" "); + auto flags = parse_list(+[](StringView x) { return String(x); }); + m_response.data().set_flags(move(flags)); + consume("\r\n"); + } else if (try_consume("OK")) { + consume(" "); + if (try_consume("[")) { + auto actual_type = parse_atom(); + consume(" "); + if (actual_type.matches("UIDNEXT")) { + auto n = parse_number(); + m_response.data().set_uid_next(n); + } else if (actual_type.matches("UIDVALIDITY")) { + auto n = parse_number(); + m_response.data().set_uid_validity(n); + } else if (actual_type.matches("UNSEEN")) { + auto n = parse_number(); + m_response.data().set_unseen(n); + } else if (actual_type.matches("PERMANENTFLAGS")) { + auto flags = parse_list(+[](StringView x) { return String(x); }); + m_response.data().set_permanent_flags(move(flags)); + } else { + dbgln("Unknown: {}", actual_type); + parse_while([](u8 x) { return x != ']'; }); + } + consume("]"); + parse_while([](u8 x) { return x != '\r'; }); + consume("\r\n"); + } else { + parse_while([](u8 x) { return x != '\r'; }); + consume("\r\n"); + } } else { auto x = parse_while([](u8 x) { return x != '\r'; }); consume("\r\n"); @@ -134,6 +186,61 @@ void Parser::parse_untagged() } } +StringView Parser::parse_quoted_string() +{ + auto str = parse_while([](u8 x) { return x != '"'; }); + consume("\""); + return str; +} + +StringView Parser::parse_string() +{ + if (try_consume("\"")) { + return parse_quoted_string(); + } else { + return parse_literal_string(); + } +} + +Optional<StringView> Parser::parse_nstring() +{ + if (try_consume("NIL")) + return {}; + else + return { parse_string() }; +} + +StringView Parser::parse_literal_string() +{ + consume("{"); + auto num_bytes = parse_number(); + consume("}\r\n"); + + if (m_buffer.size() < position + num_bytes) { + m_parsing_failed = true; + return ""; + } + + position += num_bytes; + return StringView(m_buffer.data() + position - num_bytes, num_bytes); +} + +ListItem Parser::parse_list_item() +{ + consume(" "); + auto flags_vec = parse_list(parse_mailbox_flag); + unsigned flags = 0; + for (auto flag : flags_vec) { + flags |= static_cast<unsigned>(flag); + } + consume(" \""); + auto reference = parse_while([](u8 x) { return x != '"'; }); + consume("\" "); + auto mailbox = parse_astring(); + consume("\r\n"); + return ListItem { flags, String(reference), String(mailbox) }; +} + void Parser::parse_capability_response() { auto capability = AK::Vector<String>(); @@ -178,6 +285,58 @@ ResponseStatus Parser::parse_status() return ResponseStatus::Bad; } +template<typename T> +Vector<T> Parser::parse_list(T converter(StringView)) +{ + consume("("); + Vector<T> x; + bool first = true; + while (!try_consume(")")) { + if (!first) + consume(" "); + auto item = parse_while([](u8 x) { + return x != ' ' && x != ')'; + }); + x.append(converter(item)); + first = false; + } + + return x; +} + +MailboxFlag Parser::parse_mailbox_flag(StringView s) +{ + if (s.matches("\\All")) + return MailboxFlag::All; + if (s.matches("\\Drafts")) + return MailboxFlag::Drafts; + if (s.matches("\\Flagged")) + return MailboxFlag::Flagged; + if (s.matches("\\HasChildren")) + return MailboxFlag::HasChildren; + if (s.matches("\\HasNoChildren")) + return MailboxFlag::HasNoChildren; + if (s.matches("\\Important")) + return MailboxFlag::Important; + if (s.matches("\\Junk")) + return MailboxFlag::Junk; + if (s.matches("\\Marked")) + return MailboxFlag::Marked; + if (s.matches("\\Noinferiors")) + return MailboxFlag::NoInferiors; + if (s.matches("\\Noselect")) + return MailboxFlag::NoSelect; + if (s.matches("\\Sent")) + return MailboxFlag::Sent; + if (s.matches("\\Trash")) + return MailboxFlag::Trash; + if (s.matches("\\Unmarked")) + return MailboxFlag::Unmarked; + + dbgln("Unrecognized mailbox flag {}", s); + return MailboxFlag::Unknown; +} + StringView Parser::parse_while(Function<bool(u8)> should_consume) { int chars = 0; diff --git a/Userland/Libraries/LibIMAP/Parser.h b/Userland/Libraries/LibIMAP/Parser.h index e2967ef128..663c0a9286 100644 --- a/Userland/Libraries/LibIMAP/Parser.h +++ b/Userland/Libraries/LibIMAP/Parser.h @@ -43,11 +43,24 @@ private: void parse_untagged(); StringView parse_atom(); + StringView parse_quoted_string(); + StringView parse_string(); + Optional<StringView> parse_nstring(); ResponseStatus parse_status(); + template<typename T> + Vector<T> parse_list(T (*converter)(StringView)); + + static MailboxFlag parse_mailbox_flag(StringView s); + StringView parse_while(Function<bool(u8)> should_consume); void parse_capability_response(); + + ListItem parse_list_item(); + + StringView parse_literal_string(); + StringView parse_astring(); }; } |