summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibIMAP
diff options
context:
space:
mode:
authorx-yl <kylepereira@mail.com>2021-06-02 18:40:41 +0400
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2021-06-11 23:58:28 +0430
commit076c708d0a970c121d193770fac05f8798d59225 (patch)
tree48e460178db8d138c19bf94a7c85d69ca1b8e4ed /Userland/Libraries/LibIMAP
parenta6339297eccf3f9d0d81f7f12746add081b6cd31 (diff)
downloadserenity-076c708d0a970c121d193770fac05f8798d59225.zip
LibIMAP: Support for STORE and STATUS
Diffstat (limited to 'Userland/Libraries/LibIMAP')
-rw-r--r--Userland/Libraries/LibIMAP/Client.cpp62
-rw-r--r--Userland/Libraries/LibIMAP/Client.h2
-rw-r--r--Userland/Libraries/LibIMAP/Objects.h100
-rw-r--r--Userland/Libraries/LibIMAP/Parser.cpp35
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");