|
@@ -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;
|